Spreadsheet-WriteExcel-2.40/000755 000765 000024 00000000000 12236561261 016024 5ustar00Johnstaff000000 000000 Spreadsheet-WriteExcel-2.40/bin/000755 000765 000024 00000000000 12236561260 016573 5ustar00Johnstaff000000 000000 Spreadsheet-WriteExcel-2.40/Changes000644 000765 000024 00000054677 12236556506 017350 0ustar00Johnstaff000000 000000 Revision history for Perl module Spreadsheet::WriteExcel. 2.40 2013-11-06 ! Fix for minor typo/bug that caused issues with Math::BigInt. RT 90061. 2.39 2013-06-06 ! Added Debian patch for Pod encoding. RT #85897. Thanks Gregor Herrmann. 2.38 2012-22-11 Maintenance release. Minor fixes only. ! Removed failing testcases to allow S::WE to be tested/packaged on recent perls. ! Fix filter_column bug where filter is applied to the wrong column. Merge pull request #4 from blazzy/master. ! Enabled set_zoom() for Chart worksheets. It was documented but wasn't working. Reported by Praphull Kumar. ! Enabled set_tab_color() for Chart worksheets. It was documented but wasn't working. ! Fix typo in docs. Closes RT#70432. ! Fix for rounding in test with longdoubles. Fixes RT#68954. ! Handle chart sheets with utf8 name. Fixes RT#67014. ! Fix for undefined formulas. Fixes RT#61560. 2.37 2010-02-02 + Added set_chartarea() and set_plotarea() methods to set chart and plot background colours and border lines. ! Fixed bug where embedded charts gave "Data may be lost" warning. Reported by Andreas Strobitzer. ! Fixed issue where set_properties() didn't work with ::Big. Marked ::Big as deprecated in the docs in favour of using Spreadsheet::WriteExcel directly. Removed WorkbookBig.pm. 2.36 2010-21-01 ! Fix for bug that gave a name conflict error for repeat rows and autofilters in some non-English versions of Excel. http://rt.cpan.org/Public/Bug/Display.html?id=38099 ! Fixed tab selection for Charts. + Added xl_range_formula() to Spreadsheet::WriteExcel::Utility to help with creating chart ranges. Added example to Chart docs. + Added set_legend() method with initial legend formatting options. + Documented Worksheet methods that are applicable to Charts. 2.35 2010-10-01 + Added support for Stock charts. + Added support for Scatter charts. + Improved Pie chart examples. 2.34 2010-08-01 + Added support for Pie charts. ! Re-added index.html in docs dir for Debian (and others). http://rt.cpan.org/Public/Bug/Display.html?id=53346 ! Fix for incorrectly encoded string result in repeat_formula(). 2.33 2010-03-01 + Added support for embedded charts. See insert_chart(). 2.32 2009-31-12 + Added support for native charts. See Charts.pm. Column, Bar, Line and Area charts supported. ! Fixed Apache2::Const vars in mod_perl2.pl example. Thanks to Ryan Perry. 2.31 2009-10-12 ! Fixed resources in Makefile.PL. ! Updated screenshots of example programs. 2.30 2009-25-11 ! Updated Pod generator to include screenshots of example programs. 2.29 2009-25-11 ! Made version 0.19 of OLE::Storage_Lite a prerequisite to avoid issues when using set_properties with Win 7 or Gnumeric. 2.28 2009-22-11 ! Fix to NAME section in Example.pm to get CPAN to generate pod docs for it. 2.27 2009-21-11 + Added autogenerated Examples.pm. 2.26 2009-13-11 + Beta release of defined names. ! Fix for fuzzy images issue reported by Todd Eigenschink. + Added 25_position_object.t tests to cover above issue. ! Fixed bug where file size wasn't found for progressive JPEGs. Reported by Yamashita Junji and Bruce Anderson II. + Added set_country() method. ! Fix for UTF8 strings passed to write_url(). Reported by Jon Schutz. http://rt.cpan.org/Public/Bug/Display.html?id=41048 ! Fixed very minor issue with uninitialised value for top border colour in formats. Reported by Franz Fasching. 2.25 2008-09-09 ! Fix for set_properties() bug due to 5.10/5.8 UTF-8 differences. ! Fix for failing tests due to localtime() issues. 2.24 2008-06-09 + Added set_properties() method to set document properties. Added test suite and example. 2.23 2008-10-08 + Added Excel data validation with examples and test suite. 2.22 2008-19-07 + Added JPEG support. + Added 29_process_jpg.t test for jpg dimension processing. + Turned off compatibility_mode() in Spreadsheet::WriteExcel::Big which was on by accident. + Added "Warning about Office Service Pack 3" section to the docs to explain Excel warnings with duplicate data. + Expanded "Dates and Time in Excel" section of the docs. + Added isa() to _process_images() to allow subclassing. Reported by David Worenklein. 2.21 2008-09-03 ! Fixed bug where UTF-8 strings in formulas weren't handled. Thanks Sergey Zhuravlev. http://rt.cpan.org/Public/Bug/Display.html?id=30368 ! Fixed collapse outlines compatibility with Gnumeric and OpenOffice.org. Reported by Barrie Slaymaker. http://rt.cpan.org/Public/Bug/Display.html?id=33662 + Added outline_collapsed.pl example. + Added document section about rewriting Excel files. + Minor security fix to Format to avoid eval and additional fix for RT 24218. Thanks to Bram. + Added increased compatibility with third party Excel reading applications such as POI. + Added optional compatibility_mode() to fix problems that can occur with Office SP3. + Fixed compatibility problem with Google Docs. + Added set_start_page() method for setting the start page number when printing. 2.20 2007-06-10 + Added autofilter() and filter_column() method and autofilter.pl example. + Added embed_chart() method to allow extracted chart templates to be embedded in a worksheet. Added demo4.pl and demo5.pl examples. + Added the insert_image() method and proper Excel 97 style image handling for PNG and BMP. Images now work with Gnumeric and OpenOffice. insert_bitmap() is now deprecated. ! Made pane split optional when calling freeze_panes(). Also renamed thaw_panes() as split_panes(). The old method name is still available, but deprecated. ! Renamed write_unicode() and write_unicode_le() methods to the more explicit write_utf16be_string() and write_utf16le_string(). The old method names are still available, but deprecated. 2.19 2007-14-06 + Beta testing only. Not released to CPAN. 2.18 2007-18-01 ! Correct invalid user set_column() calls to prevent crashes after Excel patch KB918419 is applied. Thanks to Sharron McKenzie for debugging. ! Fixed bug when writing comments longer than 8219 bytes. Reported by jscaglione. ! Fixed bug when using copy() with merged formats. Reported by jscaglione. ! Fixed bug where $@ was clobbered during garbage collection. Reported by afoxson. http://rt.cpan.org/Public/Bug/Display.html?id=24218 ! Removed all non-ascii characters from main docs to avoid problems with troff and man. Reported by Ken Williams. http://rt.cpan.org/Public/Bug/Display.html?id=20948 + Added autoloading of OLE::Storage_Lite, when available, to create workbooks larger than 7MB. It is no longer required to explicitly use ::Big. + Added handling of Unicode sheet names in formulas as suggested by Zhur: http://rt.cpan.org/Public/Bug/Display.html?id=20059 2.17 2006-21-05 + Added set_tab_color() method and example (tab_colors.pl). + Add set_page_view() method for Mac Excel view style and better default worksheet dimensions. + Added simulated row_wrap.pl example. + Added simulated Autofit example (autofit.pl). ! Fixed RT #17801, incorrect close value with filehandles. Thanks to Josie. ! Changed comment font to Excel's default of Tahoma 8. Reported by Stefano Maurri. ! Fixed bug that stopped Charts working in 2.16. 2.16 2006-06-01 + Rewrote and reinstated the write_comment() method to take account of the changes in the Excel 97+ comment format. Currently conflicts with insert_bitmap(), see docs. + Added standard styles such as comma, percent and currency. ! Changed write_url() methods to allow numbers or formulas as well as strings as the optional text. Requested by many. + Documented use of encoding flag in add_worksheet() to allow handling of UTF-16 sheet names. + Added encoding flag to merge_range() method to allow handling of UTF-16 strings. Requested by many. Added merge6.pl example program. + Added hide_zero() worksheet method to allow hiding of zero values in cells. + Added print_across() worksheet method to allow changing of the default print direction. + Added right_to_left() worksheet method to allow changing of the default worksheet direction in eastern versions of Excel. + Documented the Worksheet hide() method. + Added comments1.pl and comments2.pl example programs. + Added tests for the internal methods used by write_comment(). 2.15 2005-18-09 ! Changed OLE container stream name from "Book" to "Workbook" for compatibility with some third party apps. Thanks Wieland Pusch and Kevin McMahon. ! Fixed bug that caused all worksheets to be selected in OpenOfficeOrg causing strange linking of data between Worksheets. Thanks M. Brodbelt. See http://rt.cpan.org/NoAuth/Bug.html?id=14094 http://www.openoffice.org/issues/show_bug.cgi?id=52955 ! Changed file creation from end of program to the start of program in order to avoid race conditions and extra file creation on VMS. + Added optional user supplied results for formulas as a workaround for uncalculated values in some non-Excel apps. Added formula_result.pl example of above. 2.14 2005-09-05 ! Fixed handling of Unicode font names. 2.13 2005-20-04 ! Made it a fatal error to use a merged format in a non- merged cell (and vice-versa). This is a temporary measure to prevent people from shooting themselves in the foot until the Format.pm code can be refactored. + Add 01_merge_formats.t test for above. + Added date_time tests. 2.12 2005-22-03 + Added further Unicode support. The following methods now handle uft8 in perl 5.8: add_worksheet() set_header() set_footer() set_font() set_num_format() + Added unicode_cyrillic.pl example. + Added 01_add_worksheet.t test. 2.11 2004-30-09 - Removed undocumented date_time handling from write() to avoid potential false matches. + Added add_write_handler() method to allow users to add their own data handlers to write(). + Added 4 write_handler examples. 2.10 2004-23-09 + Added chart support via external templates. Added Chart.pm and add_chart_ext() method to Workbook object. Added /charts directory with examples and documentation + Added write_date_time() worksheet method for writing dates and times in Excel date format. Added date_time.pl example. + Added automatic Unicode handling via utf8 in perl 5.8 and later. Thanks Mark Fowler. Added several unicode_*.pl examples in different encodings. Thanks to Sean Burke for the sample encodings. + Added write_to_scalar.pl example in order to answer frequently asked question about writing an Excel file to a scalar. 2.04 2004-18-08 ! Fixed handling of Euro symbol in num_format() strings. ! Renamed the Excel 5 style merge() format to the more correct center_across(). Updated examples accordingly. ! Added bug warning about using merge formats outside of merged ranges. + Fixed handling of doubled double quotes in formula strings. Thanks to a tip from merlyn. + The 2.xx versions are now compatible with MS Access. Removed statements to the contrary. 2.03 2004-04-07 ! Fixed bug that prevented column outlines from displaying. Thanks to Marc Rosenthal. ! Enhanced add_worksheet() name checking to exclude case- insensitive duplicate names. Thanks to James Wilkinson. ! Internal fix to number of rows in several (unseen) places. + Added mod_perl 2 example program from Matisse Enzer. Thanks. 2.02 2004-28-05 + Fixed internal version numbers to keep CPAN.pm happy. Thanks to David Dyck. + Minor changes to demo.pl merge5.pl and the html docs. 2.01 2004-27-05 This version is a MAJOR revision to Spreadsheet::WriteExcel. The format of the Excel file that it produces has been updated from Excel 5 to Excel 97. This allows support for long, 32k character strings and Unicode. DUE TO THE MAJOR INTERNAL CHANGES IT IS RECOMMENDED THAT YOU TEST THIS UPGRADE FULLY BEFORE DEPLOYING IT. Also note that this version can be 1.5 to 2 times slower than previous versions and the write_comment() method has been temporarily removed. These issues will be fixed in later releases. + Changed the default Excel format to Excel 97. + Added support for strings up to 32767 character. + Added support for Unicode strings. + Improved speed in relation to pre-release versions. However, it is still slower than the 1.xx versions. + Improved OpenOffice.org support. + Added more examples: diag_border.pl, indent.pl, unicode.pl, unicode_japan.pl, unicode_list.pl + Changed the distro dir structure. - Temporarily removed write_comment() support since the Excel 5 version isn't compatible with Excel 97+. 1.01 2004-24-05 ! Small non-bug fix to write_comment(). 0.43 2004-28-04 ! Fixed longstanding bug where page setup features didn't show up in OpenOffice.org. ! Fixed localised @_ bug when using threaded perls. Thanks Tamas Gulacsi and Stephan Loescher. 0.42 2003-26-08 ! Fixed bug when using IO::Sclalar object as a filehandle. Thanks to Kyle R. Burton and David Robins. ! Fixed minor formula parser bug. Thanks Michael Braig ! Fixed handling of column sizing for widths less than 1 unit (again). Thanks to Rubio (again). ! Failed new() returns undef instead of croak()ing as per docs. 0.41 2003-24-04 + Added facility for adding outlines to worksheets. Added outline.pl example. Thanks to Franco Venturi. + Moved write_comment() code into the core modules. It is now possible to write cell comments. + Added option to return a slice from the sheets() method. + Added link to latest Excel97 pre-release. http://freshmeat.net/projects/writeexcel/#comment-24916 ! Changed, once more, the handling of temp files to avoid several file closing bugs. Thanks to many. ! Fixed handling of column sizing for widths less than 1 unit. Thanks Rubio. ! Renamed addworksheet() and addformat() to add_worksheet() and add_format() for consistency with other method names. Older names are supported but deprecated. 0.40 2002-21-12 ! Fixed internal sheet indexing in formulas on some platforms. This caused multi-sheet workbooks with formulas to crash. Thanks Troy Daniels. ! Fixed non-lexical filehandles to prevent problems in multi-threaded environments. Thanks Ning Xie. 0.39 2002-23-09 + Added the set_codepage() workbook method. This is mainly to prevent a bug that caused Gnumeric to crash but it is also useful for people working with non ascii chars on the Macintosh. Thanks Jody Goldberg. ! Fixed the non-deletion of temporary files. Users of version 0.38 should probably clean up their tmp dirs manually. Thanks Pierre Laplante. ! Changed default handling of set_fg_color and set_bg_color from what Excel expected to what everyone else expected. Setting the fill colour of a cell should now be more intuitive. + Replaced the merge_cells() method with the merge_range() method. This is mainly to prevent the Excel 97 crashing bug that occurs when merge_cells() is used correctly. It also provide a simpler interface to the merge feature. Updated merge example programs. ! Refactored the error raising mechanism for formulas. The source of errors should now be clearer. ! Minor internal fixes to Format.pm. 0.38 2002-29-08 + Added formula caching to speed up the writing of formulas. Thanks to Ron McKelvey. + Added handling of A:A column notation in formulas and applicable worksheet methods. + Changed handling of numbers with leading zeroes so that they can be treated as strings and the zeroes maintained. + Modified $workbook->close() to return the value of the system file close. + Made file size exceeded a die() error instead of a warning. + Added option to hide row with the set_row() method + Added more examples: merge4.pl, merge5.pl, sendmail.pl and repeat.pl ! Fixed write_url() when handling relative links in the current directory. Thanks Bob Mackay Andre de Bruin ! Fixed handling of multiple AND()'s and OR()'s in formulas. ! Fixed bug when creating a very large number of files within a single program on Windows. ! Added warning about Excel97 crashing bug with merge_cells(). ! Fixed handling of parentheses in Worksheet names. Thanks Jann Linder. ! Fixed minor bugs in Format.pm. Thanks Johann Hanne. ! Fixed File::Temp dependency in Makefile. Thanks to many. 0.37 2002-29-04 + Added set_tempdir() method to allow a user defined temp directory. This is mainly of use to IIS users. + Fixed default format behaviour for formats set via the set_row() and set_column() methods. + Extended the hide_gridlines() method to allow hiding of screen gridlines as well as print gridlines. + Added long_string.pl a workaround example for the 255 char limit. + Added https match to url regex. Thanks The Tilghman. 0.36 2002-09-04 + Added user definable colour palette. Thanks Bill Young. + Added headers.pl and colors.pl examples. 0.35 2002-18-03 + Added internal and external hyperlinks. Thanks Ian Penman. + Added worksheet set_zoom() and set_print_scale(). ! Changed default value of fit_to_pages() to allow printed output to be one page wide and as long as required. Thanks Michael Erickson. ! Changed handling of undefs in write() method. Thanks Hanc Pavel. ! Changed new() to return undef for file creation errors Thanks Felipe Prez Galiana. ! Added check for header/footer length. Thanks Peter Dintelmann. ! Added check for duplicate worksheet names. Thanks Keith Miller. - Removed spurious return() to fix bug in set_repeat_rows(). Thanks Brian White. ! Added "use integer" when using shift << to avoid bug with Perl 5.6.0 on HP-UX. Thanks Markus Schmitz, VSarkiss. ! Fixed minor bug in Formula.pm. Thanks Sven Passig. 0.34 2001-23-10 + Added Spreadsheet::WriteExcel::Utility module with helper functions for row/col and dates. + Added write_row and write_col methods after many requests. + Added ability to insert bitmap images. + Added cell protection and formula hiding. + Added more examples. 0.33 2001-30-07 + Added (hopefully) easier mechanism to add format properties. + Added more page setup options: repeat rows and columns, hide gridlines, print area, fit to pages and page breaks. ! Added fix to protect print() from -l on the command line. Thanks Cedric Bouvier and Kyle Krom. + Added more examples. 0.32 2001-17-05 + Added panes option. + Added page setup options: paper type, orientation, headers footers, centering. + Added panes example. 0.31 2001-12-04 ! Fixed several minor and major bugs in Formula.pm. Thanks Shane Ashby and Borgar Olsen and Christian Kirsch. + Added external worksheet references to formulas. + Added A1 notation. + Added facility to new() to accept valid filehandles. Thanks Peter Dintelmann. + Added more examples. Thanks Tom O'Sullivan. ! Updated bigfile.pl example. Thanks to Takanori Kawai. 0.30 2001-26-02 + Added formulas. ! Fixed docs for write_url(). Thanks James Holmes. ! Fixed bug when using a numeric format string of zeros. Thanks Kenneth Stacey. 0.26 2001-01-02 ! Fixed bug when using a border with the default colour. This caused the cell format to become unchangeable from within Excel. Thanks Shenyu Zheng. ! Changed worksheet code to automatically fall back to storing data in memory if opening a tmp file fails. 0.25 2001-19-01 ! Changed distro file format back to Unix line endings. The accidental DOS format was causing install problems. 0.24 2001-14-01 + Added write_url method. + Added set_merge() alias for set_align('merge') method. + Added warnings about deprecated code when running under -w flag. ! Documentation changes. 0.23 2000-10-12 + Added customisable numeric formats. + Added set_row() method. + Added set_1904() method. + Added extra examples including contributions. Thanks to all. + Moved secondary modules to WriteExcel namespace to clean-up Spreadsheet namespace which is now shared with ParseExcel. ! Fixed class data bug. Thanks Cedric Bouvier. ! Fixed Excel crashing!! bug in STYLE. Thanks Rich Sorden. ! Tidied up internal storage of FONTs and XFs. + Renamed set_col_width() method to set_column(). + Renamed set_format() method to set_num_format(). 0.22 2000-22-10 + Added Format.pm and the facility for cell formatting. 0.21 2000-01-10 + Replaced direct access to worksheets array with a workbook method; worksheets(). + Added set_selection() and set_col_width() worksheet methods. ! Replaced the missing thats, including the that that were in the icebox. + Added MS Access bug warning; thanks Harold Bamford. 0.20 2000-27-08 + Added multiple worksheets and OLE wrapper. Now compatible with Star Office, Gnumeric and XESS. 0.11 2000-25-08 ! Changes to documentation. Explanation of how to use WriteExcel in CGI's. Thanks Daniel Gardner. 0.10 2000-13-05 ! Bug in close() and DESTROY sequence fixed. Thanks John Wren. ! Biff version number fixed. Thanks Aurthur@ais, and then unfixed. ! Shakey test and distro fixed. Thanks to many. 0.09 2000-01-02 ! Code for writing DIMENSIONS updated to account for bug when reading files with QuickView. + Renamed xl_write methods to write. 0.08 2000-16-01 First CPAN release. Spreadsheet::WriteExcel - Write text and numbers to minimal Excel binary file. Spreadsheet-WriteExcel-2.40/docs/000755 000765 000024 00000000000 12236561260 016753 5ustar00Johnstaff000000 000000 Spreadsheet-WriteExcel-2.40/examples/000755 000765 000024 00000000000 12236561260 017641 5ustar00Johnstaff000000 000000 Spreadsheet-WriteExcel-2.40/external_charts/000755 000765 000024 00000000000 12236561260 021211 5ustar00Johnstaff000000 000000 Spreadsheet-WriteExcel-2.40/lib/000755 000765 000024 00000000000 12236561260 016571 5ustar00Johnstaff000000 000000 Spreadsheet-WriteExcel-2.40/Makefile.PL000644 000765 000024 00000002533 11317405640 017776 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w use ExtUtils::MakeMaker; use strict; # perl >= 5.005 is required for qr() support in Parse::RecDescent. # There are also some 4 arg substr()'s in WriteExcel. # require 5.005; my %deps = ( 'Parse::RecDescent' => 0, 'File::Temp' => 0, 'OLE::Storage_Lite' => 0.19, ); my %resources = ( repository => 'http://github.com/jmcnamara/spreadsheet-writeexcel', MailingList => 'http://groups.google.com/group/spreadsheet-writeexcel', ); my %params = ( 'AUTHOR' => 'John McNamara (jmcnamara@cpan.org)', 'ABSTRACT' => 'Write to a cross platform Excel binary file', 'NAME' => 'Spreadsheet::WriteExcel', 'VERSION_FROM' => 'lib/Spreadsheet/WriteExcel.pm', 'NEEDS_LINKING' => 0, 'dist' => { COMPRESS => 'gzip --best', SUFFIX => 'gz' }, 'EXE_FILES' => ['bin/chartex'], 'LICENSE' => 'perl', 'PREREQ_PM' => \%deps, ); if ( $ExtUtils::MakeMaker::VERSION ge '6.46' ) { $params{META_MERGE} = { resources => \%resources }; } WriteMakefile(%params); sub MY::postamble { "mydocs:\n" . "\tperl examples/gen_examples_pod.pl examples " . "> lib/Spreadsheet/WriteExcel/Examples.pm\n" . "\tpod2cpanhtml lib/Spreadsheet/WriteExcel.pm " . qq{ | perl -ne 'print unless /\\s+under Perl v[\\d.]+\\s/' } . "> docs/WriteExcel.html"; } Spreadsheet-WriteExcel-2.40/MANIFEST000644 000765 000024 00000011022 12236561261 017151 0ustar00Johnstaff000000 000000 Makefile.PL Changes MANIFEST README META.yml lib/Spreadsheet/WriteExcel.pm lib/Spreadsheet/WriteExcel/BIFFwriter.pm lib/Spreadsheet/WriteExcel/Big.pm lib/Spreadsheet/WriteExcel/Chart.pm lib/Spreadsheet/WriteExcel/Chart/Area.pm lib/Spreadsheet/WriteExcel/Chart/Bar.pm lib/Spreadsheet/WriteExcel/Chart/Column.pm lib/Spreadsheet/WriteExcel/Chart/External.pm lib/Spreadsheet/WriteExcel/Chart/Line.pm lib/Spreadsheet/WriteExcel/Chart/Pie.pm lib/Spreadsheet/WriteExcel/Chart/Scatter.pm lib/Spreadsheet/WriteExcel/Chart/Stock.pm lib/Spreadsheet/WriteExcel/Examples.pm lib/Spreadsheet/WriteExcel/Format.pm lib/Spreadsheet/WriteExcel/Formula.pm lib/Spreadsheet/WriteExcel/OLEwriter.pm lib/Spreadsheet/WriteExcel/Properties.pm lib/Spreadsheet/WriteExcel/Utility.pm lib/Spreadsheet/WriteExcel/Workbook.pm lib/Spreadsheet/WriteExcel/Worksheet.pm t/00_IEEE_double.t t/01_add_worksheet.t t/02_merge_formats.t t/03_cleanup.t t/04_dimensions.t t/05_rows.t t/06_extsst.t t/07_set_propertes.t t/11_date_time.t t/12_date_only.t t/13_date_seconds.t t/21_escher.t t/22_mso_drawing_group.t t/23_note.t t/24_txo.t t/25_position_object.t t/26_autofilter.t t/27_autofilter.t t/28_autofilter.t t/29_process_jpg.t t/30_validation_dval.t t/31_validation_dv_strings.t t/32_validation_dv_formula.t t/33_validation_dv.t t/40_property_types.t t/41_properties.t t/42_set_properties.t t/50_name_stored.t t/51_name_print_area.t t/52_name_print_titles.t t/53_name_autofilter.t t/60_chart_generic.t t/61_chart_subclasses.t t/62_chart_formats.t t/63_chart_area_formats.t docs/Perl_Journal_Article.html docs/images/example.jpg docs/images/swe.png docs/index.html docs/number_formats1.html docs/number_formats2.html docs/palette.html docs/tpj_images/tpj0503-0004-01.gif docs/tpj_images/tpj0503-0004-02.gif docs/tpj_images/tpj0503-0004-03.gif docs/tpj_images/tpj0503-0004-04.gif docs/tpj_images/tpj0503-0004-05.gif examples/README examples/a_simple.pl examples/autofilter.pl examples/autofit.pl examples/bigfile.pl examples/bug_report.pl examples/cgi.pl examples/chart_area.pl examples/chart_bar.pl examples/chart_column.pl examples/chart_line.pl examples/chart_pie.pl examples/chart_scatter.pl examples/chart_stock.pl examples/chess.pl examples/colors.pl examples/comments1.pl examples/comments2.pl examples/convertA1.pl examples/copyformat.pl examples/csv2xls.pl examples/data_validate.pl examples/date_time.pl examples/datecalc1.pl examples/datecalc2.pl examples/defined_name.pl examples/demo.pl examples/diag_border.pl examples/easter_egg.pl examples/filehandle.pl examples/formats.pl examples/formula_result.pl examples/function_locale.pl examples/gen_examples_pod.pl examples/headers.pl examples/hide_sheet.pl examples/hyperlink1.pl examples/hyperlink2.pl examples/images.pl examples/indent.pl examples/lecxe.pl examples/merge1.pl examples/merge2.pl examples/merge3.pl examples/merge4.pl examples/merge5.pl examples/merge6.pl examples/mod_perl1.pl examples/mod_perl2.pl examples/outline.pl examples/outline_collapsed.pl examples/panes.pl examples/properties.pl examples/protection.pl examples/regions.pl examples/repeat.pl examples/republic.png examples/right_to_left.pl examples/row_wrap.pl examples/sales.pl examples/sendmail.pl examples/stats.pl examples/stats_ext.pl examples/stocks.pl examples/tab2xls.pl examples/tab_colors.pl examples/textwrap.pl examples/unicode_2022_jp.pl examples/unicode_2022_jp.txt examples/unicode_8859_11.pl examples/unicode_8859_11.txt examples/unicode_8859_7.pl examples/unicode_8859_7.txt examples/unicode_big5.pl examples/unicode_big5.txt examples/unicode_cp1251.pl examples/unicode_cp1251.txt examples/unicode_cp1256.pl examples/unicode_cp1256.txt examples/unicode_cyrillic.pl examples/unicode_koi8r.pl examples/unicode_koi8r.txt examples/unicode_list.pl examples/unicode_polish_utf8.pl examples/unicode_polish_utf8.txt examples/unicode_shift_jis.pl examples/unicode_shift_jis.txt examples/unicode_utf16.pl examples/unicode_utf16_japan.pl examples/win32ole.pl examples/writeA1.pl examples/write_arrays.pl examples/write_handler1.pl examples/write_handler2.pl examples/write_handler3.pl examples/write_handler4.pl examples/write_to_scalar.pl external_charts/README external_charts/chartex.pl external_charts/external_charts.pod external_charts/external_charts.txt external_charts/demo1.pl external_charts/demo2.pl external_charts/demo3.pl external_charts/demo4.pl external_charts/demo5.pl external_charts/Chart1.xls external_charts/Chart2.xls external_charts/Chart3.xls external_charts/Chart4.xls external_charts/Chart5.xls bin/chartex META.json Module JSON meta-data (added by MakeMaker) Spreadsheet-WriteExcel-2.40/META.json000644 000765 000024 00000002237 12236561261 017451 0ustar00Johnstaff000000 000000 { "abstract" : "Write to a cross platform Excel binary file", "author" : [ "John McNamara (jmcnamara@cpan.org)" ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.112150", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Spreadsheet-WriteExcel", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : 0 } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : 0 } }, "runtime" : { "requires" : { "File::Temp" : 0, "OLE::Storage_Lite" : "0.19", "Parse::RecDescent" : 0 } } }, "release_status" : "stable", "resources" : { "repository" : { "url" : "http://github.com/jmcnamara/spreadsheet-writeexcel" }, "x_MailingList" : "http://groups.google.com/group/spreadsheet-writeexcel" }, "version" : "2.40" } Spreadsheet-WriteExcel-2.40/META.yml000644 000765 000024 00000001307 12236561260 017275 0ustar00Johnstaff000000 000000 --- abstract: 'Write to a cross platform Excel binary file' author: - 'John McNamara (jmcnamara@cpan.org)' build_requires: ExtUtils::MakeMaker: 0 configure_requires: ExtUtils::MakeMaker: 0 dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.112150' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Spreadsheet-WriteExcel no_index: directory: - t - inc requires: File::Temp: 0 OLE::Storage_Lite: 0.19 Parse::RecDescent: 0 resources: repository: http://github.com/jmcnamara/spreadsheet-writeexcel x_MailingList: http://groups.google.com/group/spreadsheet-writeexcel version: 2.40 Spreadsheet-WriteExcel-2.40/README000644 000765 000024 00000004742 11304427047 016711 0ustar00Johnstaff000000 000000 ====================================================================== NAME Spreadsheet::WriteExcel - Write formatted text and numbers to a cross-platform Excel binary file. ====================================================================== DESCRIPTION The Spreadsheet::WriteExcel module can be used to create a cross- platform Excel binary file. Multiple worksheets can be added to a workbook and formatting can be applied to cells. Text, numbers, formulas, hyperlinks and images can be written to the cells. TThe Excel file produced by this module is compatible with 97, 2000, 2002 and 2003. The module will work on the majority of Windows, UNIX and Macintosh platforms. Generated files are also compatible with the spreadsheet applications Gnumeric and OpenOffice.org. This module cannot be used to read an Excel file. See Spreadsheet::ParseExcel or look at the main documentation for some suggestions. This module cannot be uses to write to an existing Excel file. ====================================================================== SYNOPSIS To write a string, a formatted string, a number and a formula to the first worksheet in an Excel workbook called perl.xls: use Spreadsheet::WriteExcel; # Create a new Excel workbook my $workbook = Spreadsheet::WriteExcel->new("perl.xls"); # Add a worksheet $worksheet = $workbook->add_worksheet(); # Add and define a format $format = $workbook->add_format(); # Add a format $format->set_bold(); $format->set_color('red'); $format->set_align('center'); # Write a formatted and unformatted string. $col = $row = 0; $worksheet->write($row, $col, "Hi Excel!", $format); $worksheet->write(1, $col, "Hi Excel!"); # Write a number and a formula using A1 notation $worksheet->write('A3', 1.2345); $worksheet->write('A4', '=SIN(PI()/4)'); ====================================================================== REQUIREMENTS This module requires Perl 5.005 (or later), Parse::RecDescent and File::Temp: http://search.cpan.org/search?dist=Parse-RecDescent/ http://search.cpan.org/search?dist=File-Temp/ ====================================================================== INSTALLATION See the INSTALL file for details. ====================================================================== AUTHOR John McNamara (jmcnamara@cpan.org) Spreadsheet-WriteExcel-2.40/t/000755 000765 000024 00000000000 12236561260 016266 5ustar00Johnstaff000000 000000 Spreadsheet-WriteExcel-2.40/t/00_IEEE_double.t000644 000765 000024 00000001354 11304413104 021002 0ustar00Johnstaff000000 000000 ##################################################### # We start with some black magic to print on failure. BEGIN { $| = 1; print "1..2\n"; } END {print "not ok 1\n" unless $loaded;} use Spreadsheet::WriteExcel; $loaded = 1; print "ok 1\n"; ##################################################### # End of black magic. # TEST 2 # # Check if "pack" gives the required IEEE 64bit float my $teststr = pack "d", 1.2345; my @hexdata = (0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F); my $number = pack "C8", @hexdata; if ($number eq $teststr) { # Little Endian print "ok 2\n"; } elsif ($number eq reverse($teststr)){ # Big Endian print "ok 2\n"; } else { # Give up. I'll fix this in a later version. print "not ok 2\n"; } Spreadsheet-WriteExcel-2.40/t/01_add_worksheet.t000644 000765 000024 00000014405 11304413104 021566 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -wl ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for valid worksheet name handling. # # reverse(''), March 2005, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 69; # Tests for valid and invalid worksheet names my @tests1 = ( # Tests for valid names [ 'PASS', undef, 'No worksheet name' ], [ 'PASS', '', 'Blank worksheet name' ], [ 'PASS', 'Sheet10', 'Valid worksheet name' ], [ 'PASS', 'a' x 31, 'Valid 31 char name' ], # Tests for invalid names [ 'FAIL', 'Sheet1', 'Caught duplicate name' ], [ 'FAIL', 'Sheet2', 'Caught duplicate name' ], [ 'FAIL', 'Sheet3', 'Caught duplicate name' ], [ 'FAIL', 'sheet1', 'Caught case-insensitive name'], [ 'FAIL', 'SHEET1', 'Caught case-insensitive name'], [ 'FAIL', 'sheetz', 'Caught case-insensitive name'], [ 'FAIL', 'SHEETZ', 'Caught case-insensitive name'], [ 'FAIL', 'a' x 32, 'Caught long name' ], [ 'FAIL', '[', 'Caught invalid char' ], [ 'FAIL', ']', 'Caught invalid char' ], [ 'FAIL', ':', 'Caught invalid char' ], [ 'FAIL', '*', 'Caught invalid char' ], [ 'FAIL', '?', 'Caught invalid char' ], [ 'FAIL', '/', 'Caught invalid char' ], [ 'FAIL', '\\', 'Caught invalid char' ], ); ############################################################################### # # Tests 1. ASCII tests # my $test_file = "temp_test_file.xml"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet1 = $workbook->add_worksheet(); # Implicit name 'Sheet1' my $worksheet2 = $workbook->add_worksheet(); # Implicit name 'Sheet2' my $worksheet3 = $workbook->add_worksheet('Sheet3'); my $worksheet4 = $workbook->add_worksheet('Sheetz'); for my $test_ref (@tests1) { my $target = $test_ref->[0]; my $sheetname = $test_ref->[1]; my $caption = $test_ref->[2]; eval {$workbook->_check_sheetname($sheetname)}; my $result = $@ ? 'FAIL' : 'PASS'; $sheetname = 'undef' unless defined $sheetname; is($result, $target, sprintf " \t%-7s %-28s: %s", 'ASCII:', $caption, $sheetname); } $workbook->close(); ############################################################################### # # Tests 2. UTF16-BE tests # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); # Implicit name 'Sheet1' $worksheet2 = $workbook->add_worksheet(); # Implicit name 'Sheet2' $worksheet3 = $workbook->add_worksheet('Sheet3'); $worksheet4 = $workbook->add_worksheet("\0S\0h\0e\0e\0t\0z", 1); for my $test_ref (@tests1) { my $target = $test_ref->[0]; my $sheetname = $test_ref->[1]; my $caption = $test_ref->[2]; # Convert ASCII to UTF16-BE if not blank or undef $sheetname = pack "n*", unpack "C*", $sheetname if $sheetname; eval {$workbook->_check_sheetname($sheetname, 1)}; my $result = $@ ? 'FAIL' : 'PASS'; $sheetname = 'undef' unless defined $sheetname; # Change null byte to \0 for printing $sheetname =~ s/\0/\\0/g; is($result, $target, sprintf " \t%-7s %-28s: %s", 'UTF-16:', $caption, $sheetname); } $workbook->close(); ############################################################################### # # Tests 3. UTF-8 tests # SKIP: { my $uni = chr 0x263A; my @tests2 = ( # Tests for valid names [ 'PASS', $uni, 'Unicode char' ], [ 'PASS', $uni x 31, 'Valid 31 char name' ], # Tests for invalid names [ 'FAIL', chr 0x0438, 'Caught duplicate name' ], [ 'FAIL', chr 0x0418, 'Caught case-insensitive name'], [ 'FAIL', $uni x 32, 'Caught long name' ], [ 'FAIL', '[' . $uni, 'Caught invalid char' ], [ 'FAIL', ']' . $uni, 'Caught invalid char' ], [ 'FAIL', ':' . $uni, 'Caught invalid char' ], [ 'FAIL', '*' . $uni, 'Caught invalid char' ], [ 'FAIL', '?' . $uni, 'Caught invalid char' ], [ 'FAIL', '/' . $uni, 'Caught invalid char' ], [ 'FAIL', '\\'. $uni, 'Caught invalid char' ], ); skip "\tskipped tests requires Perl 5.8 Unicode support", 0 + @tests1 + @tests2 if $] < 5.008; $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); # Implicit name 'Sheet1' $worksheet2 = $workbook->add_worksheet(); # Implicit name 'Sheet2' $worksheet3 = $workbook->add_worksheet('Sheet3'); $worksheet4 = $workbook->add_worksheet("\0S\0h\0e\0e\0t\0z", 1); my $worksheet5 = $workbook->add_worksheet(chr 0x0438); for my $test_ref (@tests1) { my $target = $test_ref->[0]; my $sheetname = $test_ref->[1]; my $caption = $test_ref->[2]; require Encode; $sheetname = Encode::encode_utf8($sheetname) if $sheetname; eval {$workbook->_check_sheetname($sheetname)}; my $result = $@ ? 'FAIL' : 'PASS'; $sheetname = 'undef' unless defined $sheetname; # Change null byte to \0 for printing $sheetname =~ s/\0/\\0/g; is($result, $target, sprintf " \t%-7s %-28s: %s", 'UTF-8:', $caption, $sheetname); } for my $test_ref (@tests2) { my $target = $test_ref->[0]; my $sheetname = $test_ref->[1]; my $caption = $test_ref->[2]; eval {$workbook->_check_sheetname($sheetname)}; my $result = $@ ? 'FAIL' : 'PASS'; $sheetname = 'undef' unless defined $sheetname; is($result, $target, sprintf " \t%-7s %-28s: %s", 'UTF-8:', $caption, ''); } $workbook->close(); } unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/02_merge_formats.t000644 000765 000024 00000005220 11304413104 021571 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests to ensure merge formats aren't used in non-merged cells and # vice-versa. This is temporary feature to prevent users from inadvertently # making this error. # # reverse(''), April 2005, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 8; ############################################################################### # # Tests setup # my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); my $merged_format = $workbook->add_format(bold => 1); my $non_merged_format = $workbook->add_format(bold => 1); $worksheet->set_row(5, undef, $merged_format); $worksheet->set_column('G:G', undef, $merged_format); ############################################################################### # # Test # eval { $worksheet->write ('A1', 'Test', $non_merged_format); $worksheet->merge_range('A3:B4', 'Test', $merged_format ); }; ok(! $@, " \tNormal usage."); ############################################################################### # # Test # eval { $worksheet->write ('D1', 'Test', $merged_format ); }; ok( $@, " \tMerge format in non-merged cell."); ############################################################################### # # Test # eval { $worksheet->merge_range('D3:E4', 'Test', $non_merged_format); }; ok( $@, " \tNon merge format in merged cells."); ############################################################################### # # Test # eval { $worksheet->write('G1', 'Test',); }; ok( $@, " \tMerge format in column."); ############################################################################### # # Test # eval { $worksheet->write('A6', 'Test',); }; ok( $@, " \tMerge format in row."); ############################################################################### # # Test # eval { $worksheet->write('G6', 'Test',); }; ok( $@, " \tMerge format in column and row."); ############################################################################### # # Test # eval { $worksheet->write('H7', 'Test',); }; ok(! $@, " \tNo merge format in column and row."); ############################################################################### # # Test # eval { $worksheet->write ('A1', 'Test', $non_merged_format); $worksheet->merge_range('A3:B4', 'Test', $merged_format ); }; ok(! $@, " \tNormal usage again."); $workbook->close(); unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/03_cleanup.t000644 000765 000024 00000002761 11304413104 020376 0ustar00Johnstaff000000 000000 ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Test that garbage collection of a S::WE object doesn't clober $@; # # See: http://groups.google.com/group/spreadsheet-writeexcel/browse_thread/thread/f5007499fc381870 # # reverse(''), January 2007, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 2; ############################################################################### # # Tests setup # my $test_file = 'temp_test_file.xls'; my $die_message = "__SWE_test_message__\n"; ############################################################################### # # Test Spreadsheet::WriteExcel # eval { my $workbook = Spreadsheet::WriteExcel->new($test_file); die $die_message; # $workbook goes out of scope here and will be garbage collected. }; is ($@, $die_message, " \tCatching die message."); ############################################################################### # # Test Spreadsheet::WriteExcel::Big if possible. # SKIP: { eval { require OLE::Storage_Lite }; skip "\tTesting ::Big requires OLE::Storage_Lite", 1 if $@; require Spreadsheet::WriteExcel::Big; eval { my $workbook = Spreadsheet::WriteExcel::Big->new($test_file); die $die_message; # $workbook goes out of scope here and will be garbage collected. }; is( $@, $die_message, " \tCatching die message." ); } unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/04_dimensions.t000644 000765 000024 00000032505 11304413104 021117 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Check that the Excel DIMENSIONS record is written correctly. # # reverse(''), October 2007, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 31; ############################################################################### # # Tests setup # my $test_file = 'temp_test_file.xls'; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $format = $workbook->add_format(); my $worksheet; my @dims = qw(row_min row_max col_min col_max); my $data; my $caption; my %results; my %expected; my $error; my $smiley = pack "n", 0x263a; ############################################################################### # # Test 1. # $caption = " \tNo worksheet cell data."; $worksheet = $workbook->add_worksheet(); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (0, 0, 0, 0); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 2. # $caption = " \tData in cell (0, 0)."; $worksheet = $workbook->add_worksheet(); $worksheet->write(0, 0, 'Test'); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (0, 1, 0, 1); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 3. # $caption = " \tData in cell (0, 255)."; $worksheet = $workbook->add_worksheet(); $worksheet->write(0, 255, 'Test'); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (0, 1, 255, 256); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 4. # $caption = " \tData in cell (65535, 0)."; $worksheet = $workbook->add_worksheet(); $worksheet->write(65535, 0, 'Test'); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (65535, 65536, 0, 1); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 5. # $caption = " \tData in cell (65535, 255)."; $worksheet = $workbook->add_worksheet(); $worksheet->write(65535, 255, 'Test'); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (65535, 65536, 255, 256); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 6. # $caption = " \tData in cell (5, 3)."; $worksheet = $workbook->add_worksheet(); $worksheet->write(5, 3, 'Test'); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (5, 6, 3, 4); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 7. # $caption = " \tset_row() for row 4."; $worksheet = $workbook->add_worksheet(); $worksheet->set_row(4, 20); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (4, 5, 0, 0); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 8. # $caption = " \tset_row() for row 4..6."; $worksheet = $workbook->add_worksheet(); $worksheet->set_row(4, 20); $worksheet->set_row(5, 20); $worksheet->set_row(6, 20); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (4, 7, 0, 0); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 9. # $caption = " \tset_column() for row 4."; $worksheet = $workbook->add_worksheet(); $worksheet->set_column(4, 4, 20); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (0, 0, 0, 0); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 10. # $caption = " \tset_column() for row 4..6."; $worksheet = $workbook->add_worksheet(); $worksheet->set_column(4, 6, 20); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (0, 0, 0, 0); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 11. # $caption = " \tData in cell (0, 0) and set_row() for row 4."; $worksheet = $workbook->add_worksheet(); $worksheet->write(0, 0, 'Test'); $worksheet->set_row(4, 20); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (0, 5, 0, 1); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 12. # $caption = " \tData in cell (0, 0) and set_row() for row 4. Reverse order"; $worksheet = $workbook->add_worksheet(); $worksheet->set_row(4, 20); $worksheet->write(0, 0, 'Test'); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (0, 5, 0, 1); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 13. # $caption = " \tData in cell (5, 3) and set_row() for row 4."; $worksheet = $workbook->add_worksheet(); $worksheet->write(5, 3, 'Test'); $worksheet->set_row(4, 20); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (4, 6, 3, 4); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 14. # $caption = " \tComment in cell (5, 3)."; $worksheet = $workbook->add_worksheet(); $worksheet->write_comment(5, 3, 'Test'); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (5, 6, 3, 4); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 15 + 16. # $caption = " \tundef value for row"; $worksheet = $workbook->add_worksheet(); { # Ignore undef warning. $^W = 0; $error = $worksheet->write_string(undef, 1, 'Test'); }; $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (0, 0, 0, 0); is_deeply(\%results, \%expected, $caption); is (-2, $error, $caption . ' (return value)'); ############################################################################### # # Test 17 + 18. # $caption = " \tundef value for col"; $worksheet = $workbook->add_worksheet(); $error = $worksheet->write(1, undef, 'Test'); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (0, 0, 0, 0); is_deeply(\%results, \%expected, $caption); is (-2, $error, $caption . ' (return value)'); ############################################################################### # # Test 19. # $caption = " \tData in cell (5, 3) and (10, 1)."; $worksheet = $workbook->add_worksheet(); $worksheet->write(5, 3, 'Test'); $worksheet->write(10, 1, 'Test'); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (5, 11, 1, 4); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 20. # $caption = " \tData in cell (5, 3) and (10, 5)."; $worksheet = $workbook->add_worksheet(); $worksheet->write(5, 3, 'Test'); $worksheet->write(10, 5, 'Test'); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (5, 11, 3, 6); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 21. # $caption = " \twrite_string()"; $worksheet = $workbook->add_worksheet(); $worksheet->write_string(5, 3, 'Test'); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (5, 6, 3, 4); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 22. # $caption = " \twrite_number()"; $worksheet = $workbook->add_worksheet(); $worksheet->write_number(5, 3, 5); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (5, 6, 3, 4); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 23. # $caption = " \twrite_url()"; $worksheet = $workbook->add_worksheet(); $worksheet->write_url(5, 3, 'http://www.perl.com'); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (5, 6, 3, 4); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 24. # $caption = " \twrite_formula()"; $worksheet = $workbook->add_worksheet(); $worksheet->write_formula(5, 3, '= 1 + 2'); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (5, 6, 3, 4); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 25. # $caption = " \twrite_string()"; $worksheet = $workbook->add_worksheet(); $worksheet->write_string(5, 3, 'Test'); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (5, 6, 3, 4); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 26. # $caption = " \twrite_blank()"; $worksheet = $workbook->add_worksheet(); $worksheet->write_string(5, 3, $format); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (5, 6, 3, 4); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 27. # $caption = " \twrite_blank(). No format"; $worksheet = $workbook->add_worksheet(); $worksheet->write_string(5, 3); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (0, 0, 0, 0); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 28. # $caption = " \twrite_utf16be_string()"; $worksheet = $workbook->add_worksheet(); $worksheet->write_utf16be_string(5, 3, $smiley); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (5, 6, 3, 4); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 29. # $caption = " \twrite_utf16le_string()"; $worksheet = $workbook->add_worksheet(); $worksheet->write_utf16le_string(5, 3, $smiley); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (5, 6, 3, 4); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 30. # $caption = " \trepeat_formula()"; $worksheet = $workbook->add_worksheet(); my $formula = $worksheet->store_formula('=A1 * 3 + 50'); $worksheet->repeat_formula(5, 3, $formula, $format, 'A1', 'A2'); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (5, 6, 3, 4); is_deeply(\%results, \%expected, $caption); ############################################################################### # # Test 31. # $caption = " \tmerge_range()"; $worksheet = $workbook->add_worksheet(); $format = $workbook->add_format(); $worksheet->merge_range('C6:E8', 'Test', $format); $data = $worksheet ->_store_dimensions(); @results {@dims} = unpack 'x4 VVvv', $data; @expected{@dims} = (5, 8, 2, 5); is_deeply(\%results, \%expected, $caption); # Clean up. $workbook->close(); unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/05_rows.t000644 000765 000024 00000011214 11304413104 017734 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Check that max/min columns of the Excel ROW record are written correctly. # # reverse(''), October 2007, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 7; ############################################################################### # # Tests setup # my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet; my @rows; my @tests; my $row; my $col1; my $col2; $workbook->compatibility_mode(1); ############################################################################### # # Test 1. # $row = 1; $col1 = 0; $col2 = 0; $worksheet = $workbook->add_worksheet(); $worksheet->set_row($row, 15); push @tests, [ " \tset_row(): row = $row, col1 = $col1, col2 = $col2", { col_min => 0, col_max => 0, } ]; ############################################################################### # # Test 2. # $row = 2; $col1 = 0; $col2 = 0; $worksheet = $workbook->add_worksheet(); $worksheet->write($row, $col1, 'Test'); $worksheet->write($row, $col2, 'Test'); push @tests, [ " \twrite(): row = $row, col1 = $col1, col2 = $col2", { col_min => 0, col_max => 1, } ]; ############################################################################### # # Test 3. # $row = 3; $col1 = 0; $col2 = 1; $worksheet = $workbook->add_worksheet(); $worksheet->write($row, $col1, 'Test'); $worksheet->write($row, $col2, 'Test'); push @tests, [ " \twrite(): row = $row, col1 = $col1, col2 = $col2", { col_min => 0, col_max => 2, } ]; ############################################################################### # # Test 4. # $row = 4; $col1 = 1; $col2 = 1; $worksheet = $workbook->add_worksheet(); $worksheet->write($row, $col1, 'Test'); $worksheet->write($row, $col2, 'Test'); push @tests, [ " \twrite(): row = $row, col1 = $col1, col2 = $col2", { col_min => 1, col_max => 2, } ]; ############################################################################### # # Test 5. # $row = 5; $col1 = 1; $col2 = 255; $worksheet = $workbook->add_worksheet(); $worksheet->write($row, $col1, 'Test'); $worksheet->write($row, $col2, 'Test'); push @tests, [ " \twrite(): row = $row, col1 = $col1, col2 = $col2", { col_min => 1, col_max => 256, } ]; ############################################################################### # # Test 6. # $row = 6; $col1 = 255; $col2 = 255; $worksheet = $workbook->add_worksheet(); $worksheet->write($row, $col1, 'Test'); $worksheet->write($row, $col2, 'Test'); push @tests, [ " \twrite(): row = $row, col1 = $col1, col2 = $col2", { col_min => 255, col_max => 256, } ]; ############################################################################### # # Test 7. # $row = 7; $col1 = 2; $col2 = 9; $worksheet = $workbook->add_worksheet(); $worksheet->set_row($row, 15); $worksheet->write($row, $col1, 'Test'); $worksheet->write($row, $col2, 'Test'); push @tests, [ " \tset_row + write(): row = $row, col1 = $col1, col2 = $col2", { col_min => 2, col_max => 10, } ]; # Read in the row records $workbook->{_biff_only} = 1; $workbook->close(); open XLSFILE, $test_file or die "Couldn't open test file\n"; binmode XLSFILE; my $header; my $data; while (read XLSFILE, $header, 4) { my ($record, $length) = unpack 'vv', $header; read XLSFILE, $data, $length; # Read the row records only. next unless $record == 0x0208; my ($col_min, $col_max) = unpack 'x2 vv', $data; push @rows, { col_min => $col_min, col_max => $col_max, }; } for my $i (0 .. @tests -1) { is_deeply($rows[$i], $tests[$i]->[1], $tests[$i]->[0]); } # Clean up. unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/06_extsst.t000644 000765 000024 00000004754 11304413104 020310 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Check that we calculate the correct bucket size and number for the EXTSST # record. The data is taken from actual Excel files. # # reverse(''), October 2007, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 56; ############################################################################### # # Tests setup # my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my @tests = ( # Unique Number of Bucket # strings buckets size [0, 0, 8], [1, 1, 8], [7, 1, 8], [8, 1, 8], [15, 2, 8], [16, 2, 8], [17, 3, 8], [32, 4, 8], [33, 5, 8], [64, 8, 8], [128, 16, 8], [256, 32, 8], [512, 64, 8], [1023, 128, 8], [1024, 114, 9], [1025, 114, 9], [2048, 121, 17], [4096, 125, 33], [4097, 125, 33], [8192, 127, 65], [8193, 127, 65], [9000, 127, 71], [10000, 127, 79], [16384, 128, 129], [262144, 128, 2049], [1048576, 128, 8193], [4194304, 128, 32769], [8257536, 128, 64513], ); for my $test (@tests) { my $str_unique = $test->[0]; $workbook->{_str_unique} = $str_unique ; $workbook->_calculate_extsst_size(); is($workbook->{_extsst_buckets}, $test->[1], " \tBucket number for $str_unique strings"); is($workbook->{_extsst_bucket_size}, $test->[2], " \tBucket size for $str_unique strings"); } # Clean up. $workbook->{_str_unique} = 0; $workbook->close(); unlink $test_file; Spreadsheet-WriteExcel-2.40/t/07_set_propertes.t000644 000765 000024 00000001347 11304413104 021650 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests to ensure Spreadsheet::WriteExcel::Format->new() and set_properties # don't do unsafe things. # use strict; use Spreadsheet::WriteExcel::Format; use Test::More tests => 2; { my $ok = 0; eval { my $foo = Spreadsheet::WriteExcel::Format->new(0, font => q#'); die ('Error# ); $ok = 1; }; ok($ok, " Spreadsheet::WriteExcel::Format->new()"); } { my $ok = 0; my $format = Spreadsheet::WriteExcel::Format->new(); eval { my $foo = $format->set_properties(size => q#'); die ('Error# ); $ok = 1; }; ok($ok, " Spreadsheet::WriteExcel::Format->set_properties()"); } Spreadsheet-WriteExcel-2.40/t/11_date_time.t000644 000765 000024 00000055471 11304413104 020707 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests date and time handling. # # reverse(''), May 2004, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 100; my $date_time; my $number; my $result; my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); # Set float difference limit to half of an Excel millisecond my $flt_delta = 0.5/(24*60*60*1000); ############################################################################## # # Float comparison function. # sub flt_cmp { return abs($_[0] - $_[1]) < $flt_delta; } ############################################################################## # # Test the flt_cmp() function used in the other tests. # $date_time = '1899-12-31T00:00:00.0004'; $number = 0; $result = $worksheet->convert_date_time($date_time); $result = -1 unless defined $result; # Test 1. This should pass. It is less than the float diff limit. ok(flt_cmp($number, $result), " Testing convert_date_time: $date_time $number"); $date_time = '1899-12-31T00:00:00.0005'; $number = 0; $result = $worksheet->convert_date_time($date_time); $result = -1 unless defined $result; # Test 2. This should fail. It is equal to the float diff limit. my $diff = ! flt_cmp($number, $result); ok($diff, " Testing convert_date_time: $date_time $number"); ############################################################################## # # Test the time data generated in Excel. # while () { last if /^# stop/; # For debugging next unless /\S/; # Ignore blank lines next if /^#/; # Ignore comments if (/"DateTime">([^<]+)/) { my $date_time = $1; my $line = ; if ($line =~ /"Number">([^<]+)/) { my $number = 0 + $1; my $result = $worksheet->convert_date_time($date_time); $result = -1 unless defined $result; ok(flt_cmp($number, $result), " Testing convert_date_time: $date_time $number") or diag("difference between $number and $result\n" . "= " . abs($number -$result) . "\n". "> $flt_delta"); } } } # Clean up $workbook->close(); unlink $test_file; __DATA__ # Test data taken from Excel in XML format. 1899-12-31T00:00:00.000 0 1982-08-25T00:15:20.213 30188.010650613425 2065-04-19T00:16:48.290 60376.011670023145 2147-12-15T00:55:25.446 90565.038488958337 2230-08-10T01:02:46.891 120753.04359827546 2313-04-06T01:04:15.597 150942.04462496529 2395-11-30T01:09:40.889 181130.04838991899 2478-07-25T01:11:32.560 211318.04968240741 2561-03-21T01:30:19.169 241507.06272186342 2643-11-15T01:48:25.580 271695.07529606484 2726-07-12T02:03:31.919 301884.08578609955 2809-03-06T02:11:11.986 332072.09111094906 2891-10-31T02:24:37.095 362261.10042934027 2974-06-26T02:35:07.220 392449.10772245371 3057-02-19T02:45:12.109 422637.1147234838 3139-10-17T03:06:39.990 452826.12962951389 3222-06-11T03:08:08.251 483014.13065105322 3305-02-05T03:19:12.576 513203.13834 3387-10-01T03:29:42.574 543391.14563164348 3470-05-27T03:37:30.813 573579.15105107636 3553-01-21T04:14:38.231 603768.17683137732 3635-09-16T04:16:28.559 633956.17810832174 3718-05-13T04:17:58.222 664145.17914608796 3801-01-06T04:21:41.794 694333.18173372687 3883-09-02T04:56:35.792 724522.20596981479 3966-04-28T05:25:14.885 754710.2258667245 4048-12-21T05:26:05.724 784898.22645513888 4131-08-18T05:46:44.068 815087.24078782403 4214-04-13T05:48:01.141 845275.24167987274 4296-12-07T05:53:52.315 875464.24574438657 4379-08-03T06:14:48.580 905652.26028449077 4462-03-28T06:46:15.738 935840.28212659725 4544-11-22T07:31:20.407 966029.31343063654 4627-07-19T07:58:33.754 996217.33233511576 4710-03-15T08:07:43.130 1026406.3386936343 4792-11-07T08:29:11.091 1056594.3536005903 4875-07-04T09:08:15.328 1086783.3807329629 4958-02-27T09:30:41.781 1116971.3963169097 5040-10-23T09:34:04.462 1147159.3986627546 5123-06-20T09:37:23.945 1177348.4009715857 5206-02-12T09:37:56.655 1207536.4013501736 5288-10-08T09:45:12.230 1237725.406391551 5371-06-04T09:54:14.782 1267913.412671088 5454-01-28T09:54:22.108 1298101.4127558796 5536-09-24T10:01:36.151 1328290.4177795255 5619-05-20T12:09:48.602 1358478.5068125231 5702-01-14T12:34:08.549 1388667.5237100578 5784-09-08T12:56:06.495 1418855.5389640625 5867-05-06T12:58:58.217 1449044.5409515856 5949-12-30T12:59:54.263 1479232.5416002662 6032-08-24T13:34:41.331 1509420.5657561459 6115-04-21T13:58:28.601 1539609.5822754744 6197-12-14T14:02:16.899 1569797.5849178126 6280-08-10T14:36:17.444 1599986.6085352316 6363-04-06T14:37:57.451 1630174.60969272 6445-11-30T14:57:42.757 1660363.6234115392 6528-07-26T15:10:48.307 1690551.6325035533 6611-03-22T15:14:39.890 1720739.635183912 6693-11-15T15:19:47.988 1750928.6387498612 6776-07-11T16:04:24.344 1781116.6697262037 6859-03-07T16:22:23.952 1811305.6822216667 6941-10-31T16:29:55.999 1841493.6874536921 7024-06-26T16:58:20.259 1871681.7071789235 7107-02-21T17:04:02.415 1901870.7111390624 7189-10-16T17:18:29.630 1932058.7211762732 7272-06-11T17:47:21.323 1962247.7412190163 7355-02-05T17:53:29.866 1992435.7454845603 7437-10-02T17:53:41.076 2022624.7456143056 7520-05-28T17:55:06.044 2052812.7465977315 7603-01-21T18:14:49.151 2083000.7602910995 7685-09-16T18:17:45.738 2113189.7623349307 7768-05-12T18:29:59.700 2143377.7708298611 7851-01-07T18:33:21.233 2173566.773162419 7933-09-02T19:14:24.673 2203754.8016744559 8016-04-27T19:17:12.816 2233942.8036205554 8098-12-22T19:23:36.418 2264131.8080603937 8181-08-17T19:46:25.908 2294319.8239109721 8264-04-13T20:07:47.314 2324508.8387420601 8346-12-08T20:31:37.603 2354696.855296331 8429-08-03T20:39:57.770 2384885.8610853008 8512-03-29T20:50:17.067 2415073.8682530904 8594-11-22T21:02:57.827 2445261.8770581828 8677-07-19T21:23:05.519 2475450.8910360998 8760-03-14T21:34:49.572 2505638.8991848612 8842-11-08T21:39:05.944 2535827.9021521294 8925-07-04T21:39:18.426 2566015.9022965971 9008-02-28T21:46:07.769 2596203.9070343636 9090-10-24T21:57:55.662 2626392.9152275696 9173-06-19T22:19:11.732 2656580.9299968979 9256-02-13T22:23:51.376 2686769.9332335186 9338-10-09T22:27:58.771 2716957.9360968866 9421-06-05T22:43:30.392 2747146.9468795368 9504-01-30T22:48:25.834 2777334.9502990046 9586-09-24T22:53:51.727 2807522.9540709145 9669-05-20T23:12:56.536 2837711.9673210187 9752-01-14T23:15:54.109 2867899.9693762613 9834-09-10T23:17:12.632 2898088.9702850925 9999-12-31T23:59:59.000 2958465.999988426 Spreadsheet-WriteExcel-2.40/t/12_date_only.t000644 000765 000024 00000032440 11304413104 020722 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests date and time handling. Tests dates in 1900 and 1904 format. # # reverse(''), May 2004, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 420; my $date_time; my $number; my $result; my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); ############################################################################## # # Test the dates generated by Excel. # while () { last if /^# stop/; # For debugging $worksheet->{_1904} = 0 if /Excel 1900/; # Change date system $worksheet->{_1904} = 1 if /Excel 1904/; # Change date system next unless /\S/; # Ignore blank lines next if /^#/; # Ignore comments my ($count, $date, $result) = split; my $number = $worksheet->convert_date_time($date); $number = -1 unless defined $number; is($number, $result, " Testing convert_date_time: $date $result") } # Clean up $workbook->close(); unlink $test_file; __DATA__ # # The following data was generated by Excel. # # # Excel 1900 date system # 1 1899-12-31T 0 2 1900-01-00T 0 3 1900-01-01T 1 4 1900-02-27T 58 5 1900-02-28T 59 6 1900-02-29T 60 7 1900-03-01T 61 8 1900-03-02T 62 9 1900-03-11T 71 10 1900-04-08T 99 11 1900-09-12T 256 12 1901-05-03T 489 13 1901-10-13T 652 14 1902-02-15T 777 15 1902-06-06T 888 16 1902-09-25T 999 17 1902-09-27T 1001 18 1903-04-26T 1212 19 1903-08-05T 1313 20 1903-12-31T 1461 21 1904-01-01T 1462 22 1904-02-28T 1520 23 1904-02-29T 1521 24 1904-03-01T 1522 25 1907-02-27T 2615 26 1907-02-28T 2616 27 1907-03-01T 2617 28 1907-03-02T 2618 29 1907-03-03T 2619 30 1907-03-04T 2620 31 1907-03-05T 2621 32 1907-03-06T 2622 33 1999-01-01T 36161 34 1999-01-31T 36191 35 1999-02-01T 36192 36 1999-02-28T 36219 37 1999-03-01T 36220 38 1999-03-31T 36250 39 1999-04-01T 36251 40 1999-04-30T 36280 41 1999-05-01T 36281 42 1999-05-31T 36311 43 1999-06-01T 36312 44 1999-06-30T 36341 45 1999-07-01T 36342 46 1999-07-31T 36372 47 1999-08-01T 36373 48 1999-08-31T 36403 49 1999-09-01T 36404 50 1999-09-30T 36433 51 1999-10-01T 36434 52 1999-10-31T 36464 53 1999-11-01T 36465 54 1999-11-30T 36494 55 1999-12-01T 36495 56 1999-12-31T 36525 57 2000-01-01T 36526 58 2000-01-31T 36556 59 2000-02-01T 36557 60 2000-02-29T 36585 61 2000-03-01T 36586 62 2000-03-31T 36616 63 2000-04-01T 36617 64 2000-04-30T 36646 65 2000-05-01T 36647 66 2000-05-31T 36677 67 2000-06-01T 36678 68 2000-06-30T 36707 69 2000-07-01T 36708 70 2000-07-31T 36738 71 2000-08-01T 36739 72 2000-08-31T 36769 73 2000-09-01T 36770 74 2000-09-30T 36799 75 2000-10-01T 36800 76 2000-10-31T 36830 77 2000-11-01T 36831 78 2000-11-30T 36860 79 2000-12-01T 36861 80 2000-12-31T 36891 81 2001-01-01T 36892 82 2001-01-31T 36922 83 2001-02-01T 36923 84 2001-02-28T 36950 85 2001-03-01T 36951 86 2001-03-31T 36981 87 2001-04-01T 36982 88 2001-04-30T 37011 89 2001-05-01T 37012 90 2001-05-31T 37042 91 2001-06-01T 37043 92 2001-06-30T 37072 93 2001-07-01T 37073 94 2001-07-31T 37103 95 2001-08-01T 37104 96 2001-08-31T 37134 97 2001-09-01T 37135 98 2001-09-30T 37164 99 2001-10-01T 37165 100 2001-10-31T 37195 101 2001-11-01T 37196 102 2001-11-30T 37225 103 2001-12-01T 37226 104 2001-12-31T 37256 105 2400-01-01T 182623 106 2400-01-31T 182653 107 2400-02-01T 182654 108 2400-02-29T 182682 109 2400-03-01T 182683 110 2400-03-31T 182713 111 2400-04-01T 182714 112 2400-04-30T 182743 113 2400-05-01T 182744 114 2400-05-31T 182774 115 2400-06-01T 182775 116 2400-06-30T 182804 117 2400-07-01T 182805 118 2400-07-31T 182835 119 2400-08-01T 182836 120 2400-08-31T 182866 121 2400-09-01T 182867 122 2400-09-30T 182896 123 2400-10-01T 182897 124 2400-10-31T 182927 125 2400-11-01T 182928 126 2400-11-30T 182957 127 2400-12-01T 182958 128 2400-12-31T 182988 129 4000-01-01T 767011 130 4000-01-31T 767041 131 4000-02-01T 767042 132 4000-02-29T 767070 133 4000-03-01T 767071 134 4000-03-31T 767101 135 4000-04-01T 767102 136 4000-04-30T 767131 137 4000-05-01T 767132 138 4000-05-31T 767162 139 4000-06-01T 767163 140 4000-06-30T 767192 141 4000-07-01T 767193 142 4000-07-31T 767223 143 4000-08-01T 767224 144 4000-08-31T 767254 145 4000-09-01T 767255 146 4000-09-30T 767284 147 4000-10-01T 767285 148 4000-10-31T 767315 149 4000-11-01T 767316 150 4000-11-30T 767345 151 4000-12-01T 767346 152 4000-12-31T 767376 153 4321-01-01T 884254 154 4321-01-31T 884284 155 4321-02-01T 884285 156 4321-02-28T 884312 157 4321-03-01T 884313 158 4321-03-31T 884343 159 4321-04-01T 884344 160 4321-04-30T 884373 161 4321-05-01T 884374 162 4321-05-31T 884404 163 4321-06-01T 884405 164 4321-06-30T 884434 165 4321-07-01T 884435 166 4321-07-31T 884465 167 4321-08-01T 884466 168 4321-08-31T 884496 169 4321-09-01T 884497 170 4321-09-30T 884526 171 4321-10-01T 884527 172 4321-10-31T 884557 173 4321-11-01T 884558 174 4321-11-30T 884587 175 4321-12-01T 884588 176 4321-12-31T 884618 177 9999-01-01T 2958101 178 9999-01-31T 2958131 179 9999-02-01T 2958132 180 9999-02-28T 2958159 181 9999-03-01T 2958160 182 9999-03-31T 2958190 183 9999-04-01T 2958191 184 9999-04-30T 2958220 185 9999-05-01T 2958221 186 9999-05-31T 2958251 187 9999-06-01T 2958252 188 9999-06-30T 2958281 189 9999-07-01T 2958282 190 9999-07-31T 2958312 191 9999-08-01T 2958313 192 9999-08-31T 2958343 193 9999-09-01T 2958344 194 9999-09-30T 2958373 195 9999-10-01T 2958374 196 9999-10-31T 2958404 197 9999-11-01T 2958405 198 9999-11-30T 2958434 199 9999-12-01T 2958435 200 9999-12-31T 2958465 # # Excel 1904 date system # 201 1904-01-01T 0 202 1904-01-31T 30 203 1904-02-01T 31 204 1904-02-29T 59 205 1904-03-01T 60 206 1904-03-31T 90 207 1904-04-01T 91 208 1904-04-30T 120 209 1904-05-01T 121 210 1904-05-31T 151 211 1904-06-01T 152 212 1904-06-30T 181 213 1904-07-01T 182 214 1904-07-31T 212 215 1904-08-01T 213 216 1904-08-31T 243 217 1904-09-01T 244 218 1904-09-30T 273 219 1904-10-01T 274 220 1904-10-31T 304 221 1904-11-01T 305 222 1904-11-30T 334 223 1904-12-01T 335 224 1904-12-31T 365 225 1907-02-27T 1153 226 1907-02-28T 1154 227 1907-03-01T 1155 228 1907-03-02T 1156 229 1907-03-03T 1157 230 1907-03-04T 1158 231 1907-03-05T 1159 232 1907-03-06T 1160 233 1999-01-01T 34699 234 1999-01-31T 34729 235 1999-02-01T 34730 236 1999-02-28T 34757 237 1999-03-01T 34758 238 1999-03-31T 34788 239 1999-04-01T 34789 240 1999-04-30T 34818 241 1999-05-01T 34819 242 1999-05-31T 34849 243 1999-06-01T 34850 244 1999-06-30T 34879 245 1999-07-01T 34880 246 1999-07-31T 34910 247 1999-08-01T 34911 248 1999-08-31T 34941 249 1999-09-01T 34942 250 1999-09-30T 34971 251 1999-10-01T 34972 252 1999-10-31T 35002 253 1999-11-01T 35003 254 1999-11-30T 35032 255 1999-12-01T 35033 256 1999-12-31T 35063 257 2000-01-01T 35064 258 2000-01-31T 35094 259 2000-02-01T 35095 260 2000-02-29T 35123 261 2000-03-01T 35124 262 2000-03-31T 35154 263 2000-04-01T 35155 264 2000-04-30T 35184 265 2000-05-01T 35185 266 2000-05-31T 35215 267 2000-06-01T 35216 268 2000-06-30T 35245 269 2000-07-01T 35246 270 2000-07-31T 35276 271 2000-08-01T 35277 272 2000-08-31T 35307 273 2000-09-01T 35308 274 2000-09-30T 35337 275 2000-10-01T 35338 276 2000-10-31T 35368 277 2000-11-01T 35369 278 2000-11-30T 35398 279 2000-12-01T 35399 280 2000-12-31T 35429 281 2001-01-01T 35430 282 2001-01-31T 35460 283 2001-02-01T 35461 284 2001-02-28T 35488 285 2001-03-01T 35489 286 2001-03-31T 35519 287 2001-04-01T 35520 288 2001-04-30T 35549 289 2001-05-01T 35550 290 2001-05-31T 35580 291 2001-06-01T 35581 292 2001-06-30T 35610 293 2001-07-01T 35611 294 2001-07-31T 35641 295 2001-08-01T 35642 296 2001-08-31T 35672 297 2001-09-01T 35673 298 2001-09-30T 35702 299 2001-10-01T 35703 300 2001-10-31T 35733 301 2001-11-01T 35734 302 2001-11-30T 35763 303 2001-12-01T 35764 304 2001-12-31T 35794 305 2400-01-01T 181161 306 2400-01-31T 181191 307 2400-02-01T 181192 308 2400-02-29T 181220 309 2400-03-01T 181221 310 2400-03-31T 181251 311 2400-04-01T 181252 312 2400-04-30T 181281 313 2400-05-01T 181282 314 2400-05-31T 181312 315 2400-06-01T 181313 316 2400-06-30T 181342 317 2400-07-01T 181343 318 2400-07-31T 181373 319 2400-08-01T 181374 320 2400-08-31T 181404 321 2400-09-01T 181405 322 2400-09-30T 181434 323 2400-10-01T 181435 324 2400-10-31T 181465 325 2400-11-01T 181466 326 2400-11-30T 181495 327 2400-12-01T 181496 328 2400-12-31T 181526 329 4000-01-01T 765549 330 4000-01-31T 765579 331 4000-02-01T 765580 332 4000-02-29T 765608 333 4000-03-01T 765609 334 4000-03-31T 765639 335 4000-04-01T 765640 336 4000-04-30T 765669 337 4000-05-01T 765670 338 4000-05-31T 765700 339 4000-06-01T 765701 340 4000-06-30T 765730 341 4000-07-01T 765731 342 4000-07-31T 765761 343 4000-08-01T 765762 344 4000-08-31T 765792 345 4000-09-01T 765793 346 4000-09-30T 765822 347 4000-10-01T 765823 348 4000-10-31T 765853 349 4000-11-01T 765854 350 4000-11-30T 765883 351 4000-12-01T 765884 352 4000-12-31T 765914 353 4321-01-01T 882792 354 4321-01-31T 882822 355 4321-02-01T 882823 356 4321-02-28T 882850 357 4321-03-01T 882851 358 4321-03-31T 882881 359 4321-04-01T 882882 360 4321-04-30T 882911 361 4321-05-01T 882912 362 4321-05-31T 882942 363 4321-06-01T 882943 364 4321-06-30T 882972 365 4321-07-01T 882973 366 4321-07-31T 883003 367 4321-08-01T 883004 368 4321-08-31T 883034 369 4321-09-01T 883035 370 4321-09-30T 883064 371 4321-10-01T 883065 372 4321-10-31T 883095 373 4321-11-01T 883096 374 4321-11-30T 883125 375 4321-12-01T 883126 376 4321-12-31T 883156 377 9999-01-01T 2956639 378 9999-01-31T 2956669 379 9999-02-01T 2956670 380 9999-02-28T 2956697 381 9999-03-01T 2956698 382 9999-03-31T 2956728 383 9999-04-01T 2956729 384 9999-04-30T 2956758 385 9999-05-01T 2956759 386 9999-05-31T 2956789 387 9999-06-01T 2956790 388 9999-06-30T 2956819 389 9999-07-01T 2956820 390 9999-07-31T 2956850 391 9999-08-01T 2956851 392 9999-08-31T 2956881 393 9999-09-01T 2956882 394 9999-09-30T 2956911 395 9999-10-01T 2956912 396 9999-10-31T 2956942 397 9999-11-01T 2956943 398 9999-11-30T 2956972 399 9999-12-01T 2956973 400 9999-12-31T 2957003 # # The following dates are invalid. # # # Excel 1900 date system # 401 0000-12-30T -1 # Below year range. 402 1000-12-30T -1 # Below year range. 403 1899-12-30T -1 # Below year range. 404 2002-02-29T -1 # False leap-day. 405 2000-00-00T -1 # No month or day. 406 2000-01-00T -1 # No day. 407 2000-00-01T -1 # No month. 408 2000-13-01T -1 # Month out of range. 409 2000-12-32T -1 # Day out of range. 410 10000-01-01T -1 # Year out of range. # # Excel 1904 date system # 411 1899-12-31T -1 # Below year range. 412 1900-01-01T -1 # Below year range. 413 1903-12-31T -1 # Below year range. 414 2001-02-29T -1 # False leap-day. 415 2000-00-00T -1 # No month or day. 416 2000-01-00T -1 # No day. 417 2000-00-01T -1 # No month. 418 2000-13-01T -1 # Month out of range. 419 2000-12-32T -1 # Day out of range. 420 10000-01-01T -1 # Year out of range. Spreadsheet-WriteExcel-2.40/t/13_date_seconds.t000644 000765 000024 00000057561 12053236151 021422 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests date and time second handling. # # reverse('�'), May 2004, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 104; my $date_time; my $number; my $result; my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); # Set float difference limit to half of an Excel millisecond my $flt_delta = 0.5/(24*60*60*1000); ############################################################################## # # Float comparison function. # sub flt_cmp { return abs($_[0] - $_[1]) < $flt_delta; } ############################################################################## # # Test the flt_cmp() function used in the other tests. # $date_time = '1899-12-31T00:00:00.0004'; $number = 0; $result = $worksheet->convert_date_time($date_time); $result = -1 unless defined $result; # Test 1. This should pass. It is less than the float diff limit. ok(flt_cmp($number, $result), " Testing flt_cmp() for $date_time $number"); $date_time = '1899-12-31T00:00:00.0005'; $number = 0; $result = $worksheet->convert_date_time($date_time); $result = -1 unless defined $result; # Test 2. This should fail. It is equal to the float diff limit. my $diff = ! flt_cmp($number, $result); ok($diff, " Testing flt_cmp() for $date_time $number"); ############################################################################## # # Test some false times. # # These should fail. my $fail; $date_time = '1899-12-31T24:00:00.000'; $fail = ! $worksheet->convert_date_time($date_time); ok($fail, " Testing incorrect time: $date_time\tincorrect hour caught."); $date_time = '1899-12-31T00:60:00.000'; $fail = ! $worksheet->convert_date_time($date_time); ok($fail, " Testing incorrect time: $date_time\tincorrect mins caught."); $date_time = '1899-12-31T00:00:60.000'; $fail = ! $worksheet->convert_date_time($date_time); ok($fail, " Testing incorrect time: $date_time\tincorrect secs caught."); $date_time = '1899-12-31T00:00:59.999999999999999999'; # Workaround for increased precision with longdouble perls. $date_time .= "9" x length (1 - 1e-16); $fail = ! $worksheet->convert_date_time($date_time); ok($fail, " Testing incorrect time: $date_time\tincorrect secs caught."); ############################################################################## # # Test the time data generated in Excel. # while () { last if /^# stop/; # For debugging next unless /\S/; # Ignore blank lines next if /^#/; # Ignore comments if (/"DateTime">([^<]+)/) { my $date_time = $1; my $line = ; if ($line =~ /"Number">([^<]+)/) { my $number = 0 + $1; my $result = $worksheet->convert_date_time($date_time); $result = -1 unless defined $result; ok(flt_cmp($number, $result), " Testing convert_date_time: $date_time $number") or diag("difference between $number and $result\n" . "= " . abs($number -$result) . "\n". "> $flt_delta"); } } } # Clean up $workbook->close(); unlink $test_file; __DATA__ # Test data taken from Excel in XML format. 1899-12-31T00:00:00.000 0 1899-12-31T00:15:20.213 1.0650613425925924E-2 1899-12-31T00:16:48.290 1.1670023148148148E-2 1899-12-31T00:55:25.446 3.8488958333333337E-2 1899-12-31T01:02:46.891 4.3598275462962965E-2 1899-12-31T01:04:15.597 4.4624965277777782E-2 1899-12-31T01:09:40.889 4.8389918981481483E-2 1899-12-31T01:11:32.560 4.9682407407407404E-2 1899-12-31T01:30:19.169 6.2721863425925936E-2 1899-12-31T01:48:25.580 7.5296064814814809E-2 1899-12-31T02:03:31.919 8.5786099537037031E-2 1899-12-31T02:11:11.986 9.1110949074074077E-2 1899-12-31T02:24:37.095 0.10042934027777778 1899-12-31T02:35:07.220 0.1077224537037037 1899-12-31T02:45:12.109 0.11472348379629631 1899-12-31T03:06:39.990 0.12962951388888888 1899-12-31T03:08:08.251 0.13065105324074075 1899-12-31T03:19:12.576 0.13833999999999999 1899-12-31T03:29:42.574 0.14563164351851851 1899-12-31T03:37:30.813 0.1510510763888889 1899-12-31T04:14:38.231 0.1768313773148148 1899-12-31T04:16:28.559 0.17810832175925925 1899-12-31T04:17:58.222 0.17914608796296297 1899-12-31T04:21:41.794 0.18173372685185185 1899-12-31T04:56:35.792 0.2059698148148148 1899-12-31T05:25:14.885 0.22586672453703704 1899-12-31T05:26:05.724 0.22645513888888891 1899-12-31T05:46:44.068 0.24078782407407406 1899-12-31T05:48:01.141 0.2416798726851852 1899-12-31T05:53:52.315 0.24574438657407408 1899-12-31T06:14:48.580 0.26028449074074073 1899-12-31T06:46:15.738 0.28212659722222222 1899-12-31T07:31:20.407 0.31343063657407405 1899-12-31T07:58:33.754 0.33233511574074076 1899-12-31T08:07:43.130 0.33869363425925925 1899-12-31T08:29:11.091 0.35360059027777774 1899-12-31T09:08:15.328 0.380732962962963 1899-12-31T09:30:41.781 0.39631690972222228 1899-12-31T09:34:04.462 0.39866275462962958 1899-12-31T09:37:23.945 0.40097158564814817 1899-12-31T09:37:56.655 0.40135017361111114 1899-12-31T09:45:12.230 0.40639155092592594 1899-12-31T09:54:14.782 0.41267108796296298 1899-12-31T09:54:22.108 0.41275587962962962 1899-12-31T10:01:36.151 0.41777952546296299 1899-12-31T12:09:48.602 0.50681252314814818 1899-12-31T12:34:08.549 0.52371005787037039 1899-12-31T12:56:06.495 0.53896406249999995 1899-12-31T12:58:58.217 0.54095158564814816 1899-12-31T12:59:54.263 0.54160026620370372 1899-12-31T13:34:41.331 0.56575614583333333 1899-12-31T13:58:28.601 0.58227547453703699 1899-12-31T14:02:16.899 0.58491781249999997 1899-12-31T14:36:17.444 0.60853523148148148 1899-12-31T14:37:57.451 0.60969271990740748 1899-12-31T14:57:42.757 0.6234115393518519 1899-12-31T15:10:48.307 0.6325035532407407 1899-12-31T15:14:39.890 0.63518391203703706 1899-12-31T15:19:47.988 0.63874986111111109 1899-12-31T16:04:24.344 0.66972620370370362 1899-12-31T16:22:23.952 0.68222166666666662 1899-12-31T16:29:55.999 0.6874536921296297 1899-12-31T16:58:20.259 0.70717892361111112 1899-12-31T17:04:02.415 0.71113906250000003 1899-12-31T17:18:29.630 0.72117627314814825 1899-12-31T17:47:21.323 0.74121901620370367 1899-12-31T17:53:29.866 0.74548456018518516 1899-12-31T17:53:41.076 0.74561430555555563 1899-12-31T17:55:06.044 0.74659773148148145 1899-12-31T18:14:49.151 0.760291099537037 1899-12-31T18:17:45.738 0.76233493055555546 1899-12-31T18:29:59.700 0.77082986111111118 1899-12-31T18:33:21.233 0.77316241898148153 1899-12-31T19:14:24.673 0.80167445601851861 1899-12-31T19:17:12.816 0.80362055555555545 1899-12-31T19:23:36.418 0.80806039351851855 1899-12-31T19:46:25.908 0.82391097222222232 1899-12-31T20:07:47.314 0.83874206018518516 1899-12-31T20:31:37.603 0.85529633101851854 1899-12-31T20:39:57.770 0.86108530092592594 1899-12-31T20:50:17.067 0.86825309027777775 1899-12-31T21:02:57.827 0.87705818287037041 1899-12-31T21:23:05.519 0.891036099537037 1899-12-31T21:34:49.572 0.89918486111111118 1899-12-31T21:39:05.944 0.90215212962962965 1899-12-31T21:39:18.426 0.90229659722222222 1899-12-31T21:46:07.769 0.90703436342592603 1899-12-31T21:57:55.662 0.91522756944444439 1899-12-31T22:19:11.732 0.92999689814814823 1899-12-31T22:23:51.376 0.93323351851851843 1899-12-31T22:27:58.771 0.93609688657407408 1899-12-31T22:43:30.392 0.94687953703703709 1899-12-31T22:48:25.834 0.95029900462962968 1899-12-31T22:53:51.727 0.95407091435185187 1899-12-31T23:12:56.536 0.96732101851851848 1899-12-31T23:15:54.109 0.96937626157407408 1899-12-31T23:17:12.632 0.97028509259259266 1899-12-31T23:59:59.999 0.99999998842592586 Spreadsheet-WriteExcel-2.40/t/21_escher.t000644 000765 000024 00000060302 11304413104 020213 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for the internal methods used to write the records in an Escher drawing # object such as images, comments and filters. # # reverse(''), September 2005, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 42; my @tests = ( [ 'DggContainer', # Caption 0xF000, # Type 15, # Version 0, # Instance '', # Data 82, # Length '0F 00 00 F0 52 00 00 00', # Target ], [ 'DgContainer', # Caption 0xF002, # Type 15, # Version 0, # Instance '', # Data 328, # Length '0F 00 02 F0 48 01 00 00', # Target ], [ 'SpgrContainer', # Caption 0xF003, # Type 15, # Version 0, # Instance '', # Data 304, # Length '0F 00 03 F0 30 01 00 00', # Target ], [ 'SpContainer', # Caption 0xF004, # Type 15, # Version 0, # Instance '', # Data 40, # Length '0F 00 04 F0 28 00 00 00', # Target ], [ 'Dgg', # Caption 0xF006, # Type 0, # Version 0, # Instance '02 04 00 00 02 00 00 00 ' . # Data '02 00 00 00 01 00 00 00 ' . '01 00 00 00 02 00 00 00', undef, # Length '00 00 06 F0 18 00 00 00 ' . # Target '02 04 00 00 02 00 00 00 ' . '02 00 00 00 01 00 00 00 ' . '01 00 00 00 02 00 00 00', ], [ 'Dg', # Caption 0xF008, # Type 0, # Version 1, # Instance '03 00 00 00 02 04 00 00', # Data undef, # Length '10 00 08 F0 08 00 00 00 ' . # Target '03 00 00 00 02 04 00 00', ], [ 'Spgr', # Caption 0xF009, # Type 1, # Version 0, # Instance '00 0E 00 0E 40 41 00 00 ' . # Data '00 0E 00 0E 40 41 00 00', undef, # Length '01 00 09 F0 10 00 00 00 ' . # Target '00 0E 00 0E 40 41 00 00 ' . '00 0E 00 0E 40 41 00 00', ], [ 'ClientTextbox', # Caption 0xF00D, # Type 0, # Version 0, # Instance '', # Data undef, # Length '00 00 0D F0 00 00 00 00', # Target ], [ 'ClientAnchor', # Caption 0xF010, # Type 0, # Version 0, # Instance '03 00 01 00 F0 00 01 00 ' . # Data '69 00 03 00 F0 00 05 00 ' . 'C4 00', undef, # Length '00 00 10 F0 12 00 00 00 ' . # Target '03 00 01 00 F0 00 01 00 ' . '69 00 03 00 F0 00 05 00 ' . 'C4 00', ], [ 'ClientData', # Caption 0xF011, # Type 0, # Version 0, # Instance '', # Data undef, # Length '00 00 11 F0 00 00 00 00', # Target ], [ 'SplitMenuColors', # Caption 0xF11E, # Type 0, # Version 4, # Instance '0D 00 00 08 0C 00 00 08 ' . # Data '17 00 00 08 F7 00 00 10', undef, # Length '40 00 1E F1 10 00 00 00 ' . # Target '0D 00 00 08 0C 00 00 08 ' . '17 00 00 08 F7 00 00 10', ], [ 'BstoreContainer', # Caption 0xF001, # Type 15, # Version 1, # Instance '', # Data 163, # Length '1F 00 01 F0 A3 00 00 00', # Target ], ); ############################################################################### # # Tests setup # my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); my $target; my $result; my $caption; my @data; my $range; ############################################################################### # # Tests for the generic method. # for my $aref (@tests) { my @data = @$aref; my $caption = shift @data; my $target = pop @data; $data[3] =~ s/ //g; $data[3] = pack "H*", $data[3]; $caption = sprintf " \t_add_mso_generic(): (0x%04X) %s", $data[0], $caption; $result = unpack_record($worksheet->_add_mso_generic(@data)); is($result, $target, $caption); } ############################################################################### # # Test for _store_mso_dg_container. # $caption = sprintf " \t_store_mso_dgg_container()"; @data = (); $target = join ' ', qw( 0F 00 00 F0 52 00 00 00 ); $workbook->{_mso_size} = 94; $result = unpack_record($workbook->_store_mso_dgg_container(@data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_dgg. # $caption = sprintf " \t_store_mso_dgg()"; @data = (1026, 2, 2, 1, [[1,2]]); $target = join ' ', qw( 00 00 06 F0 18 00 00 00 02 04 00 00 02 00 00 00 02 00 00 00 01 00 00 00 01 00 00 00 02 00 00 00 ); $result = unpack_record($workbook->_store_mso_dgg(@data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_opt. # $caption = sprintf " \t_store_mso_opt()"; @data = (); $target = join ' ', qw( 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 ); $result = unpack_record($workbook->_store_mso_opt(@data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_split_menu_colors. # $caption = sprintf " \t_store_mso_split_menu_colors()"; @data = (); $target = join ' ', qw( 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $result = unpack_record($workbook->_store_mso_split_menu_colors(@data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_dg_container. # $caption = sprintf " \t_store_mso_dg_container()"; @data = (0xC8); $target = join ' ', qw( 0F 00 02 F0 C8 00 00 00 ); $result = unpack_record($worksheet->_store_mso_dg_container(@data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_dg. # $caption = sprintf " \t_store_mso_dg()"; @data = (1, 2, 1025); $target = join ' ', qw( 10 00 08 F0 08 00 00 00 02 00 00 00 01 04 00 00 ); $result = unpack_record($worksheet->_store_mso_dg(@data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_spgr_container. # $caption = sprintf " \t_store_mso_spgr_container()"; @data = (0xB0); $target = join ' ', qw( 0F 00 03 F0 B0 00 00 00 ); $result = unpack_record($worksheet->_store_mso_spgr_container(@data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_sp_container. # $caption = sprintf " \t_store_mso_sp_container()"; @data = (0x28); $target = join ' ', qw( 0F 00 04 F0 28 00 00 00 ); $result = unpack_record($worksheet->_store_mso_sp_container(@data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_sp. # $caption = sprintf " \t_store_mso_sp()"; @data = (0, 1024, 0x0005); $target = join ' ', qw( 02 00 0A F0 08 00 00 00 00 04 00 00 05 00 00 00 ); $result = unpack_record($worksheet->_store_mso_sp(@data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_sp. # $caption = sprintf " \t_store_mso_sp()"; @data = (202, 1025, 0x0A00); $target = join ' ', qw( A2 0C 0A F0 08 00 00 00 01 04 00 00 00 0A 00 00 ); $result = unpack_record($worksheet->_store_mso_sp(@data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_opt_comment. # $caption = sprintf " \t_store_mso_opt_comment()"; @data = (0x80); $target = join ' ', qw( 93 00 0B F0 36 00 00 00 80 00 00 00 00 00 BF 00 08 00 08 00 58 01 00 00 00 00 81 01 50 00 00 08 83 01 50 00 00 08 BF 01 10 00 11 00 01 02 00 00 00 00 3F 02 03 00 03 00 BF 03 02 00 0A 00 ); $result = unpack_record($worksheet->_store_mso_opt_comment(@data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_anchor. # $range = 'A1'; $caption = sprintf " \t_store_mso_client_anchor(%s)", $range; @data = $worksheet->_substitute_cellref($range); @data = $worksheet->_comment_params(@data, 'Test'); @data = @{$data[-1]}; $target = join ' ', qw( 00 00 10 F0 12 00 00 00 03 00 01 00 F0 00 00 00 1E 00 03 00 F0 00 04 00 78 00 ); $result = unpack_record($worksheet->_store_mso_client_anchor(3, @data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_anchor. # $range = 'A2'; $caption = sprintf " \t_store_mso_client_anchor(%s)", $range; @data = $worksheet->_substitute_cellref($range); @data = $worksheet->_comment_params(@data, 'Test'); @data = @{$data[-1]}; $target = join ' ', qw( 00 00 10 F0 12 00 00 00 03 00 01 00 F0 00 00 00 69 00 03 00 F0 00 04 00 C4 00 ); $result = unpack_record($worksheet->_store_mso_client_anchor(3, @data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_anchor. # $range = 'A3'; $caption = sprintf " \t_store_mso_client_anchor(%s)", $range; @data = $worksheet->_substitute_cellref($range); @data = $worksheet->_comment_params(@data, 'Test'); @data = @{$data[-1]}; $target = join ' ', qw( 00 00 10 F0 12 00 00 00 03 00 01 00 F0 00 01 00 69 00 03 00 F0 00 05 00 C4 00 ); $result = unpack_record($worksheet->_store_mso_client_anchor(3, @data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_anchor. # $range = 'A65534'; $caption = sprintf " \t_store_mso_client_anchor(%s)", $range; @data = $worksheet->_substitute_cellref($range); @data = $worksheet->_comment_params(@data, 'Test'); @data = @{$data[-1]}; $target = join ' ', qw( 00 00 10 F0 12 00 00 00 03 00 01 00 F0 00 F9 FF 3C 00 03 00 F0 00 FD FF 97 00 ); $result = unpack_record($worksheet->_store_mso_client_anchor(3, @data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_anchor. # $range = 'A65535'; $caption = sprintf " \t_store_mso_client_anchor(%s)", $range; @data = $worksheet->_substitute_cellref($range); @data = $worksheet->_comment_params(@data, 'Test'); @data = @{$data[-1]}; $target = join ' ', qw( 00 00 10 F0 12 00 00 00 03 00 01 00 F0 00 FA FF 3C 00 03 00 F0 00 FE FF 97 00 ); $result = unpack_record($worksheet->_store_mso_client_anchor(3, @data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_anchor. # $range = 'A65536'; $caption = sprintf " \t_store_mso_client_anchor(%s)", $range; @data = $worksheet->_substitute_cellref($range); @data = $worksheet->_comment_params(@data, 'Test'); @data = @{$data[-1]}; $target = join ' ', qw( 00 00 10 F0 12 00 00 00 03 00 01 00 F0 00 FB FF 1E 00 03 00 F0 00 FF FF 78 00 ); $result = unpack_record($worksheet->_store_mso_client_anchor(3, @data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_anchor. # $range = 'IT3'; $caption = sprintf " \t_store_mso_client_anchor(%s)", $range; @data = $worksheet->_substitute_cellref($range); @data = $worksheet->_comment_params(@data, 'Test'); @data = @{$data[-1]}; $target = join ' ', qw( 00 00 10 F0 12 00 00 00 03 00 FA 00 10 03 01 00 69 00 FC 00 10 03 05 00 C4 00 ); $result = unpack_record($worksheet->_store_mso_client_anchor(3, @data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_anchor. # $range = 'IU3'; $caption = sprintf " \t_store_mso_client_anchor(%s)", $range; @data = $worksheet->_substitute_cellref($range); @data = $worksheet->_comment_params(@data, 'Test'); @data = @{$data[-1]}; $target = join ' ', qw( 00 00 10 F0 12 00 00 00 03 00 FB 00 10 03 01 00 69 00 FD 00 10 03 05 00 C4 00 ); $result = unpack_record($worksheet->_store_mso_client_anchor(3, @data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_anchor. # $range = 'IV3'; $caption = sprintf " \t_store_mso_client_anchor(%s)", $range; @data = $worksheet->_substitute_cellref($range); @data = $worksheet->_comment_params(@data, 'Test'); @data = @{$data[-1]}; $target = join ' ', qw( 00 00 10 F0 12 00 00 00 03 00 FC 00 10 03 01 00 69 00 FE 00 10 03 05 00 C4 00 ); $result = unpack_record($worksheet->_store_mso_client_anchor(3, @data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_anchor where comment offsets have changed. # $range = 'A3'; $caption = sprintf " \t_store_mso_client_anchor(%s). Cell offsets changed.", $range; @data = $worksheet->_substitute_cellref($range); @data = $worksheet->_comment_params(@data, 'Test', x_offset => 18, y_offset => 9); @data = @{$data[-1]}; $target = join ' ', qw( 00 00 10 F0 12 00 00 00 03 00 01 00 20 01 01 00 88 00 03 00 20 01 05 00 E2 00 ); $result = unpack_record($worksheet->_store_mso_client_anchor(3, @data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_anchor where comment dimensions have changed. # $range = 'A3'; $caption = sprintf " \t_store_mso_client_anchor(%s). Dimensions changed.", $range; @data = $worksheet->_substitute_cellref($range); @data = $worksheet->_comment_params(@data, 'Test', x_scale => 3, y_scale => 2); @data = @{$data[-1]}; $target = join ' ', qw( 00 00 10 F0 12 00 00 00 03 00 01 00 F0 00 01 00 69 00 07 00 F0 00 0A 00 1E 00 ); $result = unpack_record($worksheet->_store_mso_client_anchor(3, @data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_anchor where comment dimensions have changed. # $range = 'A3'; $caption = sprintf " \t_store_mso_client_anchor(%s). Dimensions changed.", $range; @data = $worksheet->_substitute_cellref($range); @data = $worksheet->_comment_params(@data, 'Test', width => 384, height => 148); @data = @{$data[-1]}; $target = join ' ', qw( 00 00 10 F0 12 00 00 00 03 00 01 00 F0 00 01 00 69 00 07 00 F0 00 0A 00 1E 00 ); $result = unpack_record($worksheet->_store_mso_client_anchor(3, @data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_anchor where column widths have changed. # $range = 'F3'; $worksheet->set_column('G:G', 20); $caption = sprintf " \t_store_mso_client_anchor(%s). Col width changed.", $range; @data = $worksheet->_substitute_cellref($range); @data = $worksheet->_comment_params(@data, 'Test'); @data = @{$data[-1]}; $target = join ' ', qw( 00 00 10 F0 12 00 00 00 03 00 06 00 6A 00 01 00 69 00 06 00 F2 03 05 00 C4 00 ); $result = unpack_record($worksheet->_store_mso_client_anchor(3, @data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_anchor where column widths have changed. # $range = 'K3'; $worksheet->set_column('L:O', 4); $caption = sprintf " \t_store_mso_client_anchor(%s). Col width changed.", $range; @data = $worksheet->_substitute_cellref($range); @data = $worksheet->_comment_params(@data, 'Test'); @data = @{$data[-1]}; $target = join ' ', qw( 00 00 10 F0 12 00 00 00 03 00 0B 00 D1 01 01 00 69 00 0F 00 B0 00 05 00 C4 00 ); $result = unpack_record($worksheet->_store_mso_client_anchor(3, @data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_anchor where row height have changed. # $range = 'A6'; $worksheet->set_row(5, 6); $worksheet->set_row(6, 6); $worksheet->set_row(7, 6); $worksheet->set_row(8, 6); $caption = sprintf " \t_store_mso_client_anchor(%s). Row height changed.", $range; @data = $worksheet->_substitute_cellref($range); @data = $worksheet->_comment_params(@data, 'Test'); @data = @{$data[-1]}; $target = join ' ', qw( 00 00 10 F0 12 00 00 00 03 00 01 00 F0 00 04 00 69 00 03 00 F0 00 0A 00 E2 00 ); $result = unpack_record($worksheet->_store_mso_client_anchor(3, @data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_anchor where row height have changed. # $range = 'A15'; $worksheet->set_row(14, 60); $caption = sprintf " \t_store_mso_client_anchor(%s). Row height changed.", $range; @data = $worksheet->_substitute_cellref($range); @data = $worksheet->_comment_params(@data, 'Test'); @data = @{$data[-1]}; $target = join ' ', qw( 00 00 10 F0 12 00 00 00 03 00 01 00 F0 00 0D 00 69 00 03 00 F0 00 0E 00 CD 00 ); $result = unpack_record($worksheet->_store_mso_client_anchor(3, @data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_data. # $caption = sprintf " \t_store_mso_client_data()"; @data = (); $target = join ' ', qw( 00 00 11 F0 00 00 00 00 ); $result = unpack_record($worksheet->_store_mso_client_data(@data)); is($result, $target, $caption); ############################################################################### # # Test for _store_obj_comment. # $caption = sprintf " \t_store_obj_comment()"; @data = (0x01); $target = join ' ', qw( 5D 00 34 00 15 00 12 00 19 00 01 00 11 40 00 00 00 00 00 00 00 00 00 00 00 00 0D 00 16 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ); $result = unpack_record($worksheet->_store_obj_comment(@data)); is($result, $target, $caption); ############################################################################### # # Test for _store_mso_client_text_box. # $caption = sprintf " \t_store_mso_client_text_box()"; @data = (); $target = join ' ', qw( 00 00 0D F0 00 00 00 00 ); $result = unpack_record($worksheet->_store_mso_client_text_box(@data)); is($result, $target, $caption); ############################################################################### # # Unpack the binary data into a format suitable for printing in tests. # sub unpack_record { return join ' ', map {sprintf "%02X", $_} unpack "C*", $_[0]; } # Cleanup $workbook->close(); unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/22_mso_drawing_group.t000644 000765 000024 00000066032 11304413104 022476 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for the internal methods used to write the MSODRAWINGGROUP record. # # reverse(''), September 2005, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 34; ############################################################################### # # Tests setup # my $test_file = "temp_test_file.xls"; my $workbook; my $worksheet1; my $worksheet2; my $worksheet3; my $target; my $result; my $caption; my $count1; my $count2; my $count3; my @target_ids; my @result_ids; ############################################################################### # # Test # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $count1 = 1; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 5A 00 0F 00 00 F0 52 00 00 00 00 00 06 F0 18 00 00 00 02 04 00 00 02 00 00 00 02 00 00 00 01 00 00 00 01 00 00 00 02 00 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments.", $count1; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 2, 1025, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Test # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $count1 = 2; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 5A 00 0F 00 00 F0 52 00 00 00 00 00 06 F0 18 00 00 00 03 04 00 00 02 00 00 00 03 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments.", $count1; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 3, 1026, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Test # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $count1 = 3; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 5A 00 0F 00 00 F0 52 00 00 00 00 00 06 F0 18 00 00 00 04 04 00 00 02 00 00 00 04 00 00 00 01 00 00 00 01 00 00 00 04 00 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments.", $count1; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 4, 1027, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Test # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $count1 = 1023; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 5A 00 0F 00 00 F0 52 00 00 00 00 00 06 F0 18 00 00 00 00 08 00 00 02 00 00 00 00 04 00 00 01 00 00 00 01 00 00 00 00 04 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments.", $count1; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 1024, 2047, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Test # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $count1 = 1024; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 62 00 0F 00 00 F0 5A 00 00 00 00 00 06 F0 20 00 00 00 01 08 00 00 03 00 00 00 01 04 00 00 01 00 00 00 01 00 00 00 00 04 00 00 01 00 00 00 01 00 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments.", $count1; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 1025, 2048, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Test # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $count1 = 2048; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 6A 00 0F 00 00 F0 62 00 00 00 00 00 06 F0 28 00 00 00 01 0C 00 00 04 00 00 00 01 08 00 00 01 00 00 00 01 00 00 00 00 04 00 00 01 00 00 00 00 04 00 00 01 00 00 00 01 00 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments.", $count1; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 2049, 3072, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Test # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); $count1 = 1; $count2 = 1; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $worksheet2->write_comment($_ -1, 0, 'aaa') for 1 .. $count2; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 62 00 0F 00 00 F0 5A 00 00 00 00 00 06 F0 20 00 00 00 02 08 00 00 03 00 00 00 04 00 00 00 02 00 00 00 01 00 00 00 02 00 00 00 02 00 00 00 02 00 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments, Sheet2: %4d comments.", $count1, $count2; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 2, 1025, 2048, 2, 2, 2049, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Test # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); $count1 = 2; $count2 = 2; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $worksheet2->write_comment($_ -1, 0, 'aaa') for 1 .. $count2; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 62 00 0F 00 00 F0 5A 00 00 00 00 00 06 F0 20 00 00 00 03 08 00 00 03 00 00 00 06 00 00 00 02 00 00 00 01 00 00 00 03 00 00 00 02 00 00 00 03 00 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments, Sheet2: %4d comments.", $count1, $count2; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 3, 1026, 2048, 2, 3, 2050, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Test # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); $count1 = 1023; $count2 = 1; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $worksheet2->write_comment($_ -1, 0, 'aaa') for 1 .. $count2; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 62 00 0F 00 00 F0 5A 00 00 00 00 00 06 F0 20 00 00 00 02 08 00 00 03 00 00 00 02 04 00 00 02 00 00 00 01 00 00 00 00 04 00 00 02 00 00 00 02 00 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments, Sheet2: %4d comments.", $count1, $count2; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 1024, 2047, 2048, 2, 2, 2049, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Test # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); $count1 = 1023; $count2 = 1023; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $worksheet2->write_comment($_ -1, 0, 'aaa') for 1 .. $count2; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 62 00 0F 00 00 F0 5A 00 00 00 00 00 06 F0 20 00 00 00 00 0C 00 00 03 00 00 00 00 08 00 00 02 00 00 00 01 00 00 00 00 04 00 00 02 00 00 00 00 04 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments, Sheet2: %4d comments.", $count1, $count2; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 1024, 2047, 2048, 2, 1024, 3071, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Test # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); $count1 = 1024; $count2 = 1024; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $worksheet2->write_comment($_ -1, 0, 'aaa') for 1 .. $count2; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 72 00 0F 00 00 F0 6A 00 00 00 00 00 06 F0 30 00 00 00 01 10 00 00 05 00 00 00 02 08 00 00 02 00 00 00 01 00 00 00 00 04 00 00 01 00 00 00 01 00 00 00 02 00 00 00 00 04 00 00 02 00 00 00 01 00 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments, Sheet2: %4d comments.", $count1, $count2; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 1025, 2048, 3072, 2, 1025, 4096, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Test # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); $count1 = 1024; $count2 = 1; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $worksheet2->write_comment($_ -1, 0, 'aaa') for 1 .. $count2; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 6A 00 0F 00 00 F0 62 00 00 00 00 00 06 F0 28 00 00 00 02 0C 00 00 04 00 00 00 03 04 00 00 02 00 00 00 01 00 00 00 00 04 00 00 01 00 00 00 01 00 00 00 02 00 00 00 02 00 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments, Sheet2: %4d comments.", $count1, $count2; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 1025, 2048, 3072, 2, 2, 3073, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Test # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); $worksheet3 = $workbook->add_worksheet(); $count1 = 1023; $count2 = 1; $count3 = 1023; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $worksheet2->write_comment($_ -1, 0, 'aaa') for 1 .. $count2; $worksheet3->write_comment($_ -1, 0, 'aaa') for 1 .. $count3; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 6A 00 0F 00 00 F0 62 00 00 00 00 00 06 F0 28 00 00 00 00 10 00 00 04 00 00 00 02 08 00 00 03 00 00 00 01 00 00 00 00 04 00 00 02 00 00 00 02 00 00 00 03 00 00 00 00 04 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments, Sheet2: %4d comments, " . "Sheet3: %4d comments.", $count1, $count2, $count3; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 1024, 2047, 2048, 2, 2, 2049, 3072, 3, 1024, 4095, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Test # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); $worksheet3 = $workbook->add_worksheet(); $count1 = 1023; $count2 = 1023; $count3 = 1; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $worksheet2->write_comment($_ -1, 0, 'aaa') for 1 .. $count2; $worksheet3->write_comment($_ -1, 0, 'aaa') for 1 .. $count3; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 6A 00 0F 00 00 F0 62 00 00 00 00 00 06 F0 28 00 00 00 02 0C 00 00 04 00 00 00 02 08 00 00 03 00 00 00 01 00 00 00 00 04 00 00 02 00 00 00 00 04 00 00 03 00 00 00 02 00 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments, Sheet2: %4d comments, " . "Sheet3: %4d comments.", $count1, $count2, $count3; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 1024, 2047, 2048, 2, 1024, 3071, 3072, 3, 2, 3073, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Test # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); $worksheet3 = $workbook->add_worksheet(); $count1 = 1024; $count2 = 1; $count3 = 1024; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $worksheet2->write_comment($_ -1, 0, 'aaa') for 1 .. $count2; $worksheet3->write_comment($_ -1, 0, 'aaa') for 1 .. $count3; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 7A 00 0F 00 00 F0 72 00 00 00 00 00 06 F0 38 00 00 00 01 14 00 00 06 00 00 00 04 08 00 00 03 00 00 00 01 00 00 00 00 04 00 00 01 00 00 00 01 00 00 00 02 00 00 00 02 00 00 00 03 00 00 00 00 04 00 00 03 00 00 00 01 00 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments, Sheet2: %4d comments, " . "Sheet3: %4d comments.", $count1, $count2, $count3; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 1025, 2048, 3072, 2, 2, 3073, 4096, 3, 1025, 5120, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Test # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); $worksheet3 = $workbook->add_worksheet(); $count1 = 1024; $count2 = 1024; $count3 = 1; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $worksheet2->write_comment($_ -1, 0, 'aaa') for 1 .. $count2; $worksheet3->write_comment($_ -1, 0, 'aaa') for 1 .. $count3; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 7A 00 0F 00 00 F0 72 00 00 00 00 00 06 F0 38 00 00 00 02 14 00 00 06 00 00 00 04 08 00 00 03 00 00 00 01 00 00 00 00 04 00 00 01 00 00 00 01 00 00 00 02 00 00 00 00 04 00 00 02 00 00 00 01 00 00 00 03 00 00 00 02 00 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments, Sheet2: %4d comments, " . "Sheet3: %4d comments.", $count1, $count2, $count3; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 1025, 2048, 3072, 2, 1025, 4096, 5120, 3, 2, 5121, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Test. Same as previous except also tests that duplicates are ignored. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); $worksheet3 = $workbook->add_worksheet(); $count1 = 1024; $count2 = 1024; $count3 = 1; $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $worksheet2->write_comment($_ -1, 0, 'aaa') for 1 .. $count2; $worksheet3->write_comment($_ -1, 0, 'aaa') for 1 .. $count3; # Duplicates. $worksheet1->write_comment($_ -1, 0, 'aaa') for 1 .. $count1; $worksheet2->write_comment($_ -1, 0, 'aaa') for 1 .. $count2; $worksheet3->write_comment($_ -1, 0, 'aaa') for 1 .. $count3; $workbook->_calc_mso_sizes(); $target = join " ", qw( EB 00 7A 00 0F 00 00 F0 72 00 00 00 00 00 06 F0 38 00 00 00 02 14 00 00 06 00 00 00 04 08 00 00 03 00 00 00 01 00 00 00 00 04 00 00 01 00 00 00 01 00 00 00 02 00 00 00 00 04 00 00 02 00 00 00 01 00 00 00 03 00 00 00 02 00 00 00 33 00 0B F0 12 00 00 00 BF 00 08 00 08 00 81 01 09 00 00 08 C0 01 40 00 00 08 40 00 1E F1 10 00 00 00 0D 00 00 08 0C 00 00 08 17 00 00 08 F7 00 00 10 ); $caption = sprintf " \tSheet1: %4d comments, Sheet2: %4d comments, " . "Sheet3: %4d comments.", $count1, $count2, $count3; $result = unpack_record($workbook->_add_mso_drawing_group()); is($result, $target, $caption); # Test the parameters pass to the worksheets $caption .= ' (params)'; @result_ids = (); @target_ids = ( 1024, 1, 1025, 2048, 3072, 2, 1025, 4096, 5120, 3, 2, 5121, ); for my $sheet ($workbook->sheets()) { push @result_ids, @{$sheet->{_object_ids}}; } is_deeply(\@result_ids, \@target_ids , $caption); $workbook->close(); ############################################################################### # # Unpack the binary data into a format suitable for printing in tests. # sub unpack_record { return join ' ', map {sprintf "%02X", $_} unpack "C*", $_[0]; } # Cleanup unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/23_note.t000644 000765 000024 00000014441 11304413104 017714 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for some of the internal method used to write the NOTE record that # is used in cell comments. # # reverse(''), September 2005, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 5; ############################################################################### # # Tests setup # my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); my $target; my $result; my $caption; my $row; my $col; my $obj_id; my $visible; my $author; my $encoding; my @data; ############################################################################### # # Test 1 NOTE. Blank author name. # @data = $worksheet->_comment_params(2, 0, 'Test'); $row = $data[0]; $col = $data[1]; $author = $data[4]; $encoding = $data[5]; $visible = $data[6]; $obj_id = 1; $caption = " \t_store_note()"; $target = join " ", qw( 1C 00 0C 00 02 00 00 00 00 00 01 00 00 00 00 00 ); $result = unpack_record($worksheet->_store_note($row, $col, $obj_id, $author, $encoding, $visible, )); is($result, $target, $caption); ############################################################################### # # Test 2 NOTE. Defined author name # @data = $worksheet->_comment_params(2, 0, 'Test', author => 'Username'); $row = $data[0]; $col = $data[1]; $author = $data[4]; $encoding = $data[5]; $visible = $data[6]; $obj_id = 1; $caption = " \t_store_note()"; $target = join " ", qw( 1C 00 14 00 02 00 00 00 00 00 01 00 08 00 00 55 73 65 72 6E 61 6D 65 00 ); $result = unpack_record($worksheet->_store_note($row, $col, $obj_id, $author, $encoding, $visible, )); is($result, $target, $caption); ############################################################################### # # Test 3 NOTE. Visible note. # @data = $worksheet->_comment_params(4, 2, 'Test', author => 'Username', visible => 1 ); $row = $data[0]; $col = $data[1]; $author = $data[4]; $encoding = $data[5]; $visible = $data[6]; $obj_id = 1; $caption = " \t_store_note()"; $target = join " ", qw( 1C 00 14 00 04 00 02 00 02 00 01 00 08 00 00 55 73 65 72 6E 61 6D 65 00 ); $result = unpack_record($worksheet->_store_note($row, $col, $obj_id, $author, $encoding, $visible, )); is($result, $target, $caption); ############################################################################### # # Test 3 NOTE. UTF16 author name. # $author = pack "n", 0x20Ac; # Euro symbol @data = $worksheet->_comment_params(4, 2, 'Test', author =>$author, author_encoding => 1 ); $row = $data[0]; $col = $data[1]; $author = $data[4]; $encoding = $data[5]; $visible = $data[6]; $obj_id = 1; $caption = " \t_store_note()"; $target = join " ", qw( 1C 00 0E 00 04 00 02 00 00 00 01 00 01 00 01 AC 20 00 ); $result = unpack_record($worksheet->_store_note($row, $col, $obj_id, $author, $encoding, $visible, )); is($result, $target, $caption); ############################################################################### # # Test 4 NOTE. UTF8 author name. Perl 5.8 only. # SKIP: { skip " \t_store_note() skipped test requires Perl 5.8 Unicode support", 1 if $] < 5.008; $author = chr 0x20Ac; # Euro symbol @data = $worksheet->_comment_params(4, 2, 'Test', author =>$author); $row = $data[0]; $col = $data[1]; $author = $data[4]; $encoding = $data[5]; $visible = $data[6]; $obj_id = 1; $caption = " \t_store_note()"; $target = join " ", qw( 1C 00 0E 00 04 00 02 00 00 00 01 00 01 00 01 AC 20 00 ); $result = unpack_record($worksheet->_store_note($row, $col, $obj_id, $author, $encoding, $visible, )); is($result, $target, $caption); } ############################################################################### # # Unpack the binary data into a format suitable for printing in tests. # sub unpack_record { return join ' ', map {sprintf "%02X", $_} unpack "C*", $_[0]; } # Cleanup $workbook->close(); unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/24_txo.t000644 000765 000024 00000004652 11304413104 017565 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for some of the internal method used to write the NOTE record that # is used in cell comments. # # reverse(''), September 2005, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 3; ############################################################################### # # Tests setup # my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); my $target; my $result; my $caption; my $string; my $formats; ############################################################################### # # Test 1 TXO. # $string = 'aaa'; $caption = " \t_store_txo()"; $target = join " ", qw( B6 01 12 00 12 02 00 00 00 00 00 00 00 00 03 00 10 00 00 00 00 00 ); $result = unpack_record($worksheet->_store_txo(length $string)); is($result, $target, $caption); ############################################################################### # # Test 2 First CONTINUE record after TXO. # $string = 'aaa'; $caption = " \t_store_txo_continue_1()"; $target = join " ", qw( 3C 00 04 00 00 61 61 61 ); $result = unpack_record($worksheet->_store_txo_continue_1($string)); is($result, $target, $caption); ############################################################################### # # Test 3 Second CONTINUE record after TXO. # $string = 'aaa'; $caption = " \t_store_txo_continue_2()"; $target = join " ", qw( 3C 00 10 00 00 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00 ); $formats = [ [0, 0], [length($string), 0], ]; $result = unpack_record($worksheet->_store_txo_continue_2($formats)); is($result, $target, $caption); ############################################################################### # # Unpack the binary data into a format suitable for printing in tests. # sub unpack_record { return join ' ', map {sprintf "%02X", $_} unpack "C*", $_[0]; } # Cleanup $workbook->close(); unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/25_position_object.t000644 000765 000024 00000007533 11304413104 022147 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for the _position_object() Worksheet method used to calculate the # vertices that define the position of a graphical object within a worksheet. # # See the the _position_object() comments for a full explanation. # # reverse(''), September 2005, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 27; ############################################################################### # # Tests setup # my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); ############################################################################### # # Tests extracted from images imported into Excel. # # # input = ($col_start, $row_start, $x1, $y1, $width, $height) # (0, 1, 2, 3, 4, 5 ) # # expected = ($col_start, $x1, $row_start, $y1, $col_end, $x2, $row_end, $y2) # (0, 1, 2, 3, 4, 5, 6, 7 ) # my @tests = ( # Input # Expected results [ [0, 0, 0, 0, 1, 1], [ 0, 0, 0, 0, 0, 16, 0, 15] ], [ [0, 0, 0, 0, 2, 2], [ 0, 0, 0, 0, 0, 32, 0, 30] ], [ [0, 0, 0, 0, 3, 3], [ 0, 0, 0, 0, 0, 48, 0, 45] ], [ [0, 0, 0, 0, 4, 4], [ 0, 0, 0, 0, 0, 64, 0, 60] ], [ [0, 0, 0, 0, 5, 5], [ 0, 0, 0, 0, 0, 80, 0, 75] ], [ [0, 0, 0, 0, 6, 6], [ 0, 0, 0, 0, 0, 96, 0, 90] ], [ [0, 0, 0, 0, 7, 7], [ 0, 0, 0, 0, 0, 112, 0, 105] ], [ [0, 0, 0, 0, 8, 8], [ 0, 0, 0, 0, 0, 128, 0, 120] ], [ [0, 0, 0, 0, 9, 9], [ 0, 0, 0, 0, 0, 144, 0, 136] ], [ [0, 0, 0, 0, 10, 10], [ 0, 0, 0, 0, 0, 160, 0, 151] ], [ [0, 0, 0, 0, 15, 15], [ 0, 0, 0, 0, 0, 240, 0, 226] ], [ [0, 0, 0, 0, 16, 16], [ 0, 0, 0, 0, 0, 256, 0, 241] ], [ [0, 0, 0, 0, 17, 17], [ 0, 0, 0, 0, 0, 272, 1, 0] ], [ [0, 0, 0, 0, 18, 18], [ 0, 0, 0, 0, 0, 288, 1, 15] ], [ [0, 0, 0, 0, 19, 19], [ 0, 0, 0, 0, 0, 304, 1, 30] ], [ [0, 0, 0, 0, 62, 8], [ 0, 0, 0, 0, 0, 992, 0, 120] ], [ [0, 0, 0, 0, 63, 8], [ 0, 0, 0, 0, 0, 1008, 0, 120] ], [ [0, 0, 0, 0, 64, 8], [ 0, 0, 0, 0, 1, 0, 0, 120] ], [ [0, 0, 0, 0, 65, 8], [ 0, 0, 0, 0, 1, 16, 0, 120] ], [ [0, 0, 0, 0, 66, 8], [ 0, 0, 0, 0, 1, 32, 0, 120] ], [ [0, 0, 0, 0, 200, 200], [ 0, 0, 0, 0, 3, 128, 11, 196] ], [ [1, 4, 0, 0, 64, 16], [ 1, 0, 4, 0, 2, 0, 4, 241] ], [ [1, 4, 1, 0, 64, 16], [ 1, 16, 4, 0, 2, 16, 4, 241] ], [ [1, 4, 2, 0, 64, 16], [ 1, 32, 4, 0, 2, 32, 4, 241] ], [ [1, 4, 2, 1, 64, 16], [ 1, 32, 4, 15, 2, 32, 5, 0] ], [ [1, 4, 2, 2, 64, 16], [ 1, 32, 4, 30, 2, 32, 5, 15] ], # Test for comment box standard sizes. [ [2, 1, 15, 7, 128, 74], [ 2, 240, 1, 105, 4, 240, 5, 196] ], ); for my $testcase (@tests) { my $input = $testcase->[0]; my $expected = $testcase->[1]; my @results = $worksheet->_position_object(@$input); my $caption = sprintf " \t_position_object %3dw x h%-3d Offset = (%2d, %d) Cell = (%d, %d)", $input->[4], $input->[5], $input->[2], $input->[3], $input->[0], $input->[1]; is_deeply(\@results, $expected, $caption); } ############################################################################### # # Cleanup # $workbook->close(); unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/26_autofilter.t000644 000765 000024 00000024667 11304413104 021143 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for the internal methods used to write the AUTOFILTER record. # # reverse(''), August 2007, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 29; ############################################################################### # # Tests setup # my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); ############################################################################### # # Test cases. # my @tests = ( { 'column' => 0, 'expression' => 'x = Blanks', 'data' => [qw( 9E 00 18 00 00 00 84 32 0C 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 )], }, { 'column' => 1, 'expression' => 'x = Nonblanks', 'data' => [qw( 9E 00 18 00 01 00 84 32 0E 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 )], }, { 'column' => 2, 'expression' => 'x > 1.001', 'data' => [qw( 9E 00 18 00 02 00 80 32 04 04 6A BC 74 93 18 04 F0 3F 00 00 00 00 00 00 00 00 00 00 )], }, { 'column' => 3, 'expression' => 'x >= 1.001', 'data' => [qw( 9E 00 18 00 03 00 80 32 04 06 6A BC 74 93 18 04 F0 3F 00 00 00 00 00 00 00 00 00 00 )], }, { 'column' => 4, 'expression' => 'x < 1.001', 'data' => [qw( 9E 00 18 00 04 00 80 32 04 01 6A BC 74 93 18 04 F0 3F 00 00 00 00 00 00 00 00 00 00 )], }, { 'column' => 5, 'expression' => 'x <= 1.001', 'data' => [qw( 9E 00 18 00 05 00 80 32 04 03 6A BC 74 93 18 04 F0 3F 00 00 00 00 00 00 00 00 00 00 )], }, { 'column' => 6, 'expression' => 'x > 1.001 and x <= 5.001', 'data' => [qw( 9E 00 18 00 06 00 80 32 04 04 6A BC 74 93 18 04 F0 3F 04 03 1B 2F DD 24 06 01 14 40 )], }, { 'column' => 7, 'expression' => 'x > 1.001 or x <= 5.001', 'data' => [qw( 9E 00 18 00 07 00 81 32 04 04 6A BC 74 93 18 04 F0 3F 04 03 1B 2F DD 24 06 01 14 40 )], }, { 'column' => 8, 'expression' => 'x <> 2.001', 'data' => [qw( 9E 00 18 00 08 00 80 32 04 05 35 5E BA 49 0C 02 00 40 00 00 00 00 00 00 00 00 00 00 )], }, { 'column' => 9, 'expression' => 'x = 1.001', 'data' => [qw( 9E 00 1E 00 09 00 84 32 06 02 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 31 2E 30 30 31 )], }, { 'column' => 10, 'expression' => 'x = West', 'data' => [qw( 9E 00 1D 00 0A 00 84 32 06 02 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 57 65 73 74 )], }, { 'column' => 11, 'expression' => 'x = East', 'data' => [qw( 9E 00 1D 00 0B 00 84 32 06 02 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 45 61 73 74 )], }, { 'column' => 12, 'expression' => 'x <> West', 'data' => [qw( 9E 00 1D 00 0C 00 80 32 06 05 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 57 65 73 74 )], }, { 'column' => 13, 'expression' => 'x =~ b*', 'data' => [qw( 9E 00 1B 00 0D 00 80 32 06 02 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 62 2A )], }, { 'column' => 14, 'expression' => 'x !~ b*', 'data' => [qw( 9E 00 1B 00 0E 00 80 32 06 05 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 62 2A )], }, { 'column' => 15, 'expression' => 'x =~ *b', 'data' => [qw( 9E 00 1B 00 0F 00 80 32 06 02 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2A 62 )], }, { 'column' => 16, 'expression' => 'x !~ *b', 'data' => [qw( 9E 00 1B 00 10 00 80 32 06 05 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2A 62 )], }, { 'column' => 17, 'expression' => 'x =~ *b*', 'data' => [qw( 9E 00 1C 00 11 00 80 32 06 02 00 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2A 62 2A )], }, { 'column' => 18, 'expression' => 'x !~ *b*', 'data' => [qw( 9E 00 1C 00 12 00 80 32 06 05 00 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2A 62 2A )], }, { 'column' => 19, 'expression' => 'x = fo?', 'data' => [qw( 9E 00 1C 00 13 00 80 32 06 02 00 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 66 6F 3F )], }, { 'column' => 20, 'expression' => 'x = fo~?', 'data' => [qw( 9E 00 1D 00 14 00 80 32 06 02 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 66 6F 7E 3F )], }, { 'column' => 21, 'expression' => 'x = East and x = West', 'data' => [qw( 9E 00 22 00 15 00 8C 32 06 02 00 00 00 00 04 00 00 00 06 02 00 00 00 00 04 00 00 00 00 45 61 73 74 00 57 65 73 74 )], }, { 'column' => 22, 'expression' => 'top 10 items', 'data' => [qw( 9E 00 18 00 16 00 30 05 04 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 )], }, { 'column' => 23, 'expression' => 'top 10 %', 'data' => [qw( 9E 00 18 00 17 00 70 05 04 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 )], }, { 'column' => 24, 'expression' => 'bottom 10 items', 'data' => [qw( 9E 00 18 00 18 00 10 05 04 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 )], }, { 'column' => 25, 'expression' => 'bottom 10 %', 'data' => [qw( 9E 00 18 00 19 00 50 05 04 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 )], }, { 'column' => 26, 'expression' => 'top 5 items', 'data' => [qw( 9E 00 18 00 1A 00 B0 02 04 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 )], }, { 'column' => 27, 'expression' => 'top 100 items', 'data' => [qw( 9E 00 18 00 1B 00 30 32 04 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 )], }, { 'column' => 28, 'expression' => 'top 101 items', 'data' => [qw( 9E 00 18 00 1C 00 B0 32 04 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 )], }, ); ############################################################################### # # Run tests. # for my $test (@tests) { my $column = $test->{column}; my $expression = $test->{expression}; my @tokens = $worksheet->_extract_filter_tokens($expression); @tokens = $worksheet->_parse_filter_expression($expression, @tokens); my $result = $worksheet->_store_autofilter($column , @tokens); my $target = join " ", @{$test->{data}}; my $caption = " \tfilter_column($column, '$expression')"; $result = unpack_record($result); is($result, $target, $caption); } # # Helper functions. # ############################################################################### # # Unpack the binary data into a format suitable for printing in tests. # sub unpack_record { return join ' ', map {sprintf "%02X", $_} unpack "C*", $_[0]; } # Cleanup $workbook->close(); unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/27_autofilter.t000644 000765 000024 00000004624 11304413104 021133 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for the token extraction method used to parse autofilter expressions. # # reverse(''), August 2007, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 18; ############################################################################### # # Tests setup # my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); ############################################################################### # # Test cases structured as [$input, [@expected_output]] # my @tests = ( [ undef, [], ], [ '', [], ], [ '0 < 2000', [0, '<', 2000], ], [ 'x < 2000', ['x', '<', 2000], ], [ 'x > 2000', ['x', '>', 2000], ], [ 'x == 2000', ['x', '==', 2000], ], [ 'x > 2000 and x < 5000', ['x', '>', 2000, 'and', 'x', '<', 5000], ], [ 'x = "foo"', ['x', '=', 'foo'], ], [ 'x = foo', ['x', '=', 'foo'], ], [ 'x = "foo bar"', ['x', '=', 'foo bar'], ], [ 'x = "foo "" bar"', ['x', '=', 'foo " bar'], ], [ 'x = "foo bar" or x = "bar foo"', ['x', '=', 'foo bar', 'or', 'x', '=', 'bar foo'], ], [ 'x = "foo "" bar" or x = "bar "" foo"', ['x', '=', 'foo " bar', 'or', 'x', '=', 'bar " foo'], ], [ 'x = """"""""', ['x', '=', '"""'], ], [ 'x = Blanks', ['x', '=', 'Blanks'], ], [ 'x = NonBlanks', ['x', '=', 'NonBlanks'], ], [ 'top 10 %', ['top', 10, '%'], ], [ 'top 10 items', ['top', 10, 'items'], ], ); ############################################################################### # # Run the test cases. # for my $aref (@tests) { my $expression = $aref->[0]; my $expected = $aref->[1]; my @results = $worksheet->_extract_filter_tokens($expression); my $testname = $expression || 'none'; is_deeply(\@results, $expected, " \t" . $testname); } # Cleanup $workbook->close(); unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/28_autofilter.t000644 000765 000024 00000005231 11304413104 021127 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for the token parsing methods used to parse autofilter expressions. # # reverse(''), August 2007, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 24; ############################################################################### # # Tests setup # my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); ############################################################################### # # Test cases structured as [$input, [@expected_output]] # my @tests = ( [ 'x = 2000', [2, 2000], ], [ 'x == 2000', [2, 2000], ], [ 'x =~ 2000', [2, 2000], ], [ 'x eq 2000', [2, 2000], ], [ 'x <> 2000', [5, 2000], ], [ 'x != 2000', [5, 2000], ], [ 'x ne 2000', [5, 2000], ], [ 'x !~ 2000', [5, 2000], ], [ 'x > 2000', [4, 2000], ], [ 'x < 2000', [1, 2000], ], [ 'x >= 2000', [6, 2000], ], [ 'x <= 2000', [3, 2000], ], [ 'x > 2000 and x < 5000', [4, 2000, 0, 1, 5000], ], [ 'x > 2000 && x < 5000', [4, 2000, 0, 1, 5000], ], [ 'x > 2000 or x < 5000', [4, 2000, 1, 1, 5000], ], [ 'x > 2000 || x < 5000', [4, 2000, 1, 1, 5000], ], [ 'x = Blanks', [2, 'blanks'], ], [ 'x = NonBlanks', [2, 'nonblanks'], ], [ 'x <> Blanks', [2, 'nonblanks'], ], [ 'x <> NonBlanks', [2, 'blanks'], ], [ 'Top 10 Items', [30, 10], ], [ 'Top 20 %', [31, 20], ], [ 'Bottom 5 Items', [32, 5], ], [ 'Bottom 101 %', [33, 101], ], ); ############################################################################### # # Run the test cases. # for my $aref (@tests) { my $expression = $aref->[0]; my $expected = $aref->[1]; my @tokens = $worksheet->_extract_filter_tokens($expression); my @results = $worksheet->_parse_filter_expression($expression, @tokens); my $testname = $expression || 'none'; is_deeply(\@results, $expected, " \t" . $testname); } # Cleanup $workbook->close(); unlink $test_file;Spreadsheet-WriteExcel-2.40/t/29_process_jpg.t000644 000765 000024 00000100631 11304413104 021270 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for the JPEG width and height processing. # # reverse(''), March 2008, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 5; # Test setup. my @expected; my @results; my $testname; my $type = 5; # Excel Blip type (MSOBLIPTYPE). my $image; ############################################################################### # # Test 1. Valid JPEG image. # $testname = '3w x 5h jpeg image.'; $image = pack 'H*', join '', qw ( FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 60 00 60 00 00 FF DB 00 43 00 06 04 05 06 05 04 06 06 05 06 07 07 06 08 0A 10 0A 0A 09 09 0A 14 0E 0F 0C 10 17 14 18 18 17 14 16 16 1A 1D 25 1F 1A 1B 23 1C 16 16 20 2C 20 23 26 27 29 2A 29 19 1F 2D 30 2D 28 30 25 28 29 28 FF DB 00 43 01 07 07 07 0A 08 0A 13 0A 0A 13 28 1A 16 1A 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 FF C0 00 11 08 00 05 00 03 03 01 22 00 02 11 01 03 11 01 FF C4 00 15 00 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 FF C4 00 14 10 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF C4 00 15 01 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 08 FF C4 00 14 11 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF DA 00 0C 03 01 00 02 11 03 11 00 3F 00 9D 00 1C A4 5F FF D9 ); @expected = ($type, 3, 5); @results = Spreadsheet::WriteExcel::Workbook::_process_jpg(1, $image, 'test.jpg'); is_deeply(\@results, \@expected, " \t" . $testname); ############################################################################### # # Test 2. Valid JPEG image. # $testname = '5w x 3h jpeg image.'; $image = pack 'H*', join '', qw ( FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 60 00 60 00 00 FF DB 00 43 00 06 04 05 06 05 04 06 06 05 06 07 07 06 08 0A 10 0A 0A 09 09 0A 14 0E 0F 0C 10 17 14 18 18 17 14 16 16 1A 1D 25 1F 1A 1B 23 1C 16 16 20 2C 20 23 26 27 29 2A 29 19 1F 2D 30 2D 28 30 25 28 29 28 FF DB 00 43 01 07 07 07 0A 08 0A 13 0A 0A 13 28 1A 16 1A 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 FF C0 00 11 08 00 03 00 05 03 01 22 00 02 11 01 03 11 01 FF C4 00 15 00 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 FF C4 00 14 10 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF C4 00 15 01 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 08 FF C4 00 14 11 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF DA 00 0C 03 01 00 02 11 03 11 00 3F 00 9D 00 1C A4 5F FF D9 ); @expected = ($type, 5, 3); @results = Spreadsheet::WriteExcel::Workbook::_process_jpg(1, $image, 'test.jpg'); is_deeply(\@results, \@expected, " \t" . $testname); ############################################################################### # # Test 3. Invalid JPEG image. (FFCO marker missing) # $testname = 'FFCO marker missing in image.'; $image = pack 'H*', join '', qw ( FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 60 00 60 00 00 FF DB 00 43 00 06 04 05 06 05 04 06 06 05 06 07 07 06 08 0A 10 0A 0A 09 09 0A 14 0E 0F 0C 10 17 14 18 18 17 14 16 16 1A 1D 25 1F 1A 1B 23 1C 16 16 20 2C 20 23 26 27 29 2A 29 19 1F 2D 30 2D 28 30 25 28 29 28 FF DB 00 43 01 07 07 07 0A 08 0A 13 0A 0A 13 28 1A 16 1A 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 FF C1 00 11 08 00 03 00 05 03 01 22 00 02 11 01 03 11 01 FF C4 00 15 00 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 FF C4 00 14 10 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF C4 00 15 01 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 08 FF C4 00 14 11 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF DA 00 0C 03 01 00 02 11 03 11 00 3F 00 9D 00 1C A4 5F FF D9 ); eval { @results = Spreadsheet::WriteExcel::Workbook::_process_jpg(1, $image, 'test.jpg'); }; ok($@, " \t$testname"); ############################################################################### # # Test 4. Invalid JPEG image. # $testname = 'empty image.'; $image = ''; eval { @results = Spreadsheet::WriteExcel::Workbook::_process_jpg(1, $image, 'test.jpg'); }; ok($@, " \t$testname"); ############################################################################### # # Test 5. Progressive DCT-based JPEG image. # $testname = '35w x 35h progressive jpeg image.'; $image = pack 'H*', join '', qw ( FF D8 FF E0 00 10 4A 46 49 46 00 01 02 01 00 96 00 96 00 00 FF E1 04 E7 45 78 69 66 00 00 4D 4D 00 2A 00 00 00 08 00 07 01 12 00 03 00 00 00 01 00 01 00 00 01 1A 00 05 00 00 00 01 00 00 00 62 01 1B 00 05 00 00 00 01 00 00 00 6A 01 28 00 03 00 00 00 01 00 02 00 00 01 31 00 02 00 00 00 14 00 00 00 72 01 32 00 02 00 00 00 14 00 00 00 86 87 69 00 04 00 00 00 01 00 00 00 9C 00 00 00 C8 00 00 00 96 00 00 00 01 00 00 00 96 00 00 00 01 41 64 6F 62 65 20 50 68 6F 74 6F 73 68 6F 70 20 37 2E 30 00 32 30 30 38 3A 30 39 3A 30 39 20 32 32 3A 32 33 3A 31 32 00 00 00 00 03 A0 01 00 03 00 00 00 01 FF FF 00 00 A0 02 00 04 00 00 00 01 00 00 00 23 A0 03 00 04 00 00 00 01 00 00 00 23 00 00 00 00 00 00 00 06 01 03 00 03 00 00 00 01 00 06 00 00 01 1A 00 05 00 00 00 01 00 00 01 16 01 1B 00 05 00 00 00 01 00 00 01 1E 01 28 00 03 00 00 00 01 00 02 00 00 02 01 00 04 00 00 00 01 00 00 01 26 02 02 00 04 00 00 00 01 00 00 03 B9 00 00 00 00 00 00 00 48 00 00 00 01 00 00 00 48 00 00 00 01 FF D8 FF E0 00 10 4A 46 49 46 00 01 02 01 00 48 00 48 00 00 FF ED 00 0C 41 64 6F 62 65 5F 43 4D 00 02 FF EE 00 0E 41 64 6F 62 65 00 64 80 00 00 00 01 FF DB 00 84 00 0C 08 08 08 09 08 0C 09 09 0C 11 0B 0A 0B 11 15 0F 0C 0C 0F 15 18 13 13 15 13 13 18 11 0C 0C 0C 0C 0C 0C 11 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 01 0D 0B 0B 0D 0E 0D 10 0E 0E 10 14 0E 0E 0E 14 14 0E 0E 0E 0E 14 11 0C 0C 0C 0C 0C 11 11 0C 0C 0C 0C 0C 0C 11 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C FF C0 00 11 08 00 23 00 23 03 01 22 00 02 11 01 03 11 01 FF DD 00 04 00 03 FF C4 01 3F 00 00 01 05 01 01 01 01 01 01 00 00 00 00 00 00 00 03 00 01 02 04 05 06 07 08 09 0A 0B 01 00 01 05 01 01 01 01 01 01 00 00 00 00 00 00 00 01 00 02 03 04 05 06 07 08 09 0A 0B 10 00 01 04 01 03 02 04 02 05 07 06 08 05 03 0C 33 01 00 02 11 03 04 21 12 31 05 41 51 61 13 22 71 81 32 06 14 91 A1 B1 42 23 24 15 52 C1 62 33 34 72 82 D1 43 07 25 92 53 F0 E1 F1 63 73 35 16 A2 B2 83 26 44 93 54 64 45 C2 A3 74 36 17 D2 55 E2 65 F2 B3 84 C3 D3 75 E3 F3 46 27 94 A4 85 B4 95 C4 D4 E4 F4 A5 B5 C5 D5 E5 F5 56 66 76 86 96 A6 B6 C6 D6 E6 F6 37 47 57 67 77 87 97 A7 B7 C7 D7 E7 F7 11 00 02 02 01 02 04 04 03 04 05 06 07 07 06 05 35 01 00 02 11 03 21 31 12 04 41 51 61 71 22 13 05 32 81 91 14 A1 B1 42 23 C1 52 D1 F0 33 24 62 E1 72 82 92 43 53 15 63 73 34 F1 25 06 16 A2 B2 83 07 26 35 C2 D2 44 93 54 A3 17 64 45 55 36 74 65 E2 F2 B3 84 C3 D3 75 E3 F3 46 94 A4 85 B4 95 C4 D4 E4 F4 A5 B5 C5 D5 E5 F5 56 66 76 86 96 A6 B6 C6 D6 E6 F6 27 37 47 57 67 77 87 97 A7 B7 C7 FF DA 00 0C 03 01 00 02 11 03 11 00 3F 00 F5 54 0B 33 71 AB 79 AC BF 75 83 53 5B 01 7B 84 F1 B9 95 EE 73 54 F2 1C E6 D1 63 9B F4 9A C7 16 FC 40 5C F7 49 B7 1A FA 19 51 BF 23 1B 2C B4 3A EC 7B 2D 65 76 6E 23 DC FD 06 FB 58 FF 00 A4 CB 92 53 1E B9 F5 9B 3E 96 DB 8D 83 D3 B2 85 A7 DB 56 53 D8 1B 5C E9 EE 0F 7E E6 37 FE B8 AF FD 5B FD AA 71 9E EE A2 CB 6A 26 36 D7 7B C5 8F 07 5D EE DE 27 F4 7F 43 E9 21 E5 D5 85 8D 59 39 B9 04 56 EE 45 D9 6F F7 79 7A 6D 1E FF 00 EA AB 9D 0A C3 67 4D AC 9A AC C7 01 CF 0C A6 D2 4B DA D0 E7 7A 61 FE A4 BF E8 7E FA 4A 74 12 49 24 94 FF 00 FF D0 F5 2B DA 5D 4D 8D 1C B9 A4 7D E1 66 F4 FB BA 7F 50 C3 C7 17 D4 CF 59 B5 33 F4 57 06 97 00 47 2D 9F CC 74 7D 26 2D 55 CC 7D 61 CD 7F 40 38 66 BA 99 76 16 4D CE AD EC B8 4B 69 2E 06 C6 7A 76 0F 73 37 D9 EC D8 EF EC 24 A7 61 95 74 7C 70 6F 6B 28 AF 61 FE 72 1B 20 8F DD 77 D2 44 E9 97 8C 8C 63 7B 5A E6 36 CB 2C 21 AE D1 D0 1E EA F5 1F D8 58 1F 57 FA 95 DD 6B 23 25 B5 D5 5E 23 31 F6 13 63 1A 1E F9 70 73 76 56 F7 7B 6A FE 6F 7F D0 FC F5 D3 63 D1 56 3D 2C A6 A6 ED AD 82 1A 12 52 44 92 49 25 3F FF D1 F5 55 47 AD C7 EC AC AD DE 84 7A 6E 9F B5 4F A3 11 FE 1B 67 BF 67 F5 17 CC 69 24 A7 E9 6F AB BB 7F 66 55 B7 EC BB 60 47 D8 B7 7A 5C 7F C2 7E 93 FC F5 A8 BE 55 49 25 3F 55 24 BE 55 49 25 3F FF D9 FF ED 09 94 50 68 6F 74 6F 73 68 6F 70 20 33 2E 30 00 38 42 49 4D 04 25 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 38 42 49 4D 03 ED 00 00 00 00 00 10 00 96 00 00 00 01 00 01 00 96 00 00 00 01 00 01 38 42 49 4D 04 26 00 00 00 00 00 0E 00 00 00 00 00 00 00 00 00 00 3F 80 00 00 38 42 49 4D 04 0D 00 00 00 00 00 04 00 00 00 1E 38 42 49 4D 04 19 00 00 00 00 00 04 00 00 00 1E 38 42 49 4D 03 F3 00 00 00 00 00 09 00 00 00 00 00 00 00 00 01 00 38 42 49 4D 04 0A 00 00 00 00 00 01 00 00 38 42 49 4D 27 10 00 00 00 00 00 0A 00 01 00 00 00 00 00 00 00 01 38 42 49 4D 03 F5 00 00 00 00 00 48 00 2F 66 66 00 01 00 6C 66 66 00 06 00 00 00 00 00 01 00 2F 66 66 00 01 00 A1 99 9A 00 06 00 00 00 00 00 01 00 32 00 00 00 01 00 5A 00 00 00 06 00 00 00 00 00 01 00 35 00 00 00 01 00 2D 00 00 00 06 00 00 00 00 00 01 38 42 49 4D 03 F8 00 00 00 00 00 70 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 03 E8 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 03 E8 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 03 E8 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 03 E8 00 00 38 42 49 4D 04 08 00 00 00 00 00 10 00 00 00 01 00 00 02 40 00 00 02 40 00 00 00 00 38 42 49 4D 04 1E 00 00 00 00 00 04 00 00 00 00 38 42 49 4D 04 1A 00 00 00 00 03 59 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 23 00 00 00 23 00 00 00 12 00 54 00 43 00 50 00 5F 00 43 00 69 00 72 00 63 00 6C 00 65 00 5F 00 42 00 61 00 6C 00 6C 00 61 00 73 00 74 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 23 00 00 00 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 01 00 00 00 00 00 00 6E 75 6C 6C 00 00 00 02 00 00 00 06 62 6F 75 6E 64 73 4F 62 6A 63 00 00 00 01 00 00 00 00 00 00 52 63 74 31 00 00 00 04 00 00 00 00 54 6F 70 20 6C 6F 6E 67 00 00 00 00 00 00 00 00 4C 65 66 74 6C 6F 6E 67 00 00 00 00 00 00 00 00 42 74 6F 6D 6C 6F 6E 67 00 00 00 23 00 00 00 00 52 67 68 74 6C 6F 6E 67 00 00 00 23 00 00 00 06 73 6C 69 63 65 73 56 6C 4C 73 00 00 00 01 4F 62 6A 63 00 00 00 01 00 00 00 00 00 05 73 6C 69 63 65 00 00 00 12 00 00 00 07 73 6C 69 63 65 49 44 6C 6F 6E 67 00 00 00 00 00 00 00 07 67 72 6F 75 70 49 44 6C 6F 6E 67 00 00 00 00 00 00 00 06 6F 72 69 67 69 6E 65 6E 75 6D 00 00 00 0C 45 53 6C 69 63 65 4F 72 69 67 69 6E 00 00 00 0D 61 75 74 6F 47 65 6E 65 72 61 74 65 64 00 00 00 00 54 79 70 65 65 6E 75 6D 00 00 00 0A 45 53 6C 69 63 65 54 79 70 65 00 00 00 00 49 6D 67 20 00 00 00 06 62 6F 75 6E 64 73 4F 62 6A 63 00 00 00 01 00 00 00 00 00 00 52 63 74 31 00 00 00 04 00 00 00 00 54 6F 70 20 6C 6F 6E 67 00 00 00 00 00 00 00 00 4C 65 66 74 6C 6F 6E 67 00 00 00 00 00 00 00 00 42 74 6F 6D 6C 6F 6E 67 00 00 00 23 00 00 00 00 52 67 68 74 6C 6F 6E 67 00 00 00 23 00 00 00 03 75 72 6C 54 45 58 54 00 00 00 01 00 00 00 00 00 00 6E 75 6C 6C 54 45 58 54 00 00 00 01 00 00 00 00 00 00 4D 73 67 65 54 45 58 54 00 00 00 01 00 00 00 00 00 06 61 6C 74 54 61 67 54 45 58 54 00 00 00 01 00 00 00 00 00 0E 63 65 6C 6C 54 65 78 74 49 73 48 54 4D 4C 62 6F 6F 6C 01 00 00 00 08 63 65 6C 6C 54 65 78 74 54 45 58 54 00 00 00 01 00 00 00 00 00 09 68 6F 72 7A 41 6C 69 67 6E 65 6E 75 6D 00 00 00 0F 45 53 6C 69 63 65 48 6F 72 7A 41 6C 69 67 6E 00 00 00 07 64 65 66 61 75 6C 74 00 00 00 09 76 65 72 74 41 6C 69 67 6E 65 6E 75 6D 00 00 00 0F 45 53 6C 69 63 65 56 65 72 74 41 6C 69 67 6E 00 00 00 07 64 65 66 61 75 6C 74 00 00 00 0B 62 67 43 6F 6C 6F 72 54 79 70 65 65 6E 75 6D 00 00 00 11 45 53 6C 69 63 65 42 47 43 6F 6C 6F 72 54 79 70 65 00 00 00 00 4E 6F 6E 65 00 00 00 09 74 6F 70 4F 75 74 73 65 74 6C 6F 6E 67 00 00 00 00 00 00 00 0A 6C 65 66 74 4F 75 74 73 65 74 6C 6F 6E 67 00 00 00 00 00 00 00 0C 62 6F 74 74 6F 6D 4F 75 74 73 65 74 6C 6F 6E 67 00 00 00 00 00 00 00 0B 72 69 67 68 74 4F 75 74 73 65 74 6C 6F 6E 67 00 00 00 00 00 38 42 49 4D 04 11 00 00 00 00 00 01 01 00 38 42 49 4D 04 14 00 00 00 00 00 04 00 00 00 01 38 42 49 4D 04 0C 00 00 00 00 03 D5 00 00 00 01 00 00 00 23 00 00 00 23 00 00 00 6C 00 00 0E C4 00 00 03 B9 00 18 00 01 FF D8 FF E0 00 10 4A 46 49 46 00 01 02 01 00 48 00 48 00 00 FF ED 00 0C 41 64 6F 62 65 5F 43 4D 00 02 FF EE 00 0E 41 64 6F 62 65 00 64 80 00 00 00 01 FF DB 00 84 00 0C 08 08 08 09 08 0C 09 09 0C 11 0B 0A 0B 11 15 0F 0C 0C 0F 15 18 13 13 15 13 13 18 11 0C 0C 0C 0C 0C 0C 11 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 01 0D 0B 0B 0D 0E 0D 10 0E 0E 10 14 0E 0E 0E 14 14 0E 0E 0E 0E 14 11 0C 0C 0C 0C 0C 11 11 0C 0C 0C 0C 0C 0C 11 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C FF C0 00 11 08 00 23 00 23 03 01 22 00 02 11 01 03 11 01 FF DD 00 04 00 03 FF C4 01 3F 00 00 01 05 01 01 01 01 01 01 00 00 00 00 00 00 00 03 00 01 02 04 05 06 07 08 09 0A 0B 01 00 01 05 01 01 01 01 01 01 00 00 00 00 00 00 00 01 00 02 03 04 05 06 07 08 09 0A 0B 10 00 01 04 01 03 02 04 02 05 07 06 08 05 03 0C 33 01 00 02 11 03 04 21 12 31 05 41 51 61 13 22 71 81 32 06 14 91 A1 B1 42 23 24 15 52 C1 62 33 34 72 82 D1 43 07 25 92 53 F0 E1 F1 63 73 35 16 A2 B2 83 26 44 93 54 64 45 C2 A3 74 36 17 D2 55 E2 65 F2 B3 84 C3 D3 75 E3 F3 46 27 94 A4 85 B4 95 C4 D4 E4 F4 A5 B5 C5 D5 E5 F5 56 66 76 86 96 A6 B6 C6 D6 E6 F6 37 47 57 67 77 87 97 A7 B7 C7 D7 E7 F7 11 00 02 02 01 02 04 04 03 04 05 06 07 07 06 05 35 01 00 02 11 03 21 31 12 04 41 51 61 71 22 13 05 32 81 91 14 A1 B1 42 23 C1 52 D1 F0 33 24 62 E1 72 82 92 43 53 15 63 73 34 F1 25 06 16 A2 B2 83 07 26 35 C2 D2 44 93 54 A3 17 64 45 55 36 74 65 E2 F2 B3 84 C3 D3 75 E3 F3 46 94 A4 85 B4 95 C4 D4 E4 F4 A5 B5 C5 D5 E5 F5 56 66 76 86 96 A6 B6 C6 D6 E6 F6 27 37 47 57 67 77 87 97 A7 B7 C7 FF DA 00 0C 03 01 00 02 11 03 11 00 3F 00 F5 54 0B 33 71 AB 79 AC BF 75 83 53 5B 01 7B 84 F1 B9 95 EE 73 54 F2 1C E6 D1 63 9B F4 9A C7 16 FC 40 5C F7 49 B7 1A FA 19 51 BF 23 1B 2C B4 3A EC 7B 2D 65 76 6E 23 DC FD 06 FB 58 FF 00 A4 CB 92 53 1E B9 F5 9B 3E 96 DB 8D 83 D3 B2 85 A7 DB 56 53 D8 1B 5C E9 EE 0F 7E E6 37 FE B8 AF FD 5B FD AA 71 9E EE A2 CB 6A 26 36 D7 7B C5 8F 07 5D EE DE 27 F4 7F 43 E9 21 E5 D5 85 8D 59 39 B9 04 56 EE 45 D9 6F F7 79 7A 6D 1E FF 00 EA AB 9D 0A C3 67 4D AC 9A AC C7 01 CF 0C A6 D2 4B DA D0 E7 7A 61 FE A4 BF E8 7E FA 4A 74 12 49 24 94 FF 00 FF D0 F5 2B DA 5D 4D 8D 1C B9 A4 7D E1 66 F4 FB BA 7F 50 C3 C7 17 D4 CF 59 B5 33 F4 57 06 97 00 47 2D 9F CC 74 7D 26 2D 55 CC 7D 61 CD 7F 40 38 66 BA 99 76 16 4D CE AD EC B8 4B 69 2E 06 C6 7A 76 0F 73 37 D9 EC D8 EF EC 24 A7 61 95 74 7C 70 6F 6B 28 AF 61 FE 72 1B 20 8F DD 77 D2 44 E9 97 8C 8C 63 7B 5A E6 36 CB 2C 21 AE D1 D0 1E EA F5 1F D8 58 1F 57 FA 95 DD 6B 23 25 B5 D5 5E 23 31 F6 13 63 1A 1E F9 70 73 76 56 F7 7B 6A FE 6F 7F D0 FC F5 D3 63 D1 56 3D 2C A6 A6 ED AD 82 1A 12 52 44 92 49 25 3F FF D1 F5 55 47 AD C7 EC AC AD DE 84 7A 6E 9F B5 4F A3 11 FE 1B 67 BF 67 F5 17 CC 69 24 A7 E9 6F AB BB 7F 66 55 B7 EC BB 60 47 D8 B7 7A 5C 7F C2 7E 93 FC F5 A8 BE 55 49 25 3F 55 24 BE 55 49 25 3F FF D9 00 38 42 49 4D 04 21 00 00 00 00 00 55 00 00 00 01 01 00 00 00 0F 00 41 00 64 00 6F 00 62 00 65 00 20 00 50 00 68 00 6F 00 74 00 6F 00 73 00 68 00 6F 00 70 00 00 00 13 00 41 00 64 00 6F 00 62 00 65 00 20 00 50 00 68 00 6F 00 74 00 6F 00 73 00 68 00 6F 00 70 00 20 00 37 00 2E 00 30 00 00 00 01 00 38 42 49 4D 04 06 00 00 00 00 00 07 00 08 01 01 00 01 01 00 FF E1 12 48 68 74 74 70 3A 2F 2F 6E 73 2E 61 64 6F 62 65 2E 63 6F 6D 2F 78 61 70 2F 31 2E 30 2F 00 3C 3F 78 70 61 63 6B 65 74 20 62 65 67 69 6E 3D 27 EF BB BF 27 20 69 64 3D 27 57 35 4D 30 4D 70 43 65 68 69 48 7A 72 65 53 7A 4E 54 63 7A 6B 63 39 64 27 3F 3E 0A 3C 3F 61 64 6F 62 65 2D 78 61 70 2D 66 69 6C 74 65 72 73 20 65 73 63 3D 22 43 52 22 3F 3E 0A 3C 78 3A 78 61 70 6D 65 74 61 20 78 6D 6C 6E 73 3A 78 3D 27 61 64 6F 62 65 3A 6E 73 3A 6D 65 74 61 2F 27 20 78 3A 78 61 70 74 6B 3D 27 58 4D 50 20 74 6F 6F 6C 6B 69 74 20 32 2E 38 2E 32 2D 33 33 2C 20 66 72 61 6D 65 77 6F 72 6B 20 31 2E 35 27 3E 0A 3C 72 64 66 3A 52 44 46 20 78 6D 6C 6E 73 3A 72 64 66 3D 27 68 74 74 70 3A 2F 2F 77 77 77 2E 77 33 2E 6F 72 67 2F 31 39 39 39 2F 30 32 2F 32 32 2D 72 64 66 2D 73 79 6E 74 61 78 2D 6E 73 23 27 20 78 6D 6C 6E 73 3A 69 58 3D 27 68 74 74 70 3A 2F 2F 6E 73 2E 61 64 6F 62 65 2E 63 6F 6D 2F 69 58 2F 31 2E 30 2F 27 3E 0A 0A 20 3C 72 64 66 3A 44 65 73 63 72 69 70 74 69 6F 6E 20 61 62 6F 75 74 3D 27 75 75 69 64 3A 34 37 38 34 61 35 39 66 2D 37 65 64 66 2D 31 31 64 64 2D 39 61 37 39 2D 61 31 62 66 63 38 33 61 61 63 63 36 27 0A 20 20 78 6D 6C 6E 73 3A 78 61 70 4D 4D 3D 27 68 74 74 70 3A 2F 2F 6E 73 2E 61 64 6F 62 65 2E 63 6F 6D 2F 78 61 70 2F 31 2E 30 2F 6D 6D 2F 27 3E 0A 20 20 3C 78 61 70 4D 4D 3A 44 6F 63 75 6D 65 6E 74 49 44 3E 61 64 6F 62 65 3A 64 6F 63 69 64 3A 70 68 6F 74 6F 73 68 6F 70 3A 34 37 38 34 61 35 39 64 2D 37 65 64 66 2D 31 31 64 64 2D 39 61 37 39 2D 61 31 62 66 63 38 33 61 61 63 63 36 3C 2F 78 61 70 4D 4D 3A 44 6F 63 75 6D 65 6E 74 49 44 3E 0A 20 3C 2F 72 64 66 3A 44 65 73 63 72 69 70 74 69 6F 6E 3E 0A 0A 3C 2F 72 64 66 3A 52 44 46 3E 0A 3C 2F 78 3A 78 61 70 6D 65 74 61 3E 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0A 3C 3F 78 70 61 63 6B 65 74 20 65 6E 64 3D 27 77 27 3F 3E FF EE 00 21 41 64 6F 62 65 00 64 40 00 00 00 01 03 00 10 03 02 03 06 00 00 00 00 00 00 00 00 00 00 00 00 FF DB 00 84 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 02 02 02 02 02 02 02 02 02 02 02 03 03 03 03 03 03 03 03 03 03 01 01 01 01 01 01 01 01 01 01 01 02 02 01 02 02 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 FF C2 00 11 08 00 23 00 23 03 ); @expected = ($type, 35, 35); @results = Spreadsheet::WriteExcel::Workbook::_process_jpg(1, $image, 'test.jpg'); is_deeply(\@results, \@expected, " \t" . $testname); __END__ Spreadsheet-WriteExcel-2.40/t/30_validation_dval.t000644 000765 000024 00000004633 11304413104 022107 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for the Excel DVAL structure used in data validation. # # reverse(''), September 2008, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 3; ############################################################################### # # Tests setup # my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); my $target; my $result; my $caption; my $dv_count; my $obj_id; ############################################################################### # # Test 1. # $obj_id = 1; $dv_count = 1; $caption = " \tData validation: _store_dval($obj_id, $dv_count)"; $target = join " ", qw( B2 01 12 00 04 00 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 ); $result = unpack_record($worksheet->_store_dval($obj_id, $dv_count)); is($result, $target, $caption); ############################################################################### # # Test 2. # $obj_id = -1; $dv_count = 1; $caption = " \tData validation: _store_dval($obj_id, $dv_count)"; $target = join " ", qw( B2 01 12 00 04 00 00 00 00 00 00 00 00 00 FF FF FF FF 01 00 00 00 ); $result = unpack_record($worksheet->_store_dval($obj_id, $dv_count)); is($result, $target, $caption); ############################################################################### # # Test 3. # $obj_id = 1; $dv_count = 2; $caption = " \tData validation: _store_dval($obj_id, $dv_count)"; $target = join " ", qw( B2 01 12 00 04 00 00 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 ); $result = unpack_record($worksheet->_store_dval($obj_id, $dv_count)); is($result, $target, $caption); ############################################################################### # # Unpack the binary data into a format suitable for printing in tests. # sub unpack_record { return join ' ', map {sprintf "%02X", $_} unpack "C*", $_[0]; } # Cleanup $workbook->close(); unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/31_validation_dv_strings.t000644 000765 000024 00000014351 11304413104 023342 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for the packed caption/message strings used in the Excel DV structure # as part of data validation. # # reverse(''), September 2008, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 8; ############################################################################### # # Tests setup # my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); my $target; my $result; my $caption; my $string; my $max_length; ############################################################################### # # Test 1 Empty string. # $string = ''; $max_length = 32; $caption = " \tData validation: _pack_dv_string('', $max_length)"; $target = join " ", qw( 01 00 00 00 ); $result = unpack_record($worksheet->_pack_dv_string($string, $max_length)); is($result, $target, $caption); ############################################################################### # # Test 2 undef. # $string = undef; $max_length = 32; $caption = " \tData validation: _pack_dv_string(undef, $max_length)"; $target = join " ", qw( 01 00 00 00 ); $result = unpack_record($worksheet->_pack_dv_string($string, $max_length)); is($result, $target, $caption); ############################################################################### # # Test 3 Single space. # $string = ' '; $max_length = 32; $caption = " \tData validation: _pack_dv_string(' ', $max_length)"; $target = join " ", qw( 01 00 00 20 ); $result = unpack_record($worksheet->_pack_dv_string($string, $max_length)); is($result, $target, $caption); ############################################################################### # # Test 4 Single character. # $string = 'A'; $max_length = 32; $caption = " \tData validation: _pack_dv_string('$string', $max_length)"; $target = join " ", qw( 01 00 00 41 ); $result = unpack_record($worksheet->_pack_dv_string($string, $max_length)); is($result, $target, $caption); ############################################################################### # # Test 5 String longer than 32 characters (for dialog captions). # $string = 'This string is longer than 32 characters'; $max_length = 32; $caption = " \tData validation: _pack_dv_string('$string', $max_length)"; $target = join " ", qw( 20 00 00 54 68 69 73 20 73 74 72 69 6E 67 20 69 73 20 6C 6F 6E 67 65 72 20 74 68 61 6E 20 33 32 20 63 68 ); $result = unpack_record($worksheet->_pack_dv_string($string, $max_length)); is($result, $target, $caption); ############################################################################### # # Test 6 String longer than 32 characters (for dialog messages).. # $string = 'ABCD' x 64; $max_length = 255; $caption = " \tData validation: _pack_dv_string('264 char string', $max_length)"; $target = join " ", qw( FF 00 00 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 44 41 42 43 ); $result = unpack_record($worksheet->_pack_dv_string($string, $max_length)); is($result, $target, $caption); ############################################################################### # # Test 7 Unicode string. # SKIP: { skip " \_pack_dv_string(). Test requires Perl 5.8 Unicode support.", 1 if $] < 5.008; $string = chr 0x20Ac; # Euro symbol $max_length = 32; $caption = " \tData validation: _pack_dv_string(utf8 string, $max_length)"; $target = join " ", qw( 01 00 01 AC 20 ); $result = unpack_record($worksheet->_pack_dv_string($string, $max_length)); is($result, $target, $caption); } ############################################################################### # # Test 8 Longer unicode string. # SKIP: { skip " \_pack_dv_string(). Test requires Perl 5.8 Unicode support.", 1 if $] < 5.008; $string = chr(0x20Ac) . '2.99 Foo'; $max_length = 32; $caption = " \tData validation: _pack_dv_string(utf8 string, $max_length)"; $target = join " ", qw( 09 00 01 AC 20 32 00 2E 00 39 00 39 00 20 00 46 00 6F 00 6F 00 ); $result = unpack_record($worksheet->_pack_dv_string($string, $max_length)); is($result, $target, $caption); } ############################################################################### # # Unpack the binary data into a format suitable for printing in tests. # sub unpack_record { return join ' ', map {sprintf "%02X", $_} unpack "C*", $_[0]; } # Cleanup $workbook->close(); unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/32_validation_dv_formula.t000644 000765 000024 00000017221 11304413104 023316 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for the packed formula strings used in the Excel DV structure # as part of data validation. # # reverse(''), September 2008, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 12; ############################################################################### # # Tests setup # my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); my $worksheet2 = $workbook->add_worksheet(); my $target; my $result; my $caption; my $formula; my @bytes; ############################################################################### # # Test 1 Integer values. # $formula = '10'; $caption = " \tData validation: _pack_dv_formula('$formula')"; @bytes = qw( 03 00 00 E0 1E 0A 00 ); # Zero out Excel's random unused word to allow comparison. $bytes[2] = '00'; $bytes[3] = '00'; $target = join " ", @bytes; $result = unpack_record($worksheet->_pack_dv_formula($formula)); is($result, $target, $caption); ############################################################################### # # Test 2 Decimal values. # $formula = '1.2345'; $caption = " \tData validation: _pack_dv_formula('$formula')"; @bytes = qw( 09 00 E0 3F 1F 8D 97 6E 12 83 C0 F3 3F ); # Zero out Excel's random unused word to allow comparison. $bytes[2] = '00'; $bytes[3] = '00'; $target = join " ", @bytes; $result = unpack_record($worksheet->_pack_dv_formula($formula)); is($result, $target, $caption); ############################################################################### # # Test 3 Date values.. # $formula = $worksheet->convert_date_time('2008-07-24T'); $caption = " \tData validation: _pack_dv_formula('2008-07-24')"; @bytes = qw( 03 00 E0 3F 1E E5 9A ); # Zero out Excel's random unused word to allow comparison. $bytes[2] = '00'; $bytes[3] = '00'; $target = join " ", @bytes; $result = unpack_record($worksheet->_pack_dv_formula($formula)); is($result, $target, $caption); ############################################################################### # # Test 4 Time values. # $formula = $worksheet->convert_date_time('T12:00'); $caption = " \tData validation: _pack_dv_formula('12:00')"; @bytes = qw( 09 00 E0 3F 1F 00 00 00 00 00 00 E0 3F ); # Zero out Excel's random unused word to allow comparison. $bytes[2] = '00'; $bytes[3] = '00'; $target = join " ", @bytes; $result = unpack_record($worksheet->_pack_dv_formula($formula)); is($result, $target, $caption); ############################################################################### # # Test 5 Cell reference value. # $formula = '=C9'; $caption = " \tData validation: _pack_dv_formula('$formula')"; @bytes = qw( 05 00 E0 3F 44 08 00 02 C0 ); # Zero out Excel's random unused word to allow comparison. $bytes[2] = '00'; $bytes[3] = '00'; $target = join " ", @bytes; $result = unpack_record($worksheet->_pack_dv_formula($formula)); is($result, $target, $caption); ############################################################################### # # Test 6 Cell reference value. # $formula = '=E3:E6'; $caption = " \tData validation: _pack_dv_formula('$formula')"; @bytes = qw( 09 00 0C 00 25 02 00 05 00 04 C0 04 C0 ); # Zero out Excel's random unused word to allow comparison. $bytes[2] = '00'; $bytes[3] = '00'; $target = join " ", @bytes; $result = unpack_record($worksheet->_pack_dv_formula($formula)); is($result, $target, $caption); ############################################################################### # # Test 7 Cell reference value. # $formula = '=$E$3:$E$6'; $caption = " \tData validation: _pack_dv_formula('$formula')"; @bytes = qw( 09 00 0C 00 25 02 00 05 00 04 00 04 00 ); # Zero out Excel's random unused word to allow comparison. $bytes[2] = '00'; $bytes[3] = '00'; $target = join " ", @bytes; $result = unpack_record($worksheet->_pack_dv_formula($formula)); is($result, $target, $caption); ############################################################################### # # Test 8 Cell reference value. # $formula = '=$E$3:$E$6'; $caption = " \tData validation: _pack_dv_formula('$formula')"; @bytes = qw( 09 00 0C 00 25 02 00 05 00 04 00 04 00 ); # Zero out Excel's random unused word to allow comparison. $bytes[2] = '00'; $bytes[3] = '00'; $target = join " ", @bytes; $result = unpack_record($worksheet->_pack_dv_formula($formula)); is($result, $target, $caption); ############################################################################### # # Test 9 List values. # $formula = ['a', 'bb', 'ccc']; $caption = " \tData validation: _pack_dv_formula(['a', 'bb', 'ccc'])"; @bytes = qw( 0B 00 0C 00 17 08 00 61 00 62 62 00 63 63 63 ); # Zero out Excel's random unused word to allow comparison. $bytes[2] = '00'; $bytes[3] = '00'; $target = join " ", @bytes; $result = unpack_record($worksheet->_pack_dv_formula($formula)); is($result, $target, $caption); ############################################################################### # # Test 10 Empty string. # $formula = ''; $caption = " \tData validation: _pack_dv_formula('')"; @bytes = qw( 00 00 00 ); # Zero out Excel's random unused word to allow comparison. $bytes[2] = '00'; $bytes[3] = '00'; $target = join " ", @bytes; $result = unpack_record($worksheet->_pack_dv_formula($formula)); is($result, $target, $caption); ############################################################################### # # Test 11 Undefined value. # $formula = undef; $caption = " \tData validation: _pack_dv_formula(undef)"; @bytes = qw( 00 00 00 ); # Zero out Excel's random unused word to allow comparison. $bytes[2] = '00'; $bytes[3] = '00'; $target = join " ", @bytes; $result = unpack_record($worksheet->_pack_dv_formula($formula)); is($result, $target, $caption); ############################################################################### # # Test 10 List values (with a utf8 string). # SKIP: { skip " \_pack_dv_string(). Test requires Perl 5.8 Unicode support.", 1 if $] < 5.008; my $euro = chr 0x20Ac; $formula = ['a', 'bb', 'ccc', $euro]; $caption = " \tData validation: _pack_dv_formula(['a', 'bb', 'ccc', utf8])"; @bytes = qw( 17 00 0C 00 17 0A 01 61 00 00 00 62 00 62 00 00 00 63 00 63 00 63 00 00 00 AC 20 ); # Zero out Excel's random unused word to allow comparison. $bytes[2] = '00'; $bytes[3] = '00'; $target = join " ", @bytes; $result = unpack_record($worksheet->_pack_dv_formula($formula)); is($result, $target, $caption); } # TODO # Test failing reference to Sheet2!A1 # Test for formula string > 255 chars # $formula = ['a' x 256]; ############################################################################### # # Unpack the binary data into a format suitable for printing in tests. # sub unpack_record { return join ' ', map {sprintf "%02X", $_} unpack "C*", $_[0]; } # Cleanup $workbook->close(); unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/33_validation_dv.t000644 000765 000024 00000073342 12053231174 021607 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for the Excel DV structure used in data validation. # # reverse(''), September 2008, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 43; ############################################################################### # # Tests setup # my $test_file = 'temp_test_file.xls'; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); my $target; my $result; my $caption; # Store worksheet data in memory so we can access it for testing. $worksheet->{_using_tmpfile} = 0; ############################################################################### # # Test 1 Integer between 1 and 10. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, }); $worksheet->_store_validations(); $caption = " \tData validation api: integer between"; $target = join " ", qw( BE 01 2C 00 01 01 0C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 2 Integer not between 1 and 10. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => 'not between', minimum => 1, maximum => 10, }); $worksheet->_store_validations(); $caption = " \tData validation api: integer not between"; $target = join " ", qw( BE 01 2C 00 01 01 1C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 3,4,5 Integer == 1. # for my $operator ('equal to', '=', '==') { $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => $operator, value => 1, }); $worksheet->_store_validations(); $caption = " \tData validation api: integer equal to"; $target = join " ", qw( BE 01 29 00 01 01 2C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 00 00 00 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); } ############################################################################### # # Test 6,7,8 Integer != 1. # for my $operator ('not equal to', '<>', '!=') { $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => $operator, value => 1, }); $worksheet->_store_validations(); $caption = " \tData validation api: integer not equal to"; $target = join " ", qw( BE 01 29 00 01 01 3C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 00 00 00 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); } ############################################################################### # # Test 9,10 Integer > 1. # for my $operator ('greater than', '>') { $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => $operator, value => 1, }); $worksheet->_store_validations(); $caption = " \tData validation api: integer >"; $target = join " ", qw( BE 01 29 00 01 01 4C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 00 00 00 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); } ############################################################################### # # Test 11,12 Integer < 1. # for my $operator ('less than', '<') { $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => $operator, value => 1, }); $worksheet->_store_validations(); $caption = " \tData validation api: integer <"; $target = join " ", qw( BE 01 29 00 01 01 5C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 00 00 00 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); } ############################################################################### # # Test 13,14 Integer >= 1. # for my $operator ('greater than or equal to', '>=') { $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => $operator, value => 1, }); $worksheet->_store_validations(); $caption = " \tData validation api: integer >="; $target = join " ", qw( BE 01 29 00 01 01 6C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 00 00 00 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); } ############################################################################### # # Test 15,16 Integer <= 1. # for my $operator ('less than or equal to', '<=') { $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => $operator, value => 1, }); $worksheet->_store_validations(); $caption = " \tData validation api: integer <="; $target = join " ", qw( BE 01 29 00 01 01 7C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 00 00 00 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); } ############################################################################### # # Test 17 Integer between 1 and 10 (same as test 1) + Ignore blank off. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, ignore_blank => 0, }); $worksheet->_store_validations(); $caption = " \tData validation api: ignore blank off"; $target = join " ", qw( BE 01 2C 00 01 00 0C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 18 Integer between 1 and 10 (same as test 1) + Error style == warning.. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, error_type => 'warning', }); $worksheet->_store_validations(); $caption = " \tData validation api: error style = warning"; $target = join " ", qw( BE 01 2C 00 11 01 0C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 19 Integer between 1 and 10 (same as test 1) + Error style == infor.. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, error_type => 'information', }); $worksheet->_store_validations(); $caption = " \tData validation api: error style = information"; $target = join " ", qw( BE 01 2C 00 21 01 0C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 20 Integer between 1 and 10 (same as test 1) # + input title. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, input_title => 'Input title January', }); $worksheet->_store_validations(); $caption = " \tData validation api: with input title"; $target = join " ", qw( BE 01 3E 00 01 01 0C 00 13 00 00 49 6E 70 75 74 20 74 69 74 6C 65 20 4A 61 6E 75 61 72 79 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 21 Integer between 1 and 10 (same as test 1) # + input title. # + input message. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, input_title => 'Input title January', input_message => 'Input message February', }); $worksheet->_store_validations(); $caption = " \tData validation api: + input message"; $target = join " ", qw( BE 01 53 00 01 01 0C 00 13 00 00 49 6E 70 75 74 20 74 69 74 6C 65 20 4A 61 6E 75 61 72 79 01 00 00 00 16 00 00 49 6E 70 75 74 20 6D 65 73 73 61 67 65 20 46 65 62 72 75 61 72 79 01 00 00 00 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 22 Integer between 1 and 10 (same as test 1) # + input title. # + input message. # + error title. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, input_title => 'Input title January', input_message => 'Input message February', error_title => 'Error title March', }); $worksheet->_store_validations(); $caption = " \tData validation api: + error title"; $target = join " ", qw( BE 01 63 00 01 01 0C 00 13 00 00 49 6E 70 75 74 20 74 69 74 6C 65 20 4A 61 6E 75 61 72 79 11 00 00 45 72 72 6F 72 20 74 69 74 6C 65 20 4D 61 72 63 68 16 00 00 49 6E 70 75 74 20 6D 65 73 73 61 67 65 20 46 65 62 72 75 61 72 79 01 00 00 00 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 23 Integer between 1 and 10 (same as test 1) # + input title. # + input message. # + error title. # + error message. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, input_title => 'Input title January', input_message => 'Input message February', error_title => 'Error title March', error_message => 'Error message April', }); $worksheet->_store_validations(); $caption = " \tData validation api: + error message"; $target = join " ", qw( BE 01 75 00 01 01 0C 00 13 00 00 49 6E 70 75 74 20 74 69 74 6C 65 20 4A 61 6E 75 61 72 79 11 00 00 45 72 72 6F 72 20 74 69 74 6C 65 20 4D 61 72 63 68 16 00 00 49 6E 70 75 74 20 6D 65 73 73 61 67 65 20 46 65 62 72 75 61 72 79 13 00 00 45 72 72 6F 72 20 6D 65 73 73 61 67 65 20 41 70 72 69 6C 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 24 Integer between 1 and 10 (same as test 1) # + input title. # + input message. # + error title. # + error message. # - input message box. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, input_title => 'Input title January', input_message => 'Input message February', error_title => 'Error title March', error_message => 'Error message April', show_input => 0, }); $worksheet->_store_validations(); $caption = " \tData validation api: no input box"; $target = join " ", qw( BE 01 75 00 01 01 08 00 13 00 00 49 6E 70 75 74 20 74 69 74 6C 65 20 4A 61 6E 75 61 72 79 11 00 00 45 72 72 6F 72 20 74 69 74 6C 65 20 4D 61 72 63 68 16 00 00 49 6E 70 75 74 20 6D 65 73 73 61 67 65 20 46 65 62 72 75 61 72 79 13 00 00 45 72 72 6F 72 20 6D 65 73 73 61 67 65 20 41 70 72 69 6C 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 25 Integer between 1 and 10 (same as test 1) # + input title. # + input message. # + error title. # + error message. # - input message box. # - error message box. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, input_title => 'Input title January', input_message => 'Input message February', error_title => 'Error title March', error_message => 'Error message April', show_input => 0, show_error => 0, }); $worksheet->_store_validations(); $caption = " \tData validation api: no error box"; $target = join " ", qw( BE 01 75 00 01 01 00 00 13 00 00 49 6E 70 75 74 20 74 69 74 6C 65 20 4A 61 6E 75 61 72 79 11 00 00 45 72 72 6F 72 20 74 69 74 6C 65 20 4D 61 72 63 68 16 00 00 49 6E 70 75 74 20 6D 65 73 73 61 67 65 20 46 65 62 72 75 61 72 79 13 00 00 45 72 72 6F 72 20 6D 65 73 73 61 67 65 20 41 70 72 69 6C 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 26 'Any' value shouldn't produce a DV record. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'any', }); $worksheet->_store_validations(); $caption = " \tData validation api: any validation"; $target = ''; $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 27 Decimal = 1.2345 # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'decimal', criteria => '==', value => 1.2345, }); $worksheet->_store_validations(); $caption = " \tData validation api: decimal validation"; $target = join " ", qw( BE 01 2F 00 02 01 2C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 09 00 00 00 1F 8D 97 6E 12 83 C0 F3 3F 00 00 00 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 28 List = a,bb,ccc # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'list', source => ['a', 'bb', 'ccc'], }); $worksheet->_store_validations(); $caption = " \tData validation api: explicit list"; $target = join " ", qw( BE 01 31 00 83 01 0C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 0B 00 00 00 17 08 00 61 00 62 62 00 63 63 63 00 00 00 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 29 List = a,bb,ccc, No dropdown # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'list', source => ['a', 'bb', 'ccc'], dropdown => 0, }); $worksheet->_store_validations(); $caption = " \tData validation api: list with no dropdown"; $target = join " ", qw( BE 01 31 00 83 03 0C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 0B 00 00 00 17 08 00 61 00 62 62 00 63 63 63 00 00 00 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 30 List = $D$1:$D$5 # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('A1:A1', { validate => 'list', source => '=$D$1:$D$5', }); $worksheet->_store_validations(); $caption = " \tData validation api: list with range"; $target = join " ", qw( BE 01 2F 00 03 01 0C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 09 00 00 00 25 00 00 04 00 03 00 03 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 31 Date = 39653 (2008-07-24) # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'date', criteria => '==', value => 39653, }); $worksheet->_store_validations(); $caption = " \tData validation api: date"; $target = join " ", qw( BE 01 29 00 04 01 2C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E E5 9A 00 00 00 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 32 Date = 2008-07-24T # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'date', criteria => '==', value => '2008-07-24T', }); $worksheet->_store_validations(); $caption = " \tData validation api: date auto"; $target = join " ", qw( BE 01 29 00 04 01 2C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E E5 9A 00 00 00 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 33 Date between ranges. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'date', criteria => 'between', minimum => '2008-01-01T', maximum => '2008-12-12T', }); $worksheet->_store_validations(); $caption = " \tData validation api: date auto, between"; $target = join " ", qw( BE 01 2C 00 04 01 0C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 18 9A 03 00 00 00 1E 72 9B 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 34 Time = 0.5 (12:00:00) # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5:B5', { validate => 'time', criteria => '==', value => 0.5, }); $worksheet->_store_validations(); $caption = " \tData validation api: time"; $target = join " ", qw( BE 01 2F 00 05 01 2C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 09 00 00 00 1F 00 00 00 00 00 00 E0 3F 00 00 00 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 35 Time = T12:00:00 # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'time', criteria => '==', value => 'T12:00:00', }); $worksheet->_store_validations(); $caption = " \tData validation api: time auto"; $target = join " ", qw( BE 01 2F 00 05 01 2C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 09 00 00 00 1F 00 00 00 00 00 00 E0 3F 00 00 00 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 36 Custom == 10. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'Custom', criteria => '==', value => 10, }); $worksheet->_store_validations(); $caption = " \tData validation api: custom"; $target = join " ", qw( BE 01 29 00 07 01 0C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 0A 00 00 00 00 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 37 Check the row/col processing: single A1 style cell. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5', { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, }); $worksheet->_store_validations(); $caption = " \tData validation api: range options"; $target = join " ", qw( BE 01 2C 00 01 01 0C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 38 Check the row/col processing: single A1 style range. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation('B5:B10', { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, }); $worksheet->_store_validations(); $caption = " \tData validation api: range options"; $target = join " ", qw( BE 01 2C 00 01 01 0C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 01 00 04 00 09 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 39 Check the row/col processing: single (row, col) style cell. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation(4, 1, { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, }); $worksheet->_store_validations(); $caption = " \tData validation api: range options"; $target = join " ", qw( BE 01 2C 00 01 01 0C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 01 00 04 00 04 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 40 Check the row/col processing: single (row, col) style range. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation(4, 1, 9, 1, { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, }); $worksheet->_store_validations(); $caption = " \tData validation api: range options"; $target = join " ", qw( BE 01 2C 00 01 01 0C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 01 00 04 00 09 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 41 Check the row/col processing: multiple (row, col) style cells. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation(4, 1, { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, other_cells => [[4, 3, 4, 3]], }); $worksheet->_store_validations(); $caption = " \tData validation api: range options"; $target = join " ", qw( BE 01 34 00 01 01 0C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 02 00 04 00 04 00 01 00 01 00 04 00 04 00 03 00 03 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 42 Check the row/col processing: multiple (row, col) style cells. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation(4, 1, { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, other_cells => [[6, 1, 6, 1], [8, 1, 8, 1]], }); $worksheet->_store_validations(); $caption = " \tData validation api: range options"; $target = join " ", qw( BE 01 3C 00 01 01 0C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 03 00 04 00 04 00 01 00 01 00 06 00 06 00 01 00 01 00 08 00 08 00 01 00 01 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Test 43 Check the row/col processing: multiple (row, col) style cells. # $worksheet->{_data} = ''; $worksheet->{_validations} = []; $worksheet->data_validation(4, 1, 9, 1, { validate => 'integer', criteria => 'between', minimum => 1, maximum => 10, other_cells => [[4, 3, 4, 3]], }); $worksheet->_store_validations(); $caption = " \tData validation api: range options"; $target = join " ", qw( BE 01 34 00 01 01 0C 00 01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 03 00 00 00 1E 01 00 03 00 00 00 1E 0A 00 02 00 04 00 09 00 01 00 01 00 04 00 04 00 03 00 03 00 ); $result = unpack_record($worksheet->{_data}); is($result, $target, $caption); ############################################################################### # # Unpack the binary data into a format suitable for printing in tests. # sub unpack_record { return join ' ', map {sprintf "%02X", $_} unpack "C*", $_[0]; } # Cleanup $workbook->close(); unlink $test_file; __END__ Tool completed successfully Spreadsheet-WriteExcel-2.40/t/40_property_types.t000644 000765 000024 00000017120 11304413104 022053 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # Testcases for Spreadsheet::WriteExcel. # # Tests for the basic property types used in OLE property sets. # # reverse(''), Auguest 2008, John McNamara, jmcnamara@cpan.org # use strict; use Carp; use Spreadsheet::WriteExcel::Properties ':testing'; use Time::Local 'timegm'; use Test::More tests => 13; ############################################################################### # # Tests setup # my $target; my $result; my $caption; my $string; my $codepage; my $smiley = chr 0x263A; my $filetime; ############################################################################### # # Test 1. Pack a VT_I2. # $caption = " \tDoc properties: _pack_VT_I2(1252)"; $target = join " ", qw( 02 00 00 00 E4 04 00 00 ); $result = unpack_record( _pack_VT_I2(1252) ); is($result, $target, $caption); ############################################################################### # # Test 2. Pack a VT_LPSTR string and check for padding. # $string = ''; $codepage = 0x04E4; $caption = " \tDoc properties: _pack_VT_LPSTR('$string',\t$codepage')"; $target = join " ", qw( 1E 00 00 00 01 00 00 00 00 00 00 00 ); $result = unpack_record( _pack_VT_LPSTR($string, $codepage) ); is($result, $target, $caption); ############################################################################### # # Test 3. Pack a VT_LPSTR string and check for padding. # $string = 'a'; $codepage = 0x04E4; $caption = " \tDoc properties: _pack_VT_LPSTR('$string',\t$codepage')"; $target = join " ", qw( 1E 00 00 00 02 00 00 00 61 00 00 00 ); $result = unpack_record( _pack_VT_LPSTR($string, $codepage) ); is($result, $target, $caption); ############################################################################### # # Test 4. Pack a VT_LPSTR string and check for padding. # $string = 'bb'; $codepage = 0x04E4; $caption = " \tDoc properties: _pack_VT_LPSTR('$string',\t$codepage')"; $target = join " ", qw( 1E 00 00 00 03 00 00 00 62 62 00 00 ); $result = unpack_record( _pack_VT_LPSTR($string, $codepage) ); is($result, $target, $caption); ############################################################################### # # Test 5. Pack a VT_LPSTR string and check for padding. # $string = 'ccc'; $codepage = 0x04E4; $caption = " \tDoc properties: _pack_VT_LPSTR('$string',\t$codepage')"; $target = join " ", qw( 1E 00 00 00 04 00 00 00 63 63 63 00 ); $result = unpack_record( _pack_VT_LPSTR($string, $codepage) ); is($result, $target, $caption); ############################################################################### # # Test 6. Pack a VT_LPSTR string and check for padding. # $string = 'dddd'; $codepage = 0x04E4; $caption = " \tDoc properties: _pack_VT_LPSTR('$string',\t$codepage')"; $target = join " ", qw( 1E 00 00 00 05 00 00 00 64 64 64 64 00 00 00 00 ); $result = unpack_record( _pack_VT_LPSTR($string, $codepage) ); is($result, $target, $caption); ############################################################################### # # Test 7. Pack a VT_LPSTR string and check for padding. # $string = 'Username'; $codepage = 0x04E4; $caption = " \tDoc properties: _pack_VT_LPSTR('$string',\t$codepage')"; $target = join " ", qw( 1E 00 00 00 09 00 00 00 55 73 65 72 6E 61 6D 65 00 00 00 00 ); $result = unpack_record( _pack_VT_LPSTR($string, $codepage) ); is($result, $target, $caption); ############################################################################### # # Test 8. Pack a VT_LPSTR UTF8 string. # SKIP: { skip " \t_pack_VT_LPSTR(utf8). Test requires Perl 5.8 Unicode support.", 1 if $] < 5.008; $string = "$smiley"; $codepage = 0xFDE9; $caption = " \tDoc properties: _pack_VT_LPSTR('\$smiley',\t$codepage')"; $target = join " ", qw( 1E 00 00 00 04 00 00 00 E2 98 BA 00 ); $result = unpack_record( _pack_VT_LPSTR($string, $codepage) ); is($result, $target, $caption); } ############################################################################### # # Test 9. Pack a VT_LPSTR UTF8 string. # SKIP: { skip " \t_pack_VT_LPSTR(utf8). Test requires Perl 5.8 Unicode support.", 1 if $] < 5.008; $string = "a$smiley"; $codepage = 0xFDE9; $caption = " \tDoc properties: _pack_VT_LPSTR('a\$smiley',\t$codepage')"; $target = join " ", qw( 1E 00 00 00 05 00 00 00 61 E2 98 BA 00 00 00 00 ); $result = unpack_record( _pack_VT_LPSTR($string, $codepage) ); is($result, $target, $caption); } ############################################################################### # # Test 10. Pack a VT_LPSTR UTF8 string. # SKIP: { skip " \t_pack_VT_LPSTR(utf8). Test requires Perl 5.8 Unicode support.", 1 if $] < 5.008; $string = "aa$smiley"; $codepage = 0xFDE9; $caption = " \tDoc properties: _pack_VT_LPSTR('aa\$smiley',\t$codepage')"; $target = join " ", qw( 1E 00 00 00 06 00 00 00 61 61 E2 98 BA 00 00 00 ); $result = unpack_record( _pack_VT_LPSTR($string, $codepage) ); is($result, $target, $caption); } ############################################################################### # # Test 11. Pack a VT_LPSTR UTF8 string. # SKIP: { skip " \t_pack_VT_LPSTR(utf8). Test requires Perl 5.8 Unicode support.", 1 if $] < 5.008; $string = "aaa$smiley"; $codepage = 0xFDE9; $caption = " \tDoc properties: _pack_VT_LPSTR('aaa\$smiley',\t$codepage')"; $target = join " ", qw( 1E 00 00 00 07 00 00 00 61 61 61 E2 98 BA 00 00 ); $result = unpack_record( _pack_VT_LPSTR($string, $codepage) ); is($result, $target, $caption); } ############################################################################### # # Test 12. Pack a VT_LPSTR UTF8 string. # SKIP: { skip " \t_pack_VT_LPSTR(utf8). Test requires Perl 5.8 Unicode support.", 1 if $] < 5.008; $string = "aaaa$smiley"; $codepage = 0xFDE9; $caption = " \tDoc properties: _pack_VT_LPSTR('aaaa\$smiley',\t$codepage')"; $target = join " ", qw( 1E 00 00 00 08 00 00 00 61 61 61 61 E2 98 BA 00 ); $result = unpack_record( _pack_VT_LPSTR($string, $codepage) ); is($result, $target, $caption); } ############################################################################### # # Test 13. Pack a VT_FILETIME. # # Wed Aug 13 01:40:00 2008 # $sec,$min,$hour,$mday,$mon,$year # We normalise the time using timegm() so that the tests don't fail due to # different timezones. $filetime = [localtime(timegm(0, 40, 0, 13, 7, 108))]; $caption = " \tDoc properties: _pack_VT_FILETIME()"; $target = join " ", qw( 40 00 00 00 00 70 EB 1D DD FC C8 01 ); $result = unpack_record( _pack_VT_FILETIME($filetime) ); is($result, $target, $caption); ############################################################################### # # Unpack the binary data into a format suitable for printing in tests. # sub unpack_record { return join ' ', map {sprintf "%02X", $_} unpack "C*", $_[0]; } __END__ Spreadsheet-WriteExcel-2.40/t/41_properties.t000644 000765 000024 00000027706 11304413104 021153 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # Testcases for Spreadsheet::WriteExcel. # # Tests for OLE property sets. # # reverse(''), Auguest 2008, John McNamara, jmcnamara@cpan.org # use strict; use Carp; use Spreadsheet::WriteExcel::Properties ':testing'; use Time::Local 'timegm'; use Test::More tests => 8; ############################################################################### # # Tests setup # my $target; my $result; my $caption; my $string; my $codepage; my $smiley = chr 0x263A; my $filetime; my @properties; ############################################################################### # # Test 1. Codepage only. # @properties = ([0x0001, 'VT_I2', 0x04E4 ]); $caption = " \tDoc properties: create_summary_property_set('Code page')"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 18 00 00 00 01 00 00 00 01 00 00 00 10 00 00 00 02 00 00 00 E4 04 00 00 ); $result = unpack_record( create_summary_property_set(\@properties) ); is($result, $target, $caption); ############################################################################### # # Test 2. Same as previous + Title. # @properties = ( [0x0001, 'VT_I2', 0x04E4 ], [0x0002, 'VT_LPSTR', 'Title'], ); $caption = " \tDoc properties: create_summary_property_set('+ Title')"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 30 00 00 00 02 00 00 00 01 00 00 00 18 00 00 00 02 00 00 00 20 00 00 00 02 00 00 00 E4 04 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 ); $result = unpack_record( create_summary_property_set(\@properties) ); is($result, $target, $caption); ############################################################################### # # Test 3. Same as previous + Subject. # @properties = ( [0x0001, 'VT_I2', 0x04E4 ], [0x0002, 'VT_LPSTR', 'Title' ], [0x0003, 'VT_LPSTR', 'Subject'], ); $caption = " \tDoc properties: create_summary_property_set('+ Subject')"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 48 00 00 00 03 00 00 00 01 00 00 00 20 00 00 00 02 00 00 00 28 00 00 00 03 00 00 00 38 00 00 00 02 00 00 00 E4 04 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 1E 00 00 00 08 00 00 00 53 75 62 6A 65 63 74 00 ); $result = unpack_record( create_summary_property_set(\@properties) ); is($result, $target, $caption); ############################################################################### # # Test 4. Same as previous + Author. # @properties = ( [0x0001, 'VT_I2', 0x04E4 ], [0x0002, 'VT_LPSTR', 'Title' ], [0x0003, 'VT_LPSTR', 'Subject'], [0x0004, 'VT_LPSTR', 'Author' ], ); $caption = " \tDoc properties: create_summary_property_set('+ Author')"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 60 00 00 00 04 00 00 00 01 00 00 00 28 00 00 00 02 00 00 00 30 00 00 00 03 00 00 00 40 00 00 00 04 00 00 00 50 00 00 00 02 00 00 00 E4 04 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 1E 00 00 00 08 00 00 00 53 75 62 6A 65 63 74 00 1E 00 00 00 07 00 00 00 41 75 74 68 6F 72 00 00 ); $result = unpack_record( create_summary_property_set(\@properties) ); is($result, $target, $caption); ############################################################################### # # Test 5. Same as previous + Keywords. # @properties = ( [0x0001, 'VT_I2', 0x04E4 ], [0x0002, 'VT_LPSTR', 'Title' ], [0x0003, 'VT_LPSTR', 'Subject' ], [0x0004, 'VT_LPSTR', 'Author' ], [0x0005, 'VT_LPSTR', 'Keywords'], ); $caption = " \tDoc properties: create_summary_property_set('+ Keywords')"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 7C 00 00 00 05 00 00 00 01 00 00 00 30 00 00 00 02 00 00 00 38 00 00 00 03 00 00 00 48 00 00 00 04 00 00 00 58 00 00 00 05 00 00 00 68 00 00 00 02 00 00 00 E4 04 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 1E 00 00 00 08 00 00 00 53 75 62 6A 65 63 74 00 1E 00 00 00 07 00 00 00 41 75 74 68 6F 72 00 00 1E 00 00 00 09 00 00 00 4B 65 79 77 6F 72 64 73 00 00 00 00 ); $result = unpack_record( create_summary_property_set(\@properties) ); is($result, $target, $caption); ############################################################################### # # Test 6. Same as previous + Comments. # @properties = ( [0x0001, 'VT_I2', 0x04E4 ], [0x0002, 'VT_LPSTR', 'Title' ], [0x0003, 'VT_LPSTR', 'Subject' ], [0x0004, 'VT_LPSTR', 'Author' ], [0x0005, 'VT_LPSTR', 'Keywords'], [0x0006, 'VT_LPSTR', 'Comments'], ); $caption = " \tDoc properties: create_summary_property_set('+ Comments')"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 98 00 00 00 06 00 00 00 01 00 00 00 38 00 00 00 02 00 00 00 40 00 00 00 03 00 00 00 50 00 00 00 04 00 00 00 60 00 00 00 05 00 00 00 70 00 00 00 06 00 00 00 84 00 00 00 02 00 00 00 E4 04 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 1E 00 00 00 08 00 00 00 53 75 62 6A 65 63 74 00 1E 00 00 00 07 00 00 00 41 75 74 68 6F 72 00 00 1E 00 00 00 09 00 00 00 4B 65 79 77 6F 72 64 73 00 00 00 00 1E 00 00 00 09 00 00 00 43 6F 6D 6D 65 6E 74 73 00 00 00 00 ); $result = unpack_record( create_summary_property_set(\@properties) ); is($result, $target, $caption); ############################################################################### # # Test 7. Same as previous + Last author. # @properties = ( [0x0001, 'VT_I2', 0x04E4 ], [0x0002, 'VT_LPSTR', 'Title' ], [0x0003, 'VT_LPSTR', 'Subject' ], [0x0004, 'VT_LPSTR', 'Author' ], [0x0005, 'VT_LPSTR', 'Keywords'], [0x0006, 'VT_LPSTR', 'Comments'], [0x0008, 'VT_LPSTR', 'Username'], ); $caption = " \tDoc properties: create_summary_property_set('+ Last author')"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 B4 00 00 00 07 00 00 00 01 00 00 00 40 00 00 00 02 00 00 00 48 00 00 00 03 00 00 00 58 00 00 00 04 00 00 00 68 00 00 00 05 00 00 00 78 00 00 00 06 00 00 00 8C 00 00 00 08 00 00 00 A0 00 00 00 02 00 00 00 E4 04 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 1E 00 00 00 08 00 00 00 53 75 62 6A 65 63 74 00 1E 00 00 00 07 00 00 00 41 75 74 68 6F 72 00 00 1E 00 00 00 09 00 00 00 4B 65 79 77 6F 72 64 73 00 00 00 00 1E 00 00 00 09 00 00 00 43 6F 6D 6D 65 6E 74 73 00 00 00 00 1E 00 00 00 09 00 00 00 55 73 65 72 6E 61 6D 65 00 00 00 00 ); $result = unpack_record( create_summary_property_set(\@properties) ); is($result, $target, $caption); ############################################################################### # # Test 8. Same as previous + Creation date. # # Wed Aug 20 00:20:13 2008 # $sec,$min,$hour,$mday,$mon,$year # We normalise the time using timegm() so that the tests don't fail due to # different timezones. $filetime = [localtime(timegm(13, 20, 23, 19, 7, 108))]; @properties = ( [0x0001, 'VT_I2', 0x04E4 ], [0x0002, 'VT_LPSTR', 'Title' ], [0x0003, 'VT_LPSTR', 'Subject' ], [0x0004, 'VT_LPSTR', 'Author' ], [0x0005, 'VT_LPSTR', 'Keywords'], [0x0006, 'VT_LPSTR', 'Comments'], [0x0008, 'VT_LPSTR', 'Username'], [0x000C, 'VT_FILETIME', $filetime ], ); $caption = " \tDoc properties: create_summary_property_set('+ Creation date')"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 C8 00 00 00 08 00 00 00 01 00 00 00 48 00 00 00 02 00 00 00 50 00 00 00 03 00 00 00 60 00 00 00 04 00 00 00 70 00 00 00 05 00 00 00 80 00 00 00 06 00 00 00 94 00 00 00 08 00 00 00 A8 00 00 00 0C 00 00 00 BC 00 00 00 02 00 00 00 E4 04 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 1E 00 00 00 08 00 00 00 53 75 62 6A 65 63 74 00 1E 00 00 00 07 00 00 00 41 75 74 68 6F 72 00 00 1E 00 00 00 09 00 00 00 4B 65 79 77 6F 72 64 73 00 00 00 00 1E 00 00 00 09 00 00 00 43 6F 6D 6D 65 6E 74 73 00 00 00 00 1E 00 00 00 09 00 00 00 55 73 65 72 6E 61 6D 65 00 00 00 00 40 00 00 00 80 74 89 21 52 02 C9 01 ); $result = unpack_record( create_summary_property_set(\@properties) ); is($result, $target, $caption); ############################################################################### # # Unpack the binary data into a format suitable for printing in tests. # sub unpack_record { return join ' ', map {sprintf "%02X", $_} unpack "C*", $_[0]; } __END__ Spreadsheet-WriteExcel-2.40/t/42_set_properties.t000644 000765 000024 00000056473 11304413104 022032 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ############################################################################### # # Testcases for Spreadsheet::WriteExcel. # # Tests for Workbook property_sets() interface. # # reverse(''), Auguest 2008, John McNamara, jmcnamara@cpan.org # use strict; use Carp; use Spreadsheet::WriteExcel; use Spreadsheet::WriteExcel::Properties ':testing'; use Time::Local 'timegm'; use Test::More tests => 17; ############################################################################### # # Tests setup # my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); my $target; my $result; my $caption; my $string; my $codepage; my $smiley = chr 0x263A; my $filetime; my @properties; my %params; my @strings; ############################################################################### # # Test 1. _get_property_set_codepage() for default latin1 strings. # %params = ( title => 'Title', subject => 'Subject', author => 'Author', keywords => 'Keywords', comments => 'Comments', last_author => 'Username', ); @strings = qw(title subject author keywords comments last_author); $caption = " \t_get_property_set_codepage('latin1')"; $target = 0x04E4; $result = $workbook->_get_property_set_codepage(\%params, \@strings); is($result, $target, $caption); ############################################################################### # # Test 2. _get_property_set_codepage() for manual utf8 strings. # %params = ( title => 'Title', subject => 'Subject', author => 'Author', keywords => 'Keywords', comments => 'Comments', last_author => 'Username', utf8 => 1, ); @strings = qw(title subject author keywords comments last_author); $caption = " \t_get_property_set_codepage('utf8')"; $target = 0xFDE9; $result = $workbook->_get_property_set_codepage(\%params, \@strings); is($result, $target, $caption); ############################################################################### # # Test 3. _get_property_set_codepage() for perl 5.8 utf8 strings. # SKIP: { skip " \t_get_property_set_codepage('utf8'). Requires Perl 5.8 Unicode.", 1 if $] < 5.008; %params = ( title => 'Title' . $smiley, subject => 'Subject', author => 'Author', keywords => 'Keywords', comments => 'Comments', last_author => 'Username', ); @strings = qw(title subject author keywords comments last_author); $caption = " \t_get_property_set_codepage('utf8')"; $target = 0xFDE9; $result = $workbook->_get_property_set_codepage(\%params, \@strings); is($result, $target, $caption); } ############################################################################### # # Note, the "created => undef" parameters in some of the following tests is # used to avoid adding the default date to the property sets. ############################################################################### # # Test 4. Codepage only. # $workbook->set_properties( created => undef, ); $caption = " \tset_properties(codepage)"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 18 00 00 00 01 00 00 00 01 00 00 00 10 00 00 00 02 00 00 00 E4 04 00 00 ); $result = unpack_record( $workbook->{summary} ); is($result, $target, $caption); ############################################################################### # # Test 5. Same as previous + Title. # $workbook->set_properties( title => 'Title', created => undef, ); $caption = " \tset_properties('Title')"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 30 00 00 00 02 00 00 00 01 00 00 00 18 00 00 00 02 00 00 00 20 00 00 00 02 00 00 00 E4 04 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 ); $result = unpack_record( $workbook->{summary} ); is($result, $target, $caption); ############################################################################### # # Test 6. Same as previous + Subject. # $workbook->set_properties( title => 'Title', subject => 'Subject', created => undef, ); $caption = " \tset_properties('+ Subject')"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 48 00 00 00 03 00 00 00 01 00 00 00 20 00 00 00 02 00 00 00 28 00 00 00 03 00 00 00 38 00 00 00 02 00 00 00 E4 04 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 1E 00 00 00 08 00 00 00 53 75 62 6A 65 63 74 00 ); $result = unpack_record( $workbook->{summary} ); is($result, $target, $caption); ############################################################################### # # Test 7. Same as previous + Author. # $workbook->set_properties( title => 'Title', subject => 'Subject', author => 'Author', created => undef, ); $caption = " \tset_properties('+ Author')"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 60 00 00 00 04 00 00 00 01 00 00 00 28 00 00 00 02 00 00 00 30 00 00 00 03 00 00 00 40 00 00 00 04 00 00 00 50 00 00 00 02 00 00 00 E4 04 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 1E 00 00 00 08 00 00 00 53 75 62 6A 65 63 74 00 1E 00 00 00 07 00 00 00 41 75 74 68 6F 72 00 00 ); $result = unpack_record( $workbook->{summary} ); is($result, $target, $caption); ############################################################################### # # Test 8. Same as previous + Keywords. # $workbook->set_properties( title => 'Title', subject => 'Subject', author => 'Author', keywords => 'Keywords', created => undef, ); $caption = " \tset_properties('+ Keywords')"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 7C 00 00 00 05 00 00 00 01 00 00 00 30 00 00 00 02 00 00 00 38 00 00 00 03 00 00 00 48 00 00 00 04 00 00 00 58 00 00 00 05 00 00 00 68 00 00 00 02 00 00 00 E4 04 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 1E 00 00 00 08 00 00 00 53 75 62 6A 65 63 74 00 1E 00 00 00 07 00 00 00 41 75 74 68 6F 72 00 00 1E 00 00 00 09 00 00 00 4B 65 79 77 6F 72 64 73 00 00 00 00 ); $result = unpack_record( $workbook->{summary} ); is($result, $target, $caption); ############################################################################### # # Test 9. Same as previous + Comments. # $workbook->set_properties( title => 'Title', subject => 'Subject', author => 'Author', keywords => 'Keywords', comments => 'Comments', created => undef, ); $caption = " \tset_properties('+ Comments')"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 98 00 00 00 06 00 00 00 01 00 00 00 38 00 00 00 02 00 00 00 40 00 00 00 03 00 00 00 50 00 00 00 04 00 00 00 60 00 00 00 05 00 00 00 70 00 00 00 06 00 00 00 84 00 00 00 02 00 00 00 E4 04 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 1E 00 00 00 08 00 00 00 53 75 62 6A 65 63 74 00 1E 00 00 00 07 00 00 00 41 75 74 68 6F 72 00 00 1E 00 00 00 09 00 00 00 4B 65 79 77 6F 72 64 73 00 00 00 00 1E 00 00 00 09 00 00 00 43 6F 6D 6D 65 6E 74 73 00 00 00 00 ); $result = unpack_record( $workbook->{summary} ); is($result, $target, $caption); ############################################################################### # # Test 10. Same as previous + Last author. # $workbook->set_properties( title => 'Title', subject => 'Subject', author => 'Author', keywords => 'Keywords', comments => 'Comments', last_author => 'Username', created => undef, ); $caption = " \tset_properties('+ Last author')"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 B4 00 00 00 07 00 00 00 01 00 00 00 40 00 00 00 02 00 00 00 48 00 00 00 03 00 00 00 58 00 00 00 04 00 00 00 68 00 00 00 05 00 00 00 78 00 00 00 06 00 00 00 8C 00 00 00 08 00 00 00 A0 00 00 00 02 00 00 00 E4 04 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 1E 00 00 00 08 00 00 00 53 75 62 6A 65 63 74 00 1E 00 00 00 07 00 00 00 41 75 74 68 6F 72 00 00 1E 00 00 00 09 00 00 00 4B 65 79 77 6F 72 64 73 00 00 00 00 1E 00 00 00 09 00 00 00 43 6F 6D 6D 65 6E 74 73 00 00 00 00 1E 00 00 00 09 00 00 00 55 73 65 72 6E 61 6D 65 00 00 00 00 ); $result = unpack_record( $workbook->{summary} ); is($result, $target, $caption); ############################################################################### # # Test 11. Same as previous + Creation date. # # Wed Aug 20 00:20:13 2008 # $sec,$min,$hour,$mday,$mon,$year # We normalise the time using timegm() so that the tests don't fail due to # different timezones. $filetime = [localtime(timegm(13, 20, 23, 19, 7, 108))]; $workbook->set_properties( title => 'Title', subject => 'Subject', author => 'Author', keywords => 'Keywords', comments => 'Comments', last_author => 'Username', created => $filetime, ); $caption = " \tset_properties('+ Creation date')"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 C8 00 00 00 08 00 00 00 01 00 00 00 48 00 00 00 02 00 00 00 50 00 00 00 03 00 00 00 60 00 00 00 04 00 00 00 70 00 00 00 05 00 00 00 80 00 00 00 06 00 00 00 94 00 00 00 08 00 00 00 A8 00 00 00 0C 00 00 00 BC 00 00 00 02 00 00 00 E4 04 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 1E 00 00 00 08 00 00 00 53 75 62 6A 65 63 74 00 1E 00 00 00 07 00 00 00 41 75 74 68 6F 72 00 00 1E 00 00 00 09 00 00 00 4B 65 79 77 6F 72 64 73 00 00 00 00 1E 00 00 00 09 00 00 00 43 6F 6D 6D 65 6E 74 73 00 00 00 00 1E 00 00 00 09 00 00 00 55 73 65 72 6E 61 6D 65 00 00 00 00 40 00 00 00 80 74 89 21 52 02 C9 01 ); $result = unpack_record( $workbook->{summary} ); is($result, $target, $caption); ############################################################################### # # Test 12. Same as previous. Date set at the workbook level. # # Wed Aug 20 00:20:13 2008 # $sec,$min,$hour,$mday,$mon,$year # We normalise the time using timegm() so that the tests don't fail due to # different timezones. $workbook->{_localtime} = [localtime(timegm(13, 20, 23, 19, 7, 108))]; $workbook->set_properties( title => 'Title', subject => 'Subject', author => 'Author', keywords => 'Keywords', comments => 'Comments', last_author => 'Username', ); $caption = " \tset_properties('+ Creation date')"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 C8 00 00 00 08 00 00 00 01 00 00 00 48 00 00 00 02 00 00 00 50 00 00 00 03 00 00 00 60 00 00 00 04 00 00 00 70 00 00 00 05 00 00 00 80 00 00 00 06 00 00 00 94 00 00 00 08 00 00 00 A8 00 00 00 0C 00 00 00 BC 00 00 00 02 00 00 00 E4 04 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 1E 00 00 00 08 00 00 00 53 75 62 6A 65 63 74 00 1E 00 00 00 07 00 00 00 41 75 74 68 6F 72 00 00 1E 00 00 00 09 00 00 00 4B 65 79 77 6F 72 64 73 00 00 00 00 1E 00 00 00 09 00 00 00 43 6F 6D 6D 65 6E 74 73 00 00 00 00 1E 00 00 00 09 00 00 00 55 73 65 72 6E 61 6D 65 00 00 00 00 40 00 00 00 80 74 89 21 52 02 C9 01 ); $result = unpack_record( $workbook->{summary} ); is($result, $target, $caption); ############################################################################### # # Test 13. Same as 11 but params passed as a hashref. # # Wed Aug 20 00:20:13 2008 # $sec,$min,$hour,$mday,$mon,$year # We normalise the time using timegm() so that the tests don't fail due to # different timezones. $filetime = [localtime(timegm(13, 20, 23, 19, 7, 108))]; $workbook->set_properties({ title => 'Title', subject => 'Subject', author => 'Author', keywords => 'Keywords', comments => 'Comments', last_author => 'Username', created => $filetime, }); $caption = " \tset_properties({hash})"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 C8 00 00 00 08 00 00 00 01 00 00 00 48 00 00 00 02 00 00 00 50 00 00 00 03 00 00 00 60 00 00 00 04 00 00 00 70 00 00 00 05 00 00 00 80 00 00 00 06 00 00 00 94 00 00 00 08 00 00 00 A8 00 00 00 0C 00 00 00 BC 00 00 00 02 00 00 00 E4 04 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 1E 00 00 00 08 00 00 00 53 75 62 6A 65 63 74 00 1E 00 00 00 07 00 00 00 41 75 74 68 6F 72 00 00 1E 00 00 00 09 00 00 00 4B 65 79 77 6F 72 64 73 00 00 00 00 1E 00 00 00 09 00 00 00 43 6F 6D 6D 65 6E 74 73 00 00 00 00 1E 00 00 00 09 00 00 00 55 73 65 72 6E 61 6D 65 00 00 00 00 40 00 00 00 80 74 89 21 52 02 C9 01 ); $result = unpack_record( $workbook->{summary} ); is($result, $target, $caption); ############################################################################### # # Test 14. UTF-8 string used. # SKIP: { skip " \tset_properties(utf8). Test requires Perl 5.8 Unicode support.", 1 if $] < 5.008; $workbook->set_properties( title => 'Title' . $smiley, created => undef, ); $caption = " \tset_properties(utf8)"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 34 00 00 00 02 00 00 00 01 00 00 00 18 00 00 00 02 00 00 00 20 00 00 00 02 00 00 00 E9 FD 00 00 1E 00 00 00 09 00 00 00 54 69 74 6C 65 E2 98 BA 00 00 00 00 ); $result = unpack_record( $workbook->{summary} ); is($result, $target, $caption); } ############################################################################### # # Test 15. Manual UTF-8 string used.. # my $smiley_manual = pack 'H*', 'E298BA'; $workbook->set_properties( title => 'Title' . $smiley_manual, subject => 'Subject', created => undef, utf8 => 1, ); $caption = " \tset_properties(utf8)"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 4C 00 00 00 03 00 00 00 01 00 00 00 20 00 00 00 02 00 00 00 28 00 00 00 03 00 00 00 3C 00 00 00 02 00 00 00 E9 FD 00 00 1E 00 00 00 09 00 00 00 54 69 74 6C 65 E2 98 BA 00 00 00 00 1E 00 00 00 08 00 00 00 53 75 62 6A 65 63 74 00 ); $result = unpack_record( $workbook->{summary} ); is($result, $target, $caption); ############################################################################### # # Test 16. UTF-8 string used. # SKIP: { skip " \tset_properties(utf8). Test requires Perl 5.8 Unicode support.", 1 if $] < 5.008; $workbook->set_properties( title => 'Title' . $smiley, subject => 'Subject', created => undef, ); $caption = " \tset_properties(utf8)"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 4C 00 00 00 03 00 00 00 01 00 00 00 20 00 00 00 02 00 00 00 28 00 00 00 03 00 00 00 3C 00 00 00 02 00 00 00 E9 FD 00 00 1E 00 00 00 09 00 00 00 54 69 74 6C 65 E2 98 BA 00 00 00 00 1E 00 00 00 08 00 00 00 53 75 62 6A 65 63 74 00 ); $result = unpack_record( $workbook->{summary} ); is($result, $target, $caption); } ############################################################################### # # Test 17. UTF-8 string used. # SKIP: { skip " \tset_properties(utf8). Test requires Perl 5.8 Unicode support.", 1 if $] < 5.008; $workbook->set_properties( title => 'Title', subject => 'Subject' . $smiley, created => undef, ); $caption = " \tset_properties(utf8)"; $target = join " ", qw( FE FF 00 00 05 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 E0 85 9F F2 F9 4F 68 10 AB 91 08 00 2B 27 B3 D9 30 00 00 00 4C 00 00 00 03 00 00 00 01 00 00 00 20 00 00 00 02 00 00 00 28 00 00 00 03 00 00 00 38 00 00 00 02 00 00 00 E9 FD 00 00 1E 00 00 00 06 00 00 00 54 69 74 6C 65 00 00 00 1E 00 00 00 0B 00 00 00 53 75 62 6A 65 63 74 E2 98 BA 00 00 ); $result = unpack_record( $workbook->{summary} ); is($result, $target, $caption); } ############################################################################### # # Unpack the binary data into a format suitable for printing in tests. # sub unpack_record { return join ' ', map {sprintf "%02X", $_} unpack "C*", $_[0]; } # Cleanup $workbook->close(); unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/50_name_stored.t000644 000765 000024 00000025517 11304413104 021255 0ustar00Johnstaff000000 000000 ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for the Excel EXTERNSHEET and NAME records created by print_are().. # # reverse(''), September 2008, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 8; #use Test::More 'no_plan'; ############################################################################### # # Tests setup # my $test_file = "temp_test_file.xls"; my $workbook = Spreadsheet::WriteExcel->new($test_file); my $worksheet = $workbook->add_worksheet(); my $target; my $result; my $caption; my $name; my $encoding; my $sheet_index; my $formula; ############################################################################### # # Test 1. Test for print_area() NAME with simple range. # $caption = " \tNAME for \$worksheet1->print_area('A1:B12')"; $name = pack 'C', 0x06; $encoding = 0; $sheet_index = 1; $formula = pack 'H*', '3B000000000B0000000100'; $result = $workbook->_store_name( $name, $encoding, $sheet_index, $formula ); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 06 3B 00 00 00 00 0B 00 00 00 01 00 ); $result = _unpack_name($result); $target = _unpack_name($target); is_deeply($result, $target, $caption); ############################################################################### # # Test 2. Test for print_area() NAME with simple range in sheet 3. # $caption = " \tNAME for \$worksheet3->print_area('G7:H8')"; $name = pack 'C', 0x06; $encoding = 0; $sheet_index = 3; $formula = pack 'H*', '3B02000600070006000700'; $result = $workbook->_store_name( $name, $encoding, $sheet_index, $formula ); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 03 00 00 00 00 00 00 06 3B 02 00 06 00 07 00 06 00 07 00 ); $result = _unpack_name($result); $target = _unpack_name($target); is_deeply($result, $target, $caption); ############################################################################### # # Test 3. Test for repeat_rows() NAME. # $caption = " \tNAME for \$worksheet1->repeat_rows(0, 9)"; $name = pack 'C', 0x07; $encoding = 0; $sheet_index = 1; $formula = pack 'H*', '3B0000000009000000FF00'; $result = $workbook->_store_name( $name, $encoding, $sheet_index, $formula ); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 07 3B 00 00 00 00 09 00 00 00 FF 00 ); $result = _unpack_name($result); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Test 4. Test for repeat_rows() NAME on sheet 3. # $caption = " \tNAME for \$worksheet3->repeat_rows(6, 7)"; $name = pack 'C', 0x07; $encoding = 0; $sheet_index = 3; $formula = pack 'H*', '3B0200060007000000FF00'; $result = $workbook->_store_name( $name, $encoding, $sheet_index, $formula ); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 03 00 00 00 00 00 00 07 3B 02 00 06 00 07 00 00 00 FF 00 ); $result = _unpack_name($result); $target = _unpack_name($target); is_deeply($result, $target, $caption); ############################################################################### # # Test 5. Test for repeat_columns() NAME. # $caption = " \tNAME for \$worksheet1->repeat_columns('A:J')"; $name = pack 'C', 0x07; $encoding = 0; $sheet_index = 1; $formula = pack 'H*', '3B00000000FFFF00000900'; $result = $workbook->_store_name( $name, $encoding, $sheet_index, $formula ); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 07 3B 00 00 00 00 FF FF 00 00 09 00 ); $result = _unpack_name($result); $target = _unpack_name($target); is_deeply($result, $target, $caption); ############################################################################### # # Test 6. Test for repeat_rows() and repeat_columns() together NAME. # $caption = " \tNAME for repeat_rows(1, 2) repeat_columns(3, 4)"; $name = pack 'C', 0x07; $encoding = 0; $sheet_index = 1; $formula = pack 'H*', '2917003B00000000FFFF030004003B0000010002000000FF0010'; $result = $workbook->_store_name( $name, $encoding, $sheet_index, $formula ); $target = pack 'H*', join '', qw( 18 00 2A 00 20 00 00 01 1A 00 00 00 01 00 00 00 00 00 00 07 29 17 00 3B 00 00 00 00 FF FF 03 00 04 00 3B 00 00 01 00 02 00 00 00 FF 00 10 ); $result = _unpack_name($result); $target = _unpack_name($target); is_deeply($result, $target, $caption); ############################################################################### # # Test 7. Test for print_area() NAME with simple range. # $caption = " \tNAME for \$worksheet1->autofilter('A1:C5');"; $name = pack 'C', 0x0D; $encoding = 0; $sheet_index = 1; $formula = pack 'H*', '3B00000000040000000200'; $result = $workbook->_store_name( $name, $encoding, $sheet_index, $formula ); $target = pack 'H*', join '', qw( 18 00 1B 00 21 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 0D 3B 00 00 00 00 04 00 00 00 02 00 ); $result = _unpack_name($result); $target = _unpack_name($target); is_deeply($result, $target, $caption); ############################################################################### # # Test 8. Test for define_name() global NAME. # $caption = " \tNAME for \$worksheet1->define_name('Foo', ...);"; $name = 'Foo'; $encoding = 0; $sheet_index = 0; $formula = pack 'H*', '3A000007000100'; $result = $workbook->_store_name( $name, $encoding, $sheet_index, $formula ); $target = pack 'H*', join '', qw( 18 00 19 00 00 00 00 03 07 00 00 00 00 00 00 00 00 00 00 46 6F 6F 3A 00 00 07 00 01 00 ); $result = _unpack_name($result); $target = _unpack_name($target); is_deeply($result, $target, $caption); ############################################################################### # # Helper functions. # ############################################################################### ############################################################################### # # _unpack_name() # # Unpack 1 or more NAME structures into a AoH for easier comparison. # sub _unpack_name { my $data = shift; my @names; while ($data) { my %name; $name{record} = unpack 'v', substr($data, 0, 2, ''); $name{length} = unpack 'v', substr($data, 0, 2, ''); $name{flags} = unpack 'v', substr($data, 0, 2, ''); $name{shortcut} = unpack 'C', substr($data, 0, 1, ''); $name{str_len} = unpack 'C', substr($data, 0, 1, ''); $name{formula_len} = unpack 'v', substr($data, 0, 2, ''); $name{itals} = unpack 'v', substr($data, 0, 2, ''); $name{sheet_index} = unpack 'v', substr($data, 0, 2, ''); $name{menu_len} = unpack 'C', substr($data, 0, 1, ''); $name{desc_len} = unpack 'C', substr($data, 0, 1, ''); $name{help_len} = unpack 'C', substr($data, 0, 1, ''); $name{status_len} = unpack 'C', substr($data, 0, 1, ''); $name{encoding} = unpack 'C', substr($data, 0, 1, ''); # Decode the individual flag fields. my %flag; $flag{hidden} = $name{flags} & 0x0001; $flag{function} = $name{flags} & 0x0002; $flag{vb} = $name{flags} & 0x0004; $flag{macro} = $name{flags} & 0x0008; $flag{complex} = $name{flags} & 0x0010; $flag{builtin} = $name{flags} & 0x0020; $flag{group} = $name{flags} & 0x0FC0; $flag{binary} = $name{flags} & 0x1000; $name{flags} = \%flag; # Decode the string part of the NAME structure. if ($name{encoding} == 1) { # UTF-16 name. Leave in hex. $name{string} = uc unpack 'H*', substr($data, 0, 2 * $name{str_len}, ''); } elsif ($flag{'builtin'}) { # 1 digit builtin name. Leave in hex. $name{string} = uc unpack 'H*', substr($data, 0, $name{str_len}, ''); } else { # ASCII name. $name{string} = pack 'C*', unpack 'C*', substr($data, 0, $name{str_len}, ''); } # Keep the formula as a hex string. $name{formula} = uc unpack 'H*', substr($data, 0, $name{formula_len}, ''); push @names, \%name; } return \@names; } # Cleanup $workbook->close(); unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/51_name_print_area.t000644 000765 000024 00000027623 11304413104 022102 0ustar00Johnstaff000000 000000 ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for the Excel EXTERNSHEET and NAME records created by print_are().. # # reverse(''), September 2008, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 12; #use Test::More 'no_plan'; ############################################################################### # # Tests setup # my $test_file = 'temp_test_file.xls'; my $workbook; my $worksheet1; my $worksheet2; my $worksheet3; my $worksheet4; my $worksheet5; my $target; my $result; my $caption; my $area; ############################################################################### # # Tests 1, 2. Test print_area() for a simple range. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $area = 'A1:B12'; $worksheet1->print_area($area); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 08 00 01 00 00 00 00 00 00 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 06 3B 00 00 00 00 0B 00 00 00 01 00 ); $caption = " \t+ Name ( Sheet1!$area )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Tests 3, 4. Test print_area() for a single row. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $area = 'A1:IV1'; $worksheet1->print_area($area); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 08 00 01 00 00 00 00 00 00 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 06 3B 00 00 00 00 00 00 00 00 FF 00 ); $caption = " \t+ Name ( Sheet1!$area )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Tests 5, 6. Test print_area() for a single column. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $area = 'A1:A65536'; $worksheet1->print_area($area); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 08 00 01 00 00 00 00 00 00 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 06 3B 00 00 00 00 FF FF 00 00 00 00 ); $caption = " \t+ Name ( Sheet1!$area )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Tests 7, 8. Test print_area() for multiple columns. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $area = 'A:H'; $worksheet1->print_area($area); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 08 00 01 00 00 00 00 00 00 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 06 3B 00 00 00 00 FF FF 00 00 07 00 ); $caption = " \t+ Name ( Sheet1!$area )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Tests 9, 10. Test ranges on multiple sheets. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); $worksheet3 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $worksheet1->print_area('A1:B2'); $worksheet2->print_area('D4:E5'); $worksheet3->print_area('G7:H8'); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 14 00 03 00 00 00 00 00 00 00 00 00 01 00 01 00 00 00 02 00 02 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 06 3B 00 00 00 00 01 00 00 00 01 00 18 00 1B 00 20 00 00 01 0B 00 00 00 02 00 00 00 00 00 00 06 3B 01 00 03 00 04 00 03 00 04 00 18 00 1B 00 20 00 00 01 0B 00 00 00 03 00 00 00 00 00 00 06 3B 02 00 06 00 07 00 06 00 07 00 ); $caption = " \t+ Name ( Sheet1!A1:B2, Sheet2!D4:E5, Sheet3!G7:H8 )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Tests 11, 12. Test ranges on multiple sheets, with sheets spaced out. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); $worksheet3 = $workbook->add_worksheet(); $worksheet4 = $workbook->add_worksheet(); $worksheet5 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $worksheet1->print_area('A1:B2'); $worksheet3->print_area('D4:E5'); $worksheet5->print_area('G7:H8'); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 14 00 03 00 00 00 00 00 00 00 00 00 02 00 02 00 00 00 04 00 04 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 06 3B 00 00 00 00 01 00 00 00 01 00 18 00 1B 00 20 00 00 01 0B 00 00 00 03 00 00 00 00 00 00 06 3B 01 00 03 00 04 00 03 00 04 00 18 00 1B 00 20 00 00 01 0B 00 00 00 05 00 00 00 00 00 00 06 3B 02 00 06 00 07 00 06 00 07 00 ); $caption = " \t+ Name ( Sheet1!A1:B2, Sheet3!D4:E5, Sheet5!G7:H8 )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Helper functions. # ############################################################################### ############################################################################### # # _unpack_externsheet() # # Unpack the EXTERNSHEET recordfor easier comparison. # sub _unpack_externsheet { my $data = shift; my %externsheet; $externsheet{record} = unpack 'v', substr($data, 0, 2, ''); $externsheet{length} = unpack 'v', substr($data, 0, 2, ''); $externsheet{count} = unpack 'v', substr($data, 0, 2, ''); $externsheet{array} = []; for (1 .. $externsheet{count}) { push @{$externsheet{array}}, [unpack 'vvv', substr($data, 0, 6, '')]; } return \%externsheet; } ############################################################################### # # _unpack_name() # # Unpack 1 or more NAME structures into a AoH for easier comparison. # sub _unpack_name { my $data = shift; my @names; while ($data) { my %name; $name{record} = unpack 'v', substr($data, 0, 2, ''); $name{length} = unpack 'v', substr($data, 0, 2, ''); $name{flags} = unpack 'v', substr($data, 0, 2, ''); $name{shortcut} = unpack 'C', substr($data, 0, 1, ''); $name{str_len} = unpack 'C', substr($data, 0, 1, ''); $name{formula_len} = unpack 'v', substr($data, 0, 2, ''); $name{itals} = unpack 'v', substr($data, 0, 2, ''); $name{sheet_index} = unpack 'v', substr($data, 0, 2, ''); $name{menu_len} = unpack 'C', substr($data, 0, 1, ''); $name{desc_len} = unpack 'C', substr($data, 0, 1, ''); $name{help_len} = unpack 'C', substr($data, 0, 1, ''); $name{status_len} = unpack 'C', substr($data, 0, 1, ''); $name{encoding} = unpack 'C', substr($data, 0, 1, ''); # Decode the individual flag fields. my %flag; $flag{hidden} = $name{flags} & 0x0001; $flag{function} = $name{flags} & 0x0002; $flag{vb} = $name{flags} & 0x0004; $flag{macro} = $name{flags} & 0x0008; $flag{complex} = $name{flags} & 0x0010; $flag{builtin} = $name{flags} & 0x0020; $flag{group} = $name{flags} & 0x0FC0; $flag{binary} = $name{flags} & 0x1000; $name{flags} = \%flag; # Decode the string part of the NAME structure. if ($name{encoding} == 1) { # UTF-16 name. Leave in hex. $name{string} = uc unpack 'H*', substr($data, 0, 2 * $name{str_len}, ''); } elsif ($flag{'builtin'}) { # 1 digit builtin name. Leave in hex. $name{string} = uc unpack 'H*', substr($data, 0, $name{str_len}, ''); } else { # ASCII name. $name{string} = pack 'C*', unpack 'C*', substr($data, 0, $name{str_len}, ''); } # Keep the formula as a hex string. $name{formula} = uc unpack 'H*', substr($data, 0, $name{formula_len}, ''); push @names, \%name; } return \@names; } # Cleanup unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/52_name_print_titles.t000644 000765 000024 00000036432 11304413104 022475 0ustar00Johnstaff000000 000000 ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for the Excel EXTERNSHEET and NAME records created by print_are().. # # reverse(''), September 2008, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 18; #use Test::More 'no_plan'; ############################################################################### # # Tests setup # my $test_file = 'temp_test_file.xls'; my $workbook; my $worksheet1; my $worksheet2; my $worksheet3; my $worksheet4; my $worksheet5; my $target; my $result; my $caption; ############################################################################### # # Tests 1, 2. Test repeat_rows() for top row only. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $worksheet1->repeat_rows(0); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 08 00 01 00 00 00 00 00 00 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 07 3B 00 00 00 00 00 00 00 00 FF 00 ); $caption = " \t+ Name repeats ( Sheet1!1:1 )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Tests 3, 4. Test repeat_rows() for top 10 rows. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $worksheet1->repeat_rows(0, 9); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 08 00 01 00 00 00 00 00 00 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 07 3B 00 00 00 00 09 00 00 00 FF 00 ); $caption = " \t+ Name repeats ( Sheet1!1:10 )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Tests 5, 6. Test repeat_columns() for a single column. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $worksheet1->repeat_columns(0); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 08 00 01 00 00 00 00 00 00 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 07 3B 00 00 00 00 FF FF 00 00 00 00 ); $caption = " \t+ Name repeats ( Sheet1!A:A )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Tests 7, 8. Test repeat_columns() for a single column, A1 notation. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $worksheet1->repeat_columns('A:A'); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 08 00 01 00 00 00 00 00 00 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 07 3B 00 00 00 00 FF FF 00 00 00 00 ); $caption = " \t+ Name repeats ( Sheet1!A:A )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Tests 9, 10. Test repeat_columns() for a 10 columns. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $worksheet1->repeat_columns(0, 9); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 08 00 01 00 00 00 00 00 00 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 07 3B 00 00 00 00 FF FF 00 00 09 00 ); $caption = " \t+ Name repeats ( Sheet1!A:J )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Tests 11, 12. Test repeat_columns() for a 10 columns. A1 notation. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $worksheet1->repeat_columns('A:J'); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 08 00 01 00 00 00 00 00 00 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 07 3B 00 00 00 00 FF FF 00 00 09 00 ); $caption = " \t+ Name repeats ( Sheet1!A:J )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Tests 13, 14. Test repeat_rows() on multiple sheets. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); $worksheet3 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $worksheet1->repeat_rows(0, 1); $worksheet2->repeat_rows(3, 4); $worksheet3->repeat_rows(6, 7); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 14 00 03 00 00 00 00 00 00 00 00 00 01 00 01 00 00 00 02 00 02 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 07 3B 00 00 00 00 01 00 00 00 FF 00 18 00 1B 00 20 00 00 01 0B 00 00 00 02 00 00 00 00 00 00 07 3B 01 00 03 00 04 00 00 00 FF 00 18 00 1B 00 20 00 00 01 0B 00 00 00 03 00 00 00 00 00 00 07 3B 02 00 06 00 07 00 00 00 FF 00 ); $caption = " \t+ Name repeats ( Sheet1!1:2, Sheet2!4:5, Sheet3!7:8 )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Tests 15, 16. Test repeat_rows() on multiple sheets, with sheets spaced out. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); $worksheet3 = $workbook->add_worksheet(); $worksheet4 = $workbook->add_worksheet(); $worksheet5 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $worksheet1->repeat_rows(0, 1); $worksheet3->repeat_rows(3, 4); $worksheet5->repeat_rows(6, 7); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 14 00 03 00 00 00 00 00 00 00 00 00 02 00 02 00 00 00 04 00 04 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 1B 00 20 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 07 3B 00 00 00 00 01 00 00 00 FF 00 18 00 1B 00 20 00 00 01 0B 00 00 00 03 00 00 00 00 00 00 07 3B 01 00 03 00 04 00 00 00 FF 00 18 00 1B 00 20 00 00 01 0B 00 00 00 05 00 00 00 00 00 00 07 3B 02 00 06 00 07 00 00 00 FF 00 ); $caption = " \t+ Name repeats ( Sheet1!1:2, Sheet2!4:5, Sheet3!7:8 )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Tests 17, 18. Test repeat_rows() and repeat_columns() together. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $worksheet1->repeat_rows (1, 2); $worksheet1->repeat_columns(3, 4); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 08 00 01 00 00 00 00 00 00 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 2A 00 20 00 00 01 1A 00 00 00 01 00 00 00 00 00 00 07 29 17 00 3B 00 00 00 00 FF FF 03 00 04 00 3B 00 00 01 00 02 00 00 00 FF 00 10 ); $caption = " \t+ Name repeats ( Sheet1!2:3, Sheet1!D:E )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Helper functions. # ############################################################################### ############################################################################### # # _unpack_externsheet() # # Unpack the EXTERNSHEET recordfor easier comparison. # sub _unpack_externsheet { my $data = shift; my %externsheet; $externsheet{record} = unpack 'v', substr($data, 0, 2, ''); $externsheet{length} = unpack 'v', substr($data, 0, 2, ''); $externsheet{count} = unpack 'v', substr($data, 0, 2, ''); $externsheet{array} = []; for (1 .. $externsheet{count}) { push @{$externsheet{array}}, [unpack 'vvv', substr($data, 0, 6, '')]; } return \%externsheet; } ############################################################################### # # _unpack_name() # # Unpack 1 or more NAME structures into a AoH for easier comparison. # sub _unpack_name { my $data = shift; my @names; while ($data) { my %name; $name{record} = unpack 'v', substr($data, 0, 2, ''); $name{length} = unpack 'v', substr($data, 0, 2, ''); $name{flags} = unpack 'v', substr($data, 0, 2, ''); $name{shortcut} = unpack 'C', substr($data, 0, 1, ''); $name{str_len} = unpack 'C', substr($data, 0, 1, ''); $name{formula_len} = unpack 'v', substr($data, 0, 2, ''); $name{itals} = unpack 'v', substr($data, 0, 2, ''); $name{sheet_index} = unpack 'v', substr($data, 0, 2, ''); $name{menu_len} = unpack 'C', substr($data, 0, 1, ''); $name{desc_len} = unpack 'C', substr($data, 0, 1, ''); $name{help_len} = unpack 'C', substr($data, 0, 1, ''); $name{status_len} = unpack 'C', substr($data, 0, 1, ''); $name{encoding} = unpack 'C', substr($data, 0, 1, ''); # Decode the individual flag fields. my %flag; $flag{hidden} = $name{flags} & 0x0001; $flag{function} = $name{flags} & 0x0002; $flag{vb} = $name{flags} & 0x0004; $flag{macro} = $name{flags} & 0x0008; $flag{complex} = $name{flags} & 0x0010; $flag{builtin} = $name{flags} & 0x0020; $flag{group} = $name{flags} & 0x0FC0; $flag{binary} = $name{flags} & 0x1000; $name{flags} = \%flag; # Decode the string part of the NAME structure. if ($name{encoding} == 1) { # UTF-16 name. Leave in hex. $name{string} = uc unpack 'H*', substr($data, 0, 2 * $name{str_len}, ''); } elsif ($flag{'builtin'}) { # 1 digit builtin name. Leave in hex. $name{string} = uc unpack 'H*', substr($data, 0, $name{str_len}, ''); } else { # ASCII name. $name{string} = pack 'C*', unpack 'C*', substr($data, 0, $name{str_len}, ''); } # Keep the formula as a hex string. $name{formula} = uc unpack 'H*', substr($data, 0, $name{formula_len}, ''); push @names, \%name; } return \@names; } # Cleanup unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/53_name_autofilter.t000644 000765 000024 00000014736 11304413104 022137 0ustar00Johnstaff000000 000000 ############################################################################### # # A test for Spreadsheet::WriteExcel. # # Tests for the Excel EXTERNSHEET and NAME records created by print_are().. # # reverse(''), September 2008, John McNamara, jmcnamara@cpan.org # use strict; use Spreadsheet::WriteExcel; use Test::More tests => 4; #use Test::More 'no_plan'; ############################################################################### # # Tests setup # my $test_file = 'temp_test_file.xls'; my $workbook; my $worksheet1; my $worksheet2; my $worksheet3; my $worksheet4; my $worksheet5; my $target; my $result; my $caption; my $area; # Delete ############################################################################### # # Tests 1, 2. Autofilter on one sheet. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $worksheet1->autofilter('A1:C5'); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 08 00 01 00 00 00 00 00 00 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 1B 00 21 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 0D 3B 00 00 00 00 04 00 00 00 02 00 ); $caption = " \t+ Name = autofilter ( Sheet1!A1:C5 )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Tests 3, 4. Autofilter on two sheets. # $workbook = Spreadsheet::WriteExcel->new($test_file); $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); $workbook->{_using_tmpfile} = 0; $worksheet1->autofilter('A1:C5'); $worksheet2->autofilter('A1:C5'); # Test the EXTERNSHEET record. $workbook->_calculate_extern_sizes(); $workbook->_store_externsheet(); $target = pack 'H*', join '', qw( 17 00 0E 00 02 00 00 00 00 00 00 00 00 00 01 00 01 00 ); $caption = " \tExternsheet"; $result = _unpack_externsheet($workbook->{_data}); $target = _unpack_externsheet($target); is_deeply($result, $target, $caption); # Test the NAME record. $workbook->{_data} = ''; $workbook->_store_names(); $target = pack 'H*', join '', qw( 18 00 1B 00 21 00 00 01 0B 00 00 00 01 00 00 00 00 00 00 0D 3B 00 00 00 00 04 00 00 00 02 00 18 00 1B 00 21 00 00 01 0B 00 00 00 02 00 00 00 00 00 00 0D 3B 01 00 00 00 04 00 00 00 02 00 ); $caption = " \t+ Name = autofilter ( Sheet1!A1:C5, Sheet2!A1:C5 )"; $result = _unpack_name($workbook->{_data}); $target = _unpack_name($target); is_deeply($result, $target, $caption); $workbook->close(); ############################################################################### # # Helper functions. # ############################################################################### ############################################################################### # # _unpack_externsheet() # # Unpack the EXTERNSHEET recordfor easier comparison. # sub _unpack_externsheet { my $data = shift; my %externsheet; $externsheet{record} = unpack 'v', substr($data, 0, 2, ''); $externsheet{length} = unpack 'v', substr($data, 0, 2, ''); $externsheet{count} = unpack 'v', substr($data, 0, 2, ''); $externsheet{array} = []; for (1 .. $externsheet{count}) { push @{$externsheet{array}}, [unpack 'vvv', substr($data, 0, 6, '')]; } return \%externsheet; } ############################################################################### # # _unpack_name() # # Unpack 1 or more NAME structures into a AoH for easier comparison. # sub _unpack_name { my $data = shift; my @names; while ($data) { my %name; $name{record} = unpack 'v', substr($data, 0, 2, ''); $name{length} = unpack 'v', substr($data, 0, 2, ''); $name{flags} = unpack 'v', substr($data, 0, 2, ''); $name{shortcut} = unpack 'C', substr($data, 0, 1, ''); $name{str_len} = unpack 'C', substr($data, 0, 1, ''); $name{formula_len} = unpack 'v', substr($data, 0, 2, ''); $name{itals} = unpack 'v', substr($data, 0, 2, ''); $name{sheet_index} = unpack 'v', substr($data, 0, 2, ''); $name{menu_len} = unpack 'C', substr($data, 0, 1, ''); $name{desc_len} = unpack 'C', substr($data, 0, 1, ''); $name{help_len} = unpack 'C', substr($data, 0, 1, ''); $name{status_len} = unpack 'C', substr($data, 0, 1, ''); $name{encoding} = unpack 'C', substr($data, 0, 1, ''); # Decode the individual flag fields. my %flag; $flag{hidden} = $name{flags} & 0x0001; $flag{function} = $name{flags} & 0x0002; $flag{vb} = $name{flags} & 0x0004; $flag{macro} = $name{flags} & 0x0008; $flag{complex} = $name{flags} & 0x0010; $flag{builtin} = $name{flags} & 0x0020; $flag{group} = $name{flags} & 0x0FC0; $flag{binary} = $name{flags} & 0x1000; $name{flags} = \%flag; # Decode the string part of the NAME structure. if ($name{encoding} == 1) { # UTF-16 name. Leave in hex. $name{string} = uc unpack 'H*', substr($data, 0, 2 * $name{str_len}, ''); } elsif ($flag{'builtin'}) { # 1 digit builtin name. Leave in hex. $name{string} = uc unpack 'H*', substr($data, 0, $name{str_len}, ''); } else { # ASCII name. $name{string} = pack 'C*', unpack 'C*', substr($data, 0, $name{str_len}, ''); } # Keep the formula as a hex string. $name{formula} = uc unpack 'H*', substr($data, 0, $name{formula_len}, ''); push @names, \%name; } return \@names; } # Cleanup unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/60_chart_generic.t000644 000765 000024 00000036070 11323473525 021565 0ustar00Johnstaff000000 000000 ############################################################################### # # A test for Spreadsheet::WriteExcel::Chart. # # Tests for the Excel Chart.pm methods. # # reverse(''), December 2009, John McNamara, jmcnamara@cpan.org # # prove -I../lib --nocolor -v 60_chart_generic.t use strict; use Spreadsheet::WriteExcel::Chart; use Test::More tests => 41; ############################################################################### # # Tests setup # my $chart = Spreadsheet::WriteExcel::Chart->new(); my $got; my $expected; my $caption; my $string; my @values; ############################################################################### # # Test the _store_fbi method. # $caption = " \tChart: _store_fbi()"; $expected = join ' ', qw( 60 10 0A 00 B8 38 A1 22 C8 00 00 00 05 00 ); $got = unpack_record( $chart->_store_fbi( 5, 10, 0x38B8, 0x22A1, 0x0000 ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_fbi method. # $expected = join ' ', qw( 60 10 0A 00 B8 38 A1 22 C8 00 00 00 06 00 ); $got = unpack_record( $chart->_store_fbi( 6, 10, 0x38B8, 0x22A1, 0x0000 ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_chart method. # $caption = " \tChart: _store_chart()"; $expected = join ' ', qw( 02 10 10 00 00 00 00 00 00 00 00 00 E0 51 DD 02 38 B8 C2 01 ); @values = ( 0x0000, 0x0000, 0x02DD51E0, 0x01C2B838 ); $got = unpack_record( $chart->_store_chart( @values ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_series() method. # $caption = " \tChart: _store_series()"; $expected = join ' ', qw( 03 10 0C 00 01 00 01 00 08 00 08 00 01 00 00 00 ); $got = unpack_record( $chart->_store_series( 8, 8 ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_begin() method. # $caption = " \tChart: _store_begin()"; $expected = join ' ', qw( 33 10 00 00 ); $got = unpack_record( $chart->_store_begin() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_end() method. # $caption = " \tChart: _store_end()"; $expected = join ' ', qw( 34 10 00 00 ); $got = unpack_record( $chart->_store_end() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_ai() method. # $caption = " \tChart: _store_ai()"; @values = ( 0, 1, '' ); $expected = join ' ', qw( 51 10 08 00 00 01 00 00 00 00 00 00 ); $got = unpack_record( $chart->_store_ai( @values ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_ai() method. # $caption = " \tChart: _store_ai()"; @values = ( 1, 2, pack 'H*', '3B00000000070000000000' ); $expected = join ' ', qw( 51 10 13 00 01 02 00 00 00 00 0B 00 3B 00 00 00 00 07 00 00 00 00 00 ); $got = unpack_record( $chart->_store_ai( @values ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_dataformat() method. # $caption = " \tChart: _store_dataformat()"; $expected = join ' ', qw( 06 10 08 00 FF FF 00 00 00 00 00 00 ); $got = unpack_record( $chart->_store_dataformat( 0, 0, 0xFFFF ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_dataformat() method. # $caption = " \tChart: _store_dataformat()"; $expected = join ' ', qw( 06 10 08 00 00 00 00 00 FD FF 00 00 ); $got = unpack_record( $chart->_store_dataformat( 0, 0xFFFD, 0 ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_3dbarshape() method. # $caption = " \tChart: _store_3dbarshape()"; $expected = join ' ', qw( 5F 10 02 00 00 00 ); $got = unpack_record( $chart->_store_3dbarshape() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_sertocrt() method. # $caption = " \tChart: _store_sertocrt()"; $expected = join ' ', qw( 45 10 02 00 00 00 ); $got = unpack_record( $chart->_store_sertocrt() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_shtprops() method. # $caption = " \tChart: _store_shtprops()"; $expected = join ' ', qw( 44 10 04 00 0E 00 00 00 ); $got = unpack_record( $chart->_store_shtprops() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_defaulttext() method. # $caption = " \tChart: _store_defaulttext()"; $expected = join ' ', qw( 24 10 02 00 02 00 ); $got = unpack_record( $chart->_store_defaulttext() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_charttext() method. # $caption = " \tChart: _store_charttext()"; $expected = join ' ', qw( 25 10 20 00 02 02 01 00 00 00 00 00 46 FF FF FF 06 FF FF FF 00 00 00 00 00 00 00 00 B1 00 4D 00 00 00 00 00 ); $got = unpack_record( $chart->_store_charttext() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_fontx() method. # $caption = " \tChart: _store_fontx()"; $expected = join ' ', qw( 26 10 02 00 05 00 ); $got = unpack_record( $chart->_store_fontx( 5 ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_axesused() method. # $caption = " \tChart: _store_axesused()"; $expected = join ' ', qw( 46 10 02 00 01 00 ); $got = unpack_record( $chart->_store_axesused( 1 ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_axisparent() method. # $caption = " \tChart: _store_axisparent()"; $expected = join ' ', qw( 41 10 12 00 00 00 F8 00 00 00 F5 01 00 00 7F 0E 00 00 36 0B 00 00 ); @values = ( 0, 0x00F8, 0x01F5, 0x0E7F, 0x0B36 ); $got = unpack_record( $chart->_store_axisparent( @values ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_axis() method. # $caption = " \tChart: _store_axis()"; $expected = join ' ', qw( 1D 10 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ); $got = unpack_record( $chart->_store_axis( 0 ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_catserrange() method. # $caption = " \tChart: _store_catserrange()"; $expected = join ' ', qw( 20 10 08 00 01 00 01 00 01 00 01 00 ); $got = unpack_record( $chart->_store_catserrange() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_axcext() method. # $caption = " \tChart: _store_axcext()"; $expected = join ' ', qw( 62 10 12 00 00 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 EF 00 ); $got = unpack_record( $chart->_store_axcext() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_tick() method. # $caption = " \tChart: _store_tick()"; $expected = join ' ', qw( 1E 10 1E 00 02 00 03 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 23 00 4D 00 00 00 ); $got = unpack_record( $chart->_store_tick() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_valuerange() method. # $caption = " \tChart: _store_valuerange()"; $expected = join ' ', qw( 1F 10 2A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1F 01 ); $got = unpack_record( $chart->_store_valuerange() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_axislineformat() method. # $caption = " \tChart: _store_axislineformat()"; $expected = join ' ', qw( 21 10 02 00 01 00 ); $got = unpack_record( $chart->_store_axislineformat() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_lineformat() method. # $caption = " \tChart: _store_lineformat()"; $expected = join ' ', qw( 07 10 0C 00 00 00 00 00 00 00 FF FF 09 00 4D 00 ); @values = ( 0x00000000, 0x0000, 0xFFFF, 0x0009, 0x004D ); $got = unpack_record( $chart->_store_lineformat( @values ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_frame() method. # $caption = " \tChart: _store_frame()"; $expected = join ' ', qw( 32 10 04 00 00 00 03 00 ); $got = unpack_record( $chart->_store_frame( 0x00, 0x03 ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_areaformat() method. # $caption = " \tChart: _store_areaformat()"; $expected = join ' ', qw( 0A 10 10 00 C0 C0 C0 00 00 00 00 00 01 00 00 00 16 00 4F 00 ); @values = ( 0x00C0C0C0, 0x00, 0x01, 0x00, 0x16, 0x4F ); $got = unpack_record( $chart->_store_areaformat( @values ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_chartformat() method. # $caption = " \tChart: _store_chartformat()"; $expected = join ' ', qw( 14 10 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ); $got = unpack_record( $chart->_store_chartformat() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_legend() method. # $caption = " \tChart: _store_legend()"; $expected = join ' ', qw( 15 10 14 00 F9 05 00 00 E9 0E 00 00 7D 04 00 00 9C 00 00 00 00 01 0F 00 ); @values = ( 0x05F9, 0x0EE9, 0x047D, 0x009C, 0x00, 0x01, 0x000F ); $got = unpack_record( $chart->_store_legend( @values ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_pos() method. # $caption = " \tChart: _store_pos()"; $expected = join ' ', qw( 4F 10 14 00 05 00 02 00 83 0E 00 00 F9 06 00 00 00 00 00 00 00 00 00 00 ); @values = ( 5, 2, 0x0E83, 0x06F9, 0, 0 ); $got = unpack_record( $chart->_store_pos( @values ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_text() method. # $caption = " \tChart: _store_text()"; $expected = join ' ', qw( 25 10 20 00 02 02 01 00 00 00 00 00 46 FF FF FF 06 FF FF FF 00 00 00 00 00 00 00 00 B1 00 4D 00 20 10 00 00 ); @values = ( 0xFFFFFF46, 0xFFFFFF06, 0, 0, 0x00B1, 0x1020 ); $got = unpack_record( $chart->_store_text( @values ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_plotgrowth() method. # $caption = " \tChart: _store_plotgrowth()"; $expected = join ' ', qw( 64 10 08 00 00 00 01 00 00 00 01 00 ); $got = unpack_record( $chart->_store_plotgrowth() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_seriestext() method. # $caption = " \tChart: _store_seriestext()"; $expected = join ' ', qw( 0D 10 14 00 00 00 10 00 4E 61 6D 65 20 66 6F 72 20 53 65 72 69 65 73 31 ); $string = 'Name for Series1'; $got = unpack_record( $chart->_store_seriestext( $string, 0 ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_seriestext() method. UTF-16 string. # $caption = " \tChart: _store_seriestext()"; $expected = join ' ', qw( 0D 10 24 00 00 00 10 01 4E 00 61 00 6D 00 65 00 20 00 66 00 6F 00 72 00 20 00 53 00 65 00 72 00 69 00 65 00 73 00 31 00 ); $string = pack 'n*', unpack 'C*', 'Name for Series1'; $got = unpack_record( $chart->_store_seriestext( $string, 1 ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_objectlink() method. # $caption = " \tChart: _store_objectlink()"; $expected = join ' ', qw( 27 10 06 00 01 00 00 00 00 00 ); $got = unpack_record( $chart->_store_objectlink( 1 ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_pieformat() method. # $caption = " \tChart: _store_pieformat()"; $expected = join ' ', qw( 0B 10 02 00 00 00 ); $got = unpack_record( $chart->_store_pieformat() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_markerformat() method. # $caption = " \tChart: _store_markerformat()"; $expected = join ' ', qw( 09 10 14 00 00 00 00 00 00 00 00 00 02 00 01 00 4D 00 4D 00 3C 00 00 00 ); @values = ( 0x00, 0x00, 0x02, 0x01, 0x4D, 0x4D, 0x3C ); $got = unpack_record( $chart->_store_markerformat( @values ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_dropbar() method. # $caption = " \tChart: _store_dropbar()"; $expected = join ' ', qw( 3D 10 02 00 96 00 ); $got = unpack_record( $chart->_store_dropbar() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_chartline() method. # $caption = " \tChart: _store_chartline()"; $expected = join ' ', qw( 1C 10 02 00 01 00 ); $got = unpack_record( $chart->_store_chartline() ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_serparent() method. # $caption = " \tChart: _store_serparent()"; $expected = join ' ', qw( 4A 10 02 00 01 00 ); $got = unpack_record( $chart->_store_serparent( 1 ) ); is( $got, $expected, $caption ); ############################################################################### # # Test the _store_serauxtrend() method # $caption = " \tChart: _store_serauxtrend()"; $expected = join ' ', qw( 4B 10 1C 00 00 01 FF FF FF FF 00 01 FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ); @values = ( 0x00, 0x01, 0x00, 0x00 ); $got = unpack_record( $chart->_store_serauxtrend( @values ) ); is( $got, $expected, $caption ); ############################################################################### # # Utility function used by the test suite. # # Unpack the binary data into a format suitable for printing in tests. # sub unpack_record { return join ' ', map { sprintf '%02X', $_ } unpack 'C*', shift; } __END__ Spreadsheet-WriteExcel-2.40/t/61_chart_subclasses.t000644 000765 000024 00000006733 11322471477 022327 0ustar00Johnstaff000000 000000 ############################################################################### # # A test for Spreadsheet::WriteExcel::Chart subclass methods. # # Tests for the Excel Chart.pm methods. # # reverse(''), December 2009, John McNamara, jmcnamara@cpan.org # # prove -I../lib --nocolor -v 61_chart_subclasses.t use strict; use Spreadsheet::WriteExcel::Chart; use Test::More tests => 7; ############################################################################### # # Tests setup # my $chart; my $got; my $expected; my $caption; ############################################################################### # # Test for overridden _store_chart_type() in ::Chart::Column.pm. # $chart = Spreadsheet::WriteExcel::Chart->factory( 'column' ); $caption = " \tChart: Column()"; $expected = join ' ', qw( 17 10 06 00 00 00 96 00 00 00 ); $got = unpack_record( $chart->_store_chart_type() ); is( $got, $expected, $caption ); ############################################################################### # # Test for overridden _store_chart_type() in ::Chart::Bar.pm. # $chart = Spreadsheet::WriteExcel::Chart->factory( 'bar' ); $caption = " \tChart: Bar()"; $expected = join ' ', qw( 17 10 06 00 00 00 96 00 01 00 ); $got = unpack_record( $chart->_store_chart_type() ); is( $got, $expected, $caption ); ############################################################################### # # Test for overridden _store_chart_type() in ::Chart::Line.pm. # $chart = Spreadsheet::WriteExcel::Chart->factory( 'line' ); $caption = " \tChart: Line()"; $expected = join ' ', qw( 18 10 02 00 00 00 ); $got = unpack_record( $chart->_store_chart_type() ); is( $got, $expected, $caption ); ############################################################################### # # Test for overridden _store_chart_type() in ::Chart::Area.pm. # $chart = Spreadsheet::WriteExcel::Chart->factory( 'area' ); $caption = " \tChart: Area()"; $expected = join ' ', qw( 1A 10 02 00 01 00 ); $got = unpack_record( $chart->_store_chart_type() ); is( $got, $expected, $caption ); ############################################################################### # # Test for overridden _store_chart_type() in ::Chart::Pie.pm. # $chart = Spreadsheet::WriteExcel::Chart->factory( 'pie' ); $caption = " \tChart: Pie()"; $expected = join ' ', qw( 19 10 06 00 00 00 00 00 02 00 ); $got = unpack_record( $chart->_store_chart_type() ); is( $got, $expected, $caption ); ############################################################################### # # Test for overridden _store_chart_type() in ::Chart::Scatter.pm. # $chart = Spreadsheet::WriteExcel::Chart->factory( 'scatter' ); $caption = " \tChart: Scatter()"; $expected = join ' ', qw( 1B 10 06 00 64 00 01 00 00 00 ); $got = unpack_record( $chart->_store_chart_type() ); is( $got, $expected, $caption ); ############################################################################### # # Test for overridden _store_chart_type() in ::Chart::Stock.pm. # $chart = Spreadsheet::WriteExcel::Chart->factory( 'stock' ); $caption = " \tChart: Stock()"; $expected = join ' ', qw( 18 10 02 00 00 00 ); $got = unpack_record( $chart->_store_chart_type() ); is( $got, $expected, $caption ); ############################################################################## # # Utility function used by the test suite. # # Unpack the binary data into a format suitable for printing in tests. # sub unpack_record { return join ' ', map { sprintf '%02X', $_ } unpack 'C*', shift; } __END__ Spreadsheet-WriteExcel-2.40/t/62_chart_formats.t000644 000765 000024 00000016072 11331071357 021622 0ustar00Johnstaff000000 000000 ############################################################################### # # A test for Spreadsheet::WriteExcel::Chart. # # Tests for the Excel Chart.pm format conversion methods. # # reverse(''), January 2010, John McNamara, jmcnamara@cpan.org # # prove -I../lib --nocolor -v 62_chart_formats.t use strict; use Spreadsheet::WriteExcel; use Test::More tests => 20; #use Test::More 'no_plan'; ############################################################################### # # Tests setup # my $test_file = 'temp_test_file.xls'; my $workbook = Spreadsheet::WriteExcel->new( $test_file ); my $chart = $workbook->add_chart( type => 'column' ); my %values; my $color; my $got; my $got_index; my $got_rgb; my $expected; my $expected_index; my $expected_rgb; my $caption; my $caption1; my $caption2; ############################################################################### # # Test. User defined colour as string. # $color = 'red'; $caption1 = " \tChart: \$index = _get_color_indices( $color )"; $caption2 = " \tChart: \$rgb = _get_color_indices( $color )"; $expected_index = 0x0A; $expected_rgb = 0x000000FF; ( $got_index, $got_rgb ) = $chart->_get_color_indices( $color ); is( $got_index, $expected_index, $caption1 ); is( $got_rgb, $expected_rgb, $caption2 ); ############################################################################### # # Test. User defined colour as string. # $color = 'black'; $caption1 = " \tChart: \$index = _get_color_indices( $color )"; $caption2 = " \tChart: \$rgb = _get_color_indices( $color )"; $expected_index = 0x08; $expected_rgb = 0x00000000; ( $got_index, $got_rgb ) = $chart->_get_color_indices( $color ); is( $got_index, $expected_index, $caption1 ); is( $got_rgb, $expected_rgb, $caption2 ); ############################################################################### # # Test. User defined colour as string. # $color = 'white'; $caption1 = " \tChart: \$index = _get_color_indices( $color )"; $caption2 = " \tChart: \$rgb = _get_color_indices( $color )"; $expected_index = 0x09; $expected_rgb = 0x00FFFFFF; ( $got_index, $got_rgb ) = $chart->_get_color_indices( $color ); is( $got_index, $expected_index, $caption1 ); is( $got_rgb, $expected_rgb, $caption2 ); ############################################################################### # # Test. User defined colour as an index. # $color = 0x0A; $caption1 = " \tChart: \$index = _get_color_indices( $color )"; $caption2 = " \tChart: \$rgb = _get_color_indices( $color )"; $expected_index = 0x0A; $expected_rgb = 0x000000FF; ( $got_index, $got_rgb ) = $chart->_get_color_indices( $color ); is( $got_index, $expected_index, $caption1 ); is( $got_rgb, $expected_rgb, $caption2 ); ############################################################################### # # Test. User defined colour as an out of range index. # $color = 7; $caption1 = " \tChart: \$index = _get_color_indices( $color )"; $caption2 = " \tChart: \$rgb = _get_color_indices( $color )"; $expected_index = undef; $expected_rgb = undef; ( $got_index, $got_rgb ) = $chart->_get_color_indices( $color ); is( $got_index, $expected_index, $caption1 ); is( $got_rgb, $expected_rgb, $caption2 ); ############################################################################### # # Test. User defined colour as an out of range index. # $color = 64; $caption1 = " \tChart: \$index = _get_color_indices( $color )"; $caption2 = " \tChart: \$rgb = _get_color_indices( $color )"; $expected_index = undef; $expected_rgb = undef; ( $got_index, $got_rgb ) = $chart->_get_color_indices( $color ); is( $got_index, $expected_index, $caption1 ); is( $got_rgb, $expected_rgb, $caption2 ); ############################################################################### # # Test. User defined colour as an invalid string. # $color = 'plaid'; $caption1 = " \tChart: \$index = _get_color_indices( $color )"; $caption2 = " \tChart: \$rgb = _get_color_indices( $color )"; $expected_index = undef; $expected_rgb = undef; ( $got_index, $got_rgb ) = $chart->_get_color_indices( $color ); is( $got_index, $expected_index, $caption1 ); is( $got_rgb, $expected_rgb, $caption2 ); ############################################################################### # # Test. User defined colour as an undef property. # $color = undef; $caption1 = " \tChart: \$index = _get_color_indices( undef )"; $caption2 = " \tChart: \$rgb = _get_color_indices( undef )"; $expected_index = undef; $expected_rgb = undef; ( $got_index, $got_rgb ) = $chart->_get_color_indices( $color ); is( $got_index, $expected_index, $caption1 ); is( $got_rgb, $expected_rgb, $caption2 ); ############################################################################### # # Test. Line patterns with indices. # $caption = " \tChart: \$pattern = _get_line_pattern()"; %values = ( 0 => 5, 1 => 0, 2 => 1, 3 => 2, 4 => 3, 5 => 4, 6 => 7, 7 => 6, 8 => 8, 9 => 0, undef => 0 ); $expected = []; $got = []; while ( my ( $user, $excel ) = each %values ) { push @$got, $chart->_get_line_pattern( $user ); push @$expected, $excel; } is_deeply( $got, $expected, $caption ); ############################################################################### # # Test. Line patterns with names. # $caption = " \tChart: \$pattern = _get_line_pattern()"; %values = ( 'solid' => 0, 'dash' => 1, 'dot' => 2, 'dash-dot' => 3, 'dash-dot-dot' => 4, 'none' => 5, 'dark-gray' => 6, 'medium-gray' => 7, 'light-gray' => 8, 'DASH' => 1, 'fictional' => 0 ); $expected = []; $got = []; while ( my ( $user, $excel ) = each %values ) { push @$got, $chart->_get_line_pattern( $user ); push @$expected, $excel; } is_deeply( $got, $expected, $caption ); ############################################################################### # # Test. Line weights with indices. # $caption = " \tChart: \$weight = _get_line_weight()"; %values = ( 1 => -1, 2 => 0, 3 => 1, 4 => 2, 5 => 0, 0 => 0, undef => 0 ); $expected = []; $got = []; while ( my ( $user, $excel ) = each %values ) { push @$got, $chart->_get_line_weight( $user ); push @$expected, $excel; } is_deeply( $got, $expected, $caption ); ############################################################################### # # Test. Line weights with names. # $caption = " \tChart: \$weight = _get_line_weight()"; %values = ( 'hairline' => -1, 'narrow' => 0, 'medium' => 1, 'wide' => 2, 'WIDE' => 2, 'Fictional' => 0, ); $expected = []; $got = []; while ( my ( $user, $excel ) = each %values ) { push @$got, $chart->_get_line_weight( $user ); push @$expected, $excel; } is_deeply( $got, $expected, $caption ); ############################################################################### # # Clean up. # $workbook->close(); unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/t/63_chart_area_formats.t000644 000765 000024 00000044031 11332131702 022577 0ustar00Johnstaff000000 000000 ############################################################################### # # A test for Spreadsheet::WriteExcel::Chart. # # Tests for the set*area() Chart methods. # # reverse(''), January 2010, John McNamara, jmcnamara@cpan.org # # prove -I../lib --nocolor -v 63_chart_area_formats.t use strict; use Spreadsheet::WriteExcel; use Test::More tests => 52; #use Test::More 'no_plan'; ############################################################################### # # Tests setup # my $test_file = 'temp_test_file.xls'; my $workbook = Spreadsheet::WriteExcel->new( $test_file ); my $chart = $workbook->add_chart( type => 'column' ); $chart->{_using_tmpfile} = 0; my $got_line; my $got_area; my $expected_line; my $expected_area; my $caption1 = " \tChart: chartarea format - line"; my $caption2 = " \tChart: chartarea format - area"; ############################################################################### # # 1. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_chartarea( color => 'red', line_color => 'black', line_pattern => 2, line_weight => 3, ); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 01 00 01 00 00 00 08 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF 00 00 00 00 00 00 00 01 00 00 00 0A 00 08 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 3. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_chartarea( color => 'red', ); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 05 00 FF FF 08 00 4D 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF 00 00 00 00 00 00 00 01 00 00 00 0A 00 08 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 5. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_chartarea( line_color => 'red', ); $expected_line = join ' ', qw( 07 10 0C 00 FF 00 00 00 00 00 FF FF 00 00 0A 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF FF FF 00 00 00 00 00 00 00 00 00 4E 00 4D 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 7. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_chartarea( line_pattern => 2, ); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 01 00 FF FF 00 00 4F 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF FF FF 00 00 00 00 00 00 00 00 00 4E 00 4D 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 9. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_chartarea( line_weight => 3, ); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 00 00 01 00 00 00 4F 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF FF FF 00 00 00 00 00 00 00 00 00 4E 00 4D 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 11. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_chartarea( color => 'red', line_color => 'black', ); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 00 00 FF FF 00 00 08 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF 00 00 00 00 00 00 00 01 00 00 00 0A 00 08 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 13. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_chartarea( color => 'red', line_pattern => 2, ); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 01 00 FF FF 00 00 4F 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF 00 00 00 00 00 00 00 01 00 00 00 0A 00 08 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 15. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_chartarea( color => 'red', line_weight => 3, ); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 00 00 01 00 00 00 4F 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF 00 00 00 00 00 00 00 01 00 00 00 0A 00 08 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # Set some variables for embedded chart tests. # $caption1 = " \tChart: embedded chartarea format - line"; $caption2 = " \tChart: embedded chartarea format - area"; $chart->{_embedded} = 1; ############################################################################### # # 17. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart, 1 ); $chart->set_chartarea(); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 00 00 00 00 09 00 4D 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF FF FF 00 00 00 00 00 01 00 01 00 4E 00 4D 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 19. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart, 1 ); $chart->set_chartarea( color => 'red', line_color => 'black', line_pattern => 2, line_weight => 3, ); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 01 00 01 00 00 00 08 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF 00 00 00 00 00 00 00 01 00 00 00 0A 00 08 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 21. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart, 1 ); $chart->set_chartarea( color => 'red', ); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 00 00 FF FF 09 00 4D 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF 00 00 00 00 00 00 00 01 00 00 00 0A 00 08 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 23. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart, 1 ); $chart->set_chartarea( line_color => 'red', ); $expected_line = join ' ', qw( 07 10 0C 00 FF 00 00 00 00 00 FF FF 00 00 0A 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF FF FF 00 00 00 00 00 01 00 01 00 4E 00 4D 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 25. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart, 1 ); $chart->set_chartarea( line_pattern => 2, ); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 01 00 FF FF 00 00 4F 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF FF FF 00 00 00 00 00 01 00 01 00 4E 00 4D 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 27. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart, 1 ); $chart->set_chartarea( line_weight => 3, ); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 00 00 01 00 00 00 4F 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF FF FF 00 00 00 00 00 01 00 01 00 4E 00 4D 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 29. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart, 1 ); $chart->set_chartarea( color => 'red', line_color => 'black', ); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 00 00 FF FF 00 00 08 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF 00 00 00 00 00 00 00 01 00 00 00 0A 00 08 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 31. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart, 1 ); $chart->set_chartarea( color => 'red', line_pattern => 2, ); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 01 00 FF FF 00 00 4F 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF 00 00 00 00 00 00 00 01 00 00 00 0A 00 08 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 33. Test the chartarea format methods. See the set_*area() properties below. # reset_chart( $chart, 1 ); $chart->set_chartarea( color => 'red', line_weight => 3, ); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 00 00 01 00 00 00 4F 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF 00 00 00 00 00 00 00 01 00 00 00 0A 00 08 00 ); ( $got_line, $got_area ) = get_chartarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # Set some variables for embedded chart tests. # $caption1 = " \tChart: plotarea format - line"; $caption2 = " \tChart: plotarea format - area"; $chart->{_embedded} = 0; ############################################################################### # # 35. Test the plotarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_plotarea(); $expected_line = join ' ', qw( 07 10 0C 00 80 80 80 00 00 00 00 00 00 00 17 00 ); $expected_area = join ' ', qw( 0A 10 10 00 C0 C0 C0 00 00 00 00 00 01 00 00 00 16 00 4F 00 ); ( $got_line, $got_area ) = get_plotarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 37. Test the plotarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_plotarea( color => 'red', line_color => 'black', line_pattern => 2, line_weight => 3, ); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 01 00 01 00 00 00 08 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF 00 00 00 00 00 00 00 01 00 00 00 0A 00 08 00 ); ( $got_line, $got_area ) = get_plotarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 39. Test the plotarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_plotarea( color => 'red', ); $expected_line = join ' ', qw( 07 10 0C 00 80 80 80 00 00 00 00 00 00 00 17 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF 00 00 00 00 00 00 00 01 00 00 00 0A 00 08 00 ); ( $got_line, $got_area ) = get_plotarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 41. Test the plotarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_plotarea( line_color => 'red', ); $expected_line = join ' ', qw( 07 10 0C 00 FF 00 00 00 00 00 00 00 00 00 0A 00 ); $expected_area = join ' ', qw( 0A 10 10 00 C0 C0 C0 00 00 00 00 00 01 00 00 00 16 00 08 00 ); ( $got_line, $got_area ) = get_plotarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 43. Test the plotarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_plotarea( line_pattern => 2, ); $expected_line = join ' ', qw( 07 10 0C 00 80 80 80 00 01 00 00 00 00 00 17 00 ); $expected_area = join ' ', qw( 0A 10 10 00 C0 C0 C0 00 00 00 00 00 01 00 00 00 16 00 08 00 ); ( $got_line, $got_area ) = get_plotarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 45. Test the plotarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_plotarea( line_weight => 3, ); $expected_line = join ' ', qw( 07 10 0C 00 80 80 80 00 00 00 01 00 00 00 17 00 ); $expected_area = join ' ', qw( 0A 10 10 00 C0 C0 C0 00 00 00 00 00 01 00 00 00 16 00 08 00 ); ( $got_line, $got_area ) = get_plotarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 47. Test the plotarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_plotarea( color => 'red', line_color => 'black', ); $expected_line = join ' ', qw( 07 10 0C 00 00 00 00 00 00 00 00 00 00 00 08 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF 00 00 00 00 00 00 00 01 00 00 00 0A 00 08 00 ); ( $got_line, $got_area ) = get_plotarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 49. Test the plotarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_plotarea( color => 'red', line_pattern => 2, ); $expected_line = join ' ', qw( 07 10 0C 00 80 80 80 00 01 00 00 00 00 00 17 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF 00 00 00 00 00 00 00 01 00 00 00 0A 00 08 00 ); ( $got_line, $got_area ) = get_plotarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # 51. Test the plotarea format methods. See the set_*area() properties below. # reset_chart( $chart ); $chart->set_plotarea( color => 'red', line_weight => 3, ); $expected_line = join ' ', qw( 07 10 0C 00 80 80 80 00 00 00 01 00 00 00 17 00 ); $expected_area = join ' ', qw( 0A 10 10 00 FF 00 00 00 00 00 00 00 01 00 00 00 0A 00 08 00 ); ( $got_line, $got_area ) = get_plotarea_formats( $chart ); is( $got_line, $expected_line, $caption1 ); is( $got_area, $expected_area, $caption2 ); ############################################################################### # # Utility functions used by the test suite. # ############################################################################### ############################################################################### # # Reset the chart data for testing. # sub reset_chart { my $chart = shift; my $embedded = shift; # Reset the chart data. $chart->{_data} = ''; $chart->_set_default_properties(); if ( $embedded ) { $chart->_set_embedded_config_data(); } } ############################################################################### # # Extract Line and Area format records from the Chartarea Frame stream. # sub get_chartarea_formats { $chart = shift; $chart->_store_chartarea_frame_stream(); my $line = unpack_record( substr $chart->{_data}, 12, 16 ); my $area = unpack_record( substr $chart->{_data}, 28, 20 ); return ( $line, $area ); } ############################################################################### # # Extract Line and Area format records from the Chartarea Frame stream. # sub get_plotarea_formats { $chart = shift; $chart->_store_plotarea_frame_stream(); my $line = unpack_record( substr $chart->{_data}, 12, 16 ); my $area = unpack_record( substr $chart->{_data}, 28, 20 ); return ( $line, $area ); } ############################################################################### # # Unpack the binary data into a format suitable for printing in tests. # sub unpack_record { return join ' ', map { sprintf '%02X', $_ } unpack 'C*', shift; } ############################################################################### # # Clean up. # $workbook->close(); unlink $test_file; __END__ Spreadsheet-WriteExcel-2.40/lib/Spreadsheet/000755 000765 000024 00000000000 12236561260 021040 5ustar00Johnstaff000000 000000 Spreadsheet-WriteExcel-2.40/lib/Spreadsheet/WriteExcel/000755 000765 000024 00000000000 12236561260 023113 5ustar00Johnstaff000000 000000 Spreadsheet-WriteExcel-2.40/lib/Spreadsheet/WriteExcel.pm000644 000765 000024 00000667060 12236556506 023477 0ustar00Johnstaff000000 000000 package Spreadsheet::WriteExcel; ############################################################################### # # WriteExcel. # # Spreadsheet::WriteExcel - Write to a cross-platform Excel binary file. # # Copyright 2000-2012, John McNamara, jmcnamara@cpan.org # # Documentation after __END__ # use Exporter; use strict; use Spreadsheet::WriteExcel::Workbook; use vars qw($VERSION @ISA); @ISA = qw(Spreadsheet::WriteExcel::Workbook Exporter); $VERSION = '2.40'; # A farewell to arms. ############################################################################### # # new() # # Constructor. Wrapper for a Workbook object. # uses: Spreadsheet::WriteExcel::BIFFwriter # Spreadsheet::WriteExcel::Chart # Spreadsheet::WriteExcel::OLEwriter # Spreadsheet::WriteExcel::Workbook # Spreadsheet::WriteExcel::Worksheet # Spreadsheet::WriteExcel::Format # Spreadsheet::WriteExcel::Formula # Spreadsheet::WriteExcel::Properties # sub new { my $class = shift; my $self = Spreadsheet::WriteExcel::Workbook->new(@_); # Check for file creation failures before re-blessing bless $self, $class if defined $self; return $self; } 1; __END__ =head1 NAME Spreadsheet::WriteExcel - Write to a cross-platform Excel binary file. =head1 VERSION This document refers to version 2.40 of Spreadsheet::WriteExcel, released November 6, 2013. =head1 SYNOPSIS To write a string, a formatted string, a number and a formula to the first worksheet in an Excel workbook called perl.xls: use Spreadsheet::WriteExcel; # Create a new Excel workbook my $workbook = Spreadsheet::WriteExcel->new('perl.xls'); # Add a worksheet $worksheet = $workbook->add_worksheet(); # Add and define a format $format = $workbook->add_format(); # Add a format $format->set_bold(); $format->set_color('red'); $format->set_align('center'); # Write a formatted and unformatted string, row and column notation. $col = $row = 0; $worksheet->write($row, $col, 'Hi Excel!', $format); $worksheet->write(1, $col, 'Hi Excel!'); # Write a number and a formula using A1 notation $worksheet->write('A3', 1.2345); $worksheet->write('A4', '=SIN(PI()/4)'); =head1 DESCRIPTION The Spreadsheet::WriteExcel Perl module can be used to create a cross-platform Excel binary file. Multiple worksheets can be added to a workbook and formatting can be applied to cells. Text, numbers, formulas, hyperlinks, images and charts can be written to the cells. The file produced by this module is compatible with Excel 97, 2000, 2002, 2003 and 2007. The module will work on the majority of Windows, UNIX and Mac platforms. Generated files are also compatible with the Linux/UNIX spreadsheet applications Gnumeric and OpenOffice.org. This module cannot be used to write to an existing Excel file (See L). B and in future will only be updated with bug fixes. The newer, more feature rich and API compatible L module is recommended instead. See, L. =head1 QUICK START Spreadsheet::WriteExcel tries to provide an interface to as many of Excel's features as possible. As a result there is a lot of documentation to accompany the interface and it can be difficult at first glance to see what it important and what is not. So for those of you who prefer to assemble Ikea furniture first and then read the instructions, here are three easy steps: 1. Create a new Excel I (i.e. file) using C. 2. Add a I to the new workbook using C. 3. Write to the worksheet using C. Like this: use Spreadsheet::WriteExcel; # Step 0 my $workbook = Spreadsheet::WriteExcel->new('perl.xls'); # Step 1 $worksheet = $workbook->add_worksheet(); # Step 2 $worksheet->write('A1', 'Hi Excel!'); # Step 3 This will create an Excel file called C with a single worksheet and the text C<'Hi Excel!'> in the relevant cell. And that's it. Okay, so there is actually a zeroth step as well, but C goes without saying. There are also more than 80 examples that come with the distribution and which you can use to get you started. See L. Those of you who read the instructions first and assemble the furniture afterwards will know how to proceed. ;-) =head1 WORKBOOK METHODS The Spreadsheet::WriteExcel module provides an object oriented interface to a new Excel workbook. The following methods are available through a new workbook. new() add_worksheet() add_format() add_chart() add_chart_ext() close() compatibility_mode() set_properties() define_name() set_tempdir() set_custom_color() sheets() set_1904() set_codepage() If you are unfamiliar with object oriented interfaces or the way that they are implemented in Perl have a look at C and C in the main Perl documentation. =head2 new() A new Excel workbook is created using the C constructor which accepts either a filename or a filehandle as a parameter. The following example creates a new Excel file based on a filename: my $workbook = Spreadsheet::WriteExcel->new('filename.xls'); my $worksheet = $workbook->add_worksheet(); $worksheet->write(0, 0, 'Hi Excel!'); Here are some other examples of using C with filenames: my $workbook1 = Spreadsheet::WriteExcel->new($filename); my $workbook2 = Spreadsheet::WriteExcel->new('/tmp/filename.xls'); my $workbook3 = Spreadsheet::WriteExcel->new("c:\\tmp\\filename.xls"); my $workbook4 = Spreadsheet::WriteExcel->new('c:\tmp\filename.xls'); The last two examples demonstrates how to create a file on DOS or Windows where it is necessary to either escape the directory separator C<\> or to use single quotes to ensure that it isn't interpolated. For more information see C. The C constructor returns a Spreadsheet::WriteExcel object that you can use to add worksheets and store data. It should be noted that although C is not specifically required it defines the scope of the new workbook variable and, in the majority of cases, ensures that the workbook is closed properly without explicitly calling the C method. If the file cannot be created, due to file permissions or some other reason, C will return C. Therefore, it is good practice to check the return value of C before proceeding. As usual the Perl variable C<$!> will be set if there is a file creation error. You will also see one of the warning messages detailed in L: my $workbook = Spreadsheet::WriteExcel->new('protected.xls'); die "Problems creating new Excel file: $!" unless defined $workbook; You can also pass a valid filehandle to the C constructor. For example in a CGI program you could do something like this: binmode(STDOUT); my $workbook = Spreadsheet::WriteExcel->new(\*STDOUT); The requirement for C is explained below. See also, the C program in the C directory of the distro. However, this special case will not work in C programs where you will have to do something like the following: # mod_perl 1 ... tie *XLS, 'Apache'; binmode(XLS); my $workbook = Spreadsheet::WriteExcel->new(\*XLS); ... # mod_perl 2 ... tie *XLS => $r; # Tie to the Apache::RequestRec object binmode(*XLS); my $workbook = Spreadsheet::WriteExcel->new(\*XLS); ... See also, the C and C programs in the C directory of the distro. Filehandles can also be useful if you want to stream an Excel file over a socket or if you want to store an Excel file in a scalar. For example here is a way to write an Excel file to a scalar with C: #!/usr/bin/perl -w use strict; use Spreadsheet::WriteExcel; # Requires perl 5.8 or later open my $fh, '>', \my $str or die "Failed to open filehandle: $!"; my $workbook = Spreadsheet::WriteExcel->new($fh); my $worksheet = $workbook->add_worksheet(); $worksheet->write(0, 0, 'Hi Excel!'); $workbook->close(); # The Excel file in now in $str. Remember to binmode() the output # filehandle before printing it. binmode STDOUT; print $str; See also the C and C programs in the C directory of the distro. B C. An Excel file is comprised of binary data. Therefore, if you are using a filehandle you should ensure that you C it prior to passing it to C.You should do this regardless of whether you are on a Windows platform or not. This applies especially to users of perl 5.8 on systems where C is likely to be in operation such as RedHat Linux 9. If your program, either intentionally or not, writes C data to a filehandle that is passed to C it will corrupt the Excel file that is created. You don't have to worry about C if you are using filenames instead of filehandles. Spreadsheet::WriteExcel performs the C internally when it converts the filename to a filehandle. For more information about C see C and C in the main Perl documentation. =head2 add_worksheet($sheetname, $utf_16_be) At least one worksheet should be added to a new workbook. A worksheet is used to write data into cells: $worksheet1 = $workbook->add_worksheet(); # Sheet1 $worksheet2 = $workbook->add_worksheet('Foglio2'); # Foglio2 $worksheet3 = $workbook->add_worksheet('Data'); # Data $worksheet4 = $workbook->add_worksheet(); # Sheet4 If C<$sheetname> is not specified the default Excel convention will be followed, i.e. Sheet1, Sheet2, etc. The C<$utf_16_be> parameter is optional, see below. The worksheet name must be a valid Excel worksheet name, i.e. it cannot contain any of the following characters, C<[ ] : * ? / \> and it must be less than 32 characters. In addition, you cannot use the same, case insensitive, C<$sheetname> for more than one worksheet. On systems with C and later the C method will also handle strings in C format. $worksheet = $workbook->add_worksheet("\x{263a}"); # Smiley On earlier Perl systems your can specify C worksheet names using an additional optional parameter: my $name = pack 'n', 0x263a; $worksheet = $workbook->add_worksheet($name, 1); # Smiley =head2 add_format(%properties) The C method can be used to create new Format objects which are used to apply formatting to a cell. You can either define the properties at creation time via a hash of property values or later via method calls. $format1 = $workbook->add_format(%props); # Set properties at creation $format2 = $workbook->add_format(); # Set properties later See the L section for more details about Format properties and how to set them. =head2 add_chart(%properties) This method is use to create a new chart either as a standalone worksheet (the default) or as an embeddable object that can be inserted into a worksheet via the C Worksheet method. my $chart = $workbook->add_chart( type => 'column' ); The properties that can be set are: type (required) name (optional) embedded (optional) =over =item * C This is a required parameter. It defines the type of chart that will be created. my $chart = $workbook->add_chart( type => 'line' ); The available types are: area bar column line pie scatter stock =item * C Set the name for the chart sheet. The name property is optional and if it isn't supplied will default to C. The name must be a valid Excel worksheet name. See C for more details on valid sheet names. The C property can be omitted for embedded charts. my $chart = $workbook->add_chart( type => 'line', name => 'Results Chart' ); =item * C Specifies that the Chart object will be inserted in a worksheet via the C Worksheet method. It is an error to try insert a Chart that doesn't have this flag set. my $chart = $workbook->add_chart( type => 'line', embedded => 1 ); # Configure the chart. ... # Insert the chart into the a worksheet. $worksheet->insert_chart( 'E2', $chart ); =back See L for details on how to configure the chart object once it is created. See also the C programs in the examples directory of the distro. =head2 add_chart_ext($chart_data, $chartname) This method is use to include externally generated charts in a Spreadsheet::WriteExcel file. my $chart = $workbook->add_chart_ext('chart01.bin', 'Chart1'); This feature is semi-deprecated in favour of the "native" charts created using C. Read C (or C<.pod>) in the external_charts directory of the distro for a full explanation. =head2 close() In general your Excel file will be closed automatically when your program ends or when the Workbook object goes out of scope, however the C method can be used to explicitly close an Excel file. $workbook->close(); An explicit C is required if the file must be closed prior to performing some external action on it such as copying it, reading its size or attaching it to an email. In addition, C may be required to prevent perl's garbage collector from disposing of the Workbook, Worksheet and Format objects in the wrong order. Situations where this can occur are: =over 4 =item * If C was not used to declare the scope of a workbook variable created using C. =item * If the C, C or C methods are called in subroutines. =back The reason for this is that Spreadsheet::WriteExcel relies on Perl's C mechanism to trigger destructor methods in a specific sequence. This may not happen in cases where the Workbook, Worksheet and Format variables are not lexically scoped or where they have different lexical scopes. In general, if you create a file with a size of 0 bytes or you fail to create a file you need to call C. The return value of C is the same as that returned by perl when it closes the file created by C. This allows you to handle error conditions in the usual way: $workbook->close() or die "Error closing file: $!"; =head2 compatibility_mode() This method is used to improve compatibility with third party applications that read Excel files. $workbook->compatibility_mode(); An Excel file is comprised of binary records that describe properties of a spreadsheet. Excel is reasonably liberal about this and, outside of a core subset, it doesn't require every possible record to be present when it reads a file. This is also true of Gnumeric and OpenOffice.Org Calc. Spreadsheet::WriteExcel takes advantage of this fact to omit some records in order to minimise the amount of data stored in memory and to simplify and speed up the writing of files. However, some third party applications that read Excel files often expect certain records to be present. In "compatibility mode" Spreadsheet::WriteExcel writes these records and tries to be as close to an Excel generated file as possible. Applications that require C are Apache POI, Apple Numbers, and Quickoffice on Nokia, Palm and other devices. You should also use C if your Excel file will be used as an external data source by another Excel file. If you encounter other situations that require C, please let me know. It should be noted that C requires additional data to be stored in memory and additional processing. This incurs a memory and speed penalty and may not be suitable for very large files (>20MB). You must call C before calling C. =head2 set_properties() The C method can be used to set the document properties of the Excel file created by C. These properties are visible when you use the C<< File->Properties >> menu option in Excel and are also available to external applications that read or index windows files. The properties should be passed as a hash of values as follows: $workbook->set_properties( title => 'This is an example spreadsheet', author => 'John McNamara', comments => 'Created with Perl and Spreadsheet::WriteExcel', ); The properties that can be set are: title subject author manager company category keywords comments User defined properties are not supported due to effort required. In perl 5.8+ you can also pass UTF-8 strings as properties. See L. my $smiley = chr 0x263A; $workbook->set_properties( subject => "Happy now? $smiley", ); With older versions of perl you can use a module to convert a non-ASCII string to a binary representation of UTF-8 and then pass an additional C flag to C: my $smiley = pack 'H*', 'E298BA'; $workbook->set_properties( subject => "Happy now? $smiley", utf8 => 1, ); Usually Spreadsheet::WriteExcel allows you to use UTF-16 with pre 5.8 versions of perl. However, document properties don't support UTF-16 for these type of strings. In order to promote the usefulness of Perl and the Spreadsheet::WriteExcel module consider adding a comment such as the following when using document properties: $workbook->set_properties( ..., comments => 'Created with Perl and Spreadsheet::WriteExcel', ..., ); This feature requires that the C module is installed (which is usually the case for a standard Spreadsheet::WriteExcel installation). However, this also means that the resulting OLE document may B be buggy for files less than 7MB since it hasn't been as rigorously tested in that domain. As a result of this C is currently incompatible with Gnumeric for files less than 7MB. This is being investigated. If you encounter any problems with this features let me know. For convenience it is possible to pass either a hash or hash ref of arguments to this method. See also the C program in the examples directory of the distro. =head2 define_name() This method is used to defined a name that can be used to represent a value, a single cell or a range of cells in a workbook. $workbook->define_name('Exchange_rate', '=0.96'); $workbook->define_name('Sales', '=Sheet1!$G$1:$H$10'); $workbook->define_name('Sheet2!Sales', '=Sheet2!$G$1:$G$10'); See the defined_name.pl program in the examples dir of the distro. Note: This currently a beta feature. More documentation and examples will be added. =head2 set_tempdir() For speed and efficiency C stores worksheet data in temporary files prior to assembling the final workbook. If Spreadsheet::WriteExcel is unable to create these temporary files it will store the required data in memory. This can be slow for large files. The problem occurs mainly with IIS on Windows although it could feasibly occur on Unix systems as well. The problem generally occurs because the default temp file directory is defined as C or some other directory that IIS doesn't provide write access to. To check if this might be a problem on a particular system you can run a simple test program with C<-w> or C. This will generate a warning if the module cannot create the required temporary files: #!/usr/bin/perl -w use Spreadsheet::WriteExcel; my $workbook = Spreadsheet::WriteExcel->new('test.xls'); my $worksheet = $workbook->add_worksheet(); To avoid this problem the C method can be used to specify a directory that is accessible for the creation of temporary files. The C module is used to create the temporary files. File::Temp uses C to determine an appropriate location for these files such as C or C. You can find out which directory is used on your system as follows: perl -MFile::Spec -le "print File::Spec->tmpdir" Even if the default temporary file directory is accessible you may wish to specify an alternative location for security or maintenance reasons: $workbook->set_tempdir('/tmp/writeexcel'); $workbook->set_tempdir('c:\windows\temp\writeexcel'); The directory for the temporary file must exist, C will not create a new directory. One disadvantage of using the C method is that on some Windows systems it will limit you to approximately 800 concurrent tempfiles. This means that a single program running on one of these systems will be limited to creating a total of 800 workbook and worksheet objects. You can run multiple, non-concurrent programs to work around this if necessary. =head2 set_custom_color($index, $red, $green, $blue) The C method can be used to override one of the built-in palette values with a more suitable colour. The value for C<$index> should be in the range 8..63, see L. The default named colours use the following indices: 8 => black 9 => white 10 => red 11 => lime 12 => blue 13 => yellow 14 => magenta 15 => cyan 16 => brown 17 => green 18 => navy 20 => purple 22 => silver 23 => gray 33 => pink 53 => orange A new colour is set using its RGB (red green blue) components. The C<$red>, C<$green> and C<$blue> values must be in the range 0..255. You can determine the required values in Excel using the COptions-EColors-EModify> dialog. The C workbook method can also be used with a HTML style C<#rrggbb> hex value: $workbook->set_custom_color(40, 255, 102, 0 ); # Orange $workbook->set_custom_color(40, 0xFF, 0x66, 0x00); # Same thing $workbook->set_custom_color(40, '#FF6600' ); # Same thing my $font = $workbook->add_format(color => 40); # Use the modified colour The return value from C is the index of the colour that was changed: my $ferrari = $workbook->set_custom_color(40, 216, 12, 12); my $format = $workbook->add_format( bg_color => $ferrari, pattern => 1, border => 1 ); =head2 sheets(0, 1, ...) The C method returns a list, or a sliced list, of the worksheets in a workbook. If no arguments are passed the method returns a list of all the worksheets in the workbook. This is useful if you want to repeat an operation on each worksheet: foreach $worksheet ($workbook->sheets()) { print $worksheet->get_name(); } You can also specify a slice list to return one or more worksheet objects: $worksheet = $workbook->sheets(0); $worksheet->write('A1', 'Hello'); Or since return value from C is a reference to a worksheet object you can write the above example as: $workbook->sheets(0)->write('A1', 'Hello'); The following example returns the first and last worksheet in a workbook: foreach $worksheet ($workbook->sheets(0, -1)) { # Do something } Array slices are explained in the perldata manpage. =head2 set_1904() Excel stores dates as real numbers where the integer part stores the number of days since the epoch and the fractional part stores the percentage of the day. The epoch can be either 1900 or 1904. Excel for Windows uses 1900 and Excel for Macintosh uses 1904. However, Excel on either platform will convert automatically between one system and the other. Spreadsheet::WriteExcel stores dates in the 1900 format by default. If you wish to change this you can call the C workbook method. You can query the current value by calling the C workbook method. This returns 0 for 1900 and 1 for 1904. See also L for more information about working with Excel's date system. In general you probably won't need to use C. =head2 set_codepage($codepage) The default code page or character set used by Spreadsheet::WriteExcel is ANSI. This is also the default used by Excel for Windows. Occasionally however it may be necessary to change the code page via the C method. Changing the code page may be required if your are using Spreadsheet::WriteExcel on the Macintosh and you are using characters outside the ASCII 128 character set: $workbook->set_codepage(1); # ANSI, MS Windows $workbook->set_codepage(2); # Apple Macintosh The C method is rarely required. =head1 WORKSHEET METHODS A new worksheet is created by calling the C method from a workbook object: $worksheet1 = $workbook->add_worksheet(); $worksheet2 = $workbook->add_worksheet(); The following methods are available through a new worksheet: write() write_number() write_string() write_utf16be_string() write_utf16le_string() keep_leading_zeros() write_blank() write_row() write_col() write_date_time() write_url() write_url_range() write_formula() store_formula() repeat_formula() write_comment() show_comments() add_write_handler() insert_image() insert_chart() data_validation() get_name() activate() select() hide() set_first_sheet() protect() set_selection() set_row() set_column() outline_settings() freeze_panes() split_panes() merge_range() set_zoom() right_to_left() hide_zero() set_tab_color() autofilter() =head2 Cell notation Spreadsheet::WriteExcel supports two forms of notation to designate the position of cells: Row-column notation and A1 notation. Row-column notation uses a zero based index for both row and column while A1 notation uses the standard Excel alphanumeric sequence of column letter and 1-based row. For example: (0, 0) # The top left cell in row-column notation. ('A1') # The top left cell in A1 notation. (1999, 29) # Row-column notation. ('AD2000') # The same cell in A1 notation. Row-column notation is useful if you are referring to cells programmatically: for my $i (0 .. 9) { $worksheet->write($i, 0, 'Hello'); # Cells A1 to A10 } A1 notation is useful for setting up a worksheet manually and for working with formulas: $worksheet->write('H1', 200); $worksheet->write('H2', '=H1+1'); In formulas and applicable methods you can also use the C column notation: $worksheet->write('A1', '=SUM(B:B)'); The C module that is included in the distro contains helper functions for dealing with A1 notation, for example: use Spreadsheet::WriteExcel::Utility; ($row, $col) = xl_cell_to_rowcol('C2'); # (1, 2) $str = xl_rowcol_to_cell(1, 2); # C2 For simplicity, the parameter lists for the worksheet method calls in the following sections are given in terms of row-column notation. In all cases it is also possible to use A1 notation. Note: in Excel it is also possible to use a R1C1 notation. This is not supported by Spreadsheet::WriteExcel. =head2 write($row, $column, $token, $format) Excel makes a distinction between data types such as strings, numbers, blanks, formulas and hyperlinks. To simplify the process of writing data the C method acts as a general alias for several more specific methods: write_string() write_number() write_blank() write_formula() write_url() write_row() write_col() The general rule is that if the data looks like a I then a I is written. Here are some examples in both row-column and A1 notation: # Same as: $worksheet->write(0, 0, 'Hello' ); # write_string() $worksheet->write(1, 0, 'One' ); # write_string() $worksheet->write(2, 0, 2 ); # write_number() $worksheet->write(3, 0, 3.00001 ); # write_number() $worksheet->write(4, 0, "" ); # write_blank() $worksheet->write(5, 0, '' ); # write_blank() $worksheet->write(6, 0, undef ); # write_blank() $worksheet->write(7, 0 ); # write_blank() $worksheet->write(8, 0, 'http://www.perl.com/'); # write_url() $worksheet->write('A9', 'ftp://ftp.cpan.org/' ); # write_url() $worksheet->write('A10', 'internal:Sheet1!A1' ); # write_url() $worksheet->write('A11', 'external:c:\foo.xls' ); # write_url() $worksheet->write('A12', '=A3 + 3*A4' ); # write_formula() $worksheet->write('A13', '=SIN(PI()/4)' ); # write_formula() $worksheet->write('A14', \@array ); # write_row() $worksheet->write('A15', [\@array] ); # write_col() # And if the keep_leading_zeros property is set: $worksheet->write('A16', '2' ); # write_number() $worksheet->write('A17', '02' ); # write_string() $worksheet->write('A18', '00002' ); # write_string() The "looks like" rule is defined by regular expressions: C if C<$token> is a number based on the following regex: C<$token =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/>. C if C is set and C<$token> is an integer with leading zeros based on the following regex: C<$token =~ /^0\d+$/>. C if C<$token> is undef or a blank string: C, C<""> or C<''>. C if C<$token> is a http, https, ftp or mailto URL based on the following regexes: C<$token =~ m|^[fh]tt?ps?://|> or C<$token =~ m|^mailto:|>. C if C<$token> is an internal or external sheet reference based on the following regex: C<$token =~ m[^(in|ex)ternal:]>. C if the first character of C<$token> is C<"=">. C if C<$token> is an array ref. C if C<$token> is an array ref of array refs. C if none of the previous conditions apply. The C<$format> parameter is optional. It should be a valid Format object, see L: my $format = $workbook->add_format(); $format->set_bold(); $format->set_color('red'); $format->set_align('center'); $worksheet->write(4, 0, 'Hello', $format); # Formatted string The write() method will ignore empty strings or C tokens unless a format is also supplied. As such you needn't worry about special handling for empty or C values in your data. See also the C method. One problem with the C method is that occasionally data looks like a number but you don't want it treated as a number. For example, zip codes or ID numbers often start with a leading zero. If you write this data as a number then the leading zero(s) will be stripped. You can change this default behaviour by using the C method. While this property is in place any integers with leading zeros will be treated as strings and the zeros will be preserved. See the C section for a full discussion of this issue. You can also add your own data handlers to the C method using C. On systems with C and later the C method will also handle Unicode strings in C format. The C methods return: 0 for success. -1 for insufficient number of arguments. -2 for row or column out of bounds. -3 for string too long. =head2 write_number($row, $column, $number, $format) Write an integer or a float to the cell specified by C<$row> and C<$column>: $worksheet->write_number(0, 0, 123456); $worksheet->write_number('A2', 2.3451); See the note about L. The C<$format> parameter is optional. In general it is sufficient to use the C method. =head2 write_string($row, $column, $string, $format) Write a string to the cell specified by C<$row> and C<$column>: $worksheet->write_string(0, 0, 'Your text here' ); $worksheet->write_string('A2', 'or here' ); The maximum string size is 32767 characters. However the maximum string segment that Excel can display in a cell is 1000. All 32767 characters can be displayed in the formula bar. The C<$format> parameter is optional. On systems with C and later the C method will also handle strings in C format. With older perls you can also write Unicode in C format via the C method. See also the C programs in the examples directory of the distro. In general it is sufficient to use the C method. However, you may sometimes wish to use the C method to write data that looks like a number but that you don't want treated as a number. For example, zip codes or phone numbers: # Write as a plain string $worksheet->write_string('A1', '01209'); However, if the user edits this string Excel may convert it back to a number. To get around this you can use the Excel text format C<@>: # Format as a string. Doesn't change to a number when edited my $format1 = $workbook->add_format(num_format => '@'); $worksheet->write_string('A2', '01209', $format1); See also the note about L. =head2 write_utf16be_string($row, $column, $string, $format) This method is used to write C strings to a cell in Excel. It is functionally the same as the C method except that the string should be in C Unicode format. It is generally easier, when using Spreadsheet::WriteExcel, to write unicode strings in C format, see L. The C method is mainly of use in versions of perl prior to 5.8. The following is a simple example showing how to write some Unicode strings in C format: #!/usr/bin/perl -w use strict; use Spreadsheet::WriteExcel; use Unicode::Map(); my $workbook = Spreadsheet::WriteExcel->new('utf_16_be.xls'); my $worksheet = $workbook->add_worksheet(); # Increase the column width for clarity $worksheet->set_column('A:A', 25); # Write a Unicode character # my $smiley = pack 'n', 0x263a; # Increase the font size for legibility. my $big_font = $workbook->add_format(size => 72); $worksheet->write_utf16be_string('A3', $smiley, $big_font); # Write a phrase in Cyrillic using a hex-encoded string # my $str = pack 'H*', '042d0442043e0020044404400430043704300020043d' . '043000200440044304410441043a043e043c0021'; $worksheet->write_utf16be_string('A5', $str); # Map a string to UTF-16BE using an external module. # my $map = Unicode::Map->new('ISO-8859-1'); my $utf16 = $map->to_unicode('Hello world!'); $worksheet->write_utf16be_string('A7', $utf16); You can convert ASCII encodings to the required C format using one of the many Unicode modules on CPAN. For example C and C: L and L. For a full list of the Perl Unicode modules see: L. C is the format most often returned by C modules that generate C. To write C strings in little-endian format use the C method below. The C method was previously called C. That, overly general, name is still supported but deprecated. See also the C programs in the examples directory of the distro. =head2 write_utf16le_string($row, $column, $string, $format) This method is the same as C except that the string should be 16-bit characters in little-endian format. This is generally referred to as C. See L. C data can be changed from little-endian to big-endian format (and vice-versa) as follows: $utf16be = pack 'n*', unpack 'v*', $utf16le; =head2 keep_leading_zeros() This method changes the default handling of integers with leading zeros when using the C method. The C method uses regular expressions to determine what type of data to write to an Excel worksheet. If the data looks like a number it writes a number using C. One problem with this approach is that occasionally data looks like a number but you don't want it treated as a number. Zip codes and ID numbers, for example, often start with a leading zero. If you write this data as a number then the leading zero(s) will be stripped. This is the also the default behaviour when you enter data manually in Excel. To get around this you can use one of three options. Write a formatted number, write the number as a string or use the C method to change the default behaviour of C: # Implicitly write a number, the leading zero is removed: 1209 $worksheet->write('A1', '01209'); # Write a zero padded number using a format: 01209 my $format1 = $workbook->add_format(num_format => '00000'); $worksheet->write('A2', '01209', $format1); # Write explicitly as a string: 01209 $worksheet->write_string('A3', '01209'); # Write implicitly as a string: 01209 $worksheet->keep_leading_zeros(); $worksheet->write('A4', '01209'); The above code would generate a worksheet that looked like the following: ----------------------------------------------------------- | | A | B | C | D | ... ----------------------------------------------------------- | 1 | 1209 | | | | ... | 2 | 01209 | | | | ... | 3 | 01209 | | | | ... | 4 | 01209 | | | | ... The examples are on different sides of the cells due to the fact that Excel displays strings with a left justification and numbers with a right justification by default. You can change this by using a format to justify the data, see L. It should be noted that if the user edits the data in examples C and C the strings will revert back to numbers. Again this is Excel's default behaviour. To avoid this you can use the text format C<@>: # Format as a string (01209) my $format2 = $workbook->add_format(num_format => '@'); $worksheet->write_string('A5', '01209', $format2); The C property is off by default. The C method takes 0 or 1 as an argument. It defaults to 1 if an argument isn't specified: $worksheet->keep_leading_zeros(); # Set on $worksheet->keep_leading_zeros(1); # Set on $worksheet->keep_leading_zeros(0); # Set off See also the C method. =head2 write_blank($row, $column, $format) Write a blank cell specified by C<$row> and C<$column>: $worksheet->write_blank(0, 0, $format); This method is used to add formatting to a cell which doesn't contain a string or number value. Excel differentiates between an "Empty" cell and a "Blank" cell. An "Empty" cell is a cell which doesn't contain data whilst a "Blank" cell is a cell which doesn't contain data but does contain formatting. Excel stores "Blank" cells but ignores "Empty" cells. As such, if you write an empty cell without formatting it is ignored: $worksheet->write('A1', undef, $format); # write_blank() $worksheet->write('A2', undef ); # Ignored This seemingly uninteresting fact means that you can write arrays of data without special treatment for undef or empty string values. See the note about L. =head2 write_row($row, $column, $array_ref, $format) The C method can be used to write a 1D or 2D array of data in one go. This is useful for converting the results of a database query into an Excel worksheet. You must pass a reference to the array of data rather than the array itself. The C method is then called for each element of the data. For example: @array = ('awk', 'gawk', 'mawk'); $array_ref = \@array; $worksheet->write_row(0, 0, $array_ref); # The above example is equivalent to: $worksheet->write(0, 0, $array[0]); $worksheet->write(0, 1, $array[1]); $worksheet->write(0, 2, $array[2]); Note: For convenience the C method behaves in the same way as C if it is passed an array reference. Therefore the following two method calls are equivalent: $worksheet->write_row('A1', $array_ref); # Write a row of data $worksheet->write( 'A1', $array_ref); # Same thing As with all of the write methods the C<$format> parameter is optional. If a format is specified it is applied to all the elements of the data array. Array references within the data will be treated as columns. This allows you to write 2D arrays of data in one go. For example: @eec = ( ['maggie', 'milly', 'molly', 'may' ], [13, 14, 15, 16 ], ['shell', 'star', 'crab', 'stone'] ); $worksheet->write_row('A1', \@eec); Would produce a worksheet as follows: ----------------------------------------------------------- | | A | B | C | D | E | ... ----------------------------------------------------------- | 1 | maggie | 13 | shell | ... | ... | ... | 2 | milly | 14 | star | ... | ... | ... | 3 | molly | 15 | crab | ... | ... | ... | 4 | may | 16 | stone | ... | ... | ... | 5 | ... | ... | ... | ... | ... | ... | 6 | ... | ... | ... | ... | ... | ... To write the data in a row-column order refer to the C method below. Any C values in the data will be ignored unless a format is applied to the data, in which case a formatted blank cell will be written. In either case the appropriate row or column value will still be incremented. To find out more about array references refer to C and C in the main Perl documentation. To find out more about 2D arrays or "lists of lists" refer to C. The C method returns the first error encountered when writing the elements of the data or zero if no errors were encountered. See the return values described for the C method above. See also the C program in the C directory of the distro. The C method allows the following idiomatic conversion of a text file to an Excel file: #!/usr/bin/perl -w use strict; use Spreadsheet::WriteExcel; my $workbook = Spreadsheet::WriteExcel->new('file.xls'); my $worksheet = $workbook->add_worksheet(); open INPUT, 'file.txt' or die "Couldn't open file: $!"; $worksheet->write($.-1, 0, [split]) while ; =head2 write_col($row, $column, $array_ref, $format) The C method can be used to write a 1D or 2D array of data in one go. This is useful for converting the results of a database query into an Excel worksheet. You must pass a reference to the array of data rather than the array itself. The C method is then called for each element of the data. For example: @array = ('awk', 'gawk', 'mawk'); $array_ref = \@array; $worksheet->write_col(0, 0, $array_ref); # The above example is equivalent to: $worksheet->write(0, 0, $array[0]); $worksheet->write(1, 0, $array[1]); $worksheet->write(2, 0, $array[2]); As with all of the write methods the C<$format> parameter is optional. If a format is specified it is applied to all the elements of the data array. Array references within the data will be treated as rows. This allows you to write 2D arrays of data in one go. For example: @eec = ( ['maggie', 'milly', 'molly', 'may' ], [13, 14, 15, 16 ], ['shell', 'star', 'crab', 'stone'] ); $worksheet->write_col('A1', \@eec); Would produce a worksheet as follows: ----------------------------------------------------------- | | A | B | C | D | E | ... ----------------------------------------------------------- | 1 | maggie | milly | molly | may | ... | ... | 2 | 13 | 14 | 15 | 16 | ... | ... | 3 | shell | star | crab | stone | ... | ... | 4 | ... | ... | ... | ... | ... | ... | 5 | ... | ... | ... | ... | ... | ... | 6 | ... | ... | ... | ... | ... | ... To write the data in a column-row order refer to the C method above. Any C values in the data will be ignored unless a format is applied to the data, in which case a formatted blank cell will be written. In either case the appropriate row or column value will still be incremented. As noted above the C method can be used as a synonym for C and C handles nested array refs as columns. Therefore, the following two method calls are equivalent although the more explicit call to C would be preferable for maintainability: $worksheet->write_col('A1', $array_ref ); # Write a column of data $worksheet->write( 'A1', [ $array_ref ]); # Same thing To find out more about array references refer to C and C in the main Perl documentation. To find out more about 2D arrays or "lists of lists" refer to C. The C method returns the first error encountered when writing the elements of the data or zero if no errors were encountered. See the return values described for the C method above. See also the C program in the C directory of the distro. =head2 write_date_time($row, $col, $date_string, $format) The C method can be used to write a date or time to the cell specified by C<$row> and C<$column>: $worksheet->write_date_time('A1', '2004-05-13T23:20', $date_format); The C<$date_string> should be in the following format: yyyy-mm-ddThh:mm:ss.sss This conforms to an ISO8601 date but it should be noted that the full range of ISO8601 formats are not supported. The following variations on the C<$date_string> parameter are permitted: yyyy-mm-ddThh:mm:ss.sss # Standard format yyyy-mm-ddT # No time Thh:mm:ss.sss # No date yyyy-mm-ddThh:mm:ss.sssZ # Additional Z (but not time zones) yyyy-mm-ddThh:mm:ss # No fractional seconds yyyy-mm-ddThh:mm # No seconds Note that the C is required in all cases. A date should always have a C<$format>, otherwise it will appear as a number, see L and L. Here is a typical example: my $date_format = $workbook->add_format(num_format => 'mm/dd/yy'); $worksheet->write_date_time('A1', '2004-05-13T23:20', $date_format); Valid dates should be in the range 1900-01-01 to 9999-12-31, for the 1900 epoch and 1904-01-01 to 9999-12-31, for the 1904 epoch. As with Excel, dates outside these ranges will be written as a string. See also the date_time.pl program in the C directory of the distro. =head2 write_url($row, $col, $url, $label, $format) Write a hyperlink to a URL in the cell specified by C<$row> and C<$column>. The hyperlink is comprised of two elements: the visible label and the invisible link. The visible label is the same as the link unless an alternative label is specified. The parameters C<$label> and the C<$format> are optional and their position is interchangeable. The label is written using the C method. Therefore it is possible to write strings, numbers or formulas as labels. There are four web style URI's supported: C, C, C and C: $worksheet->write_url(0, 0, 'ftp://www.perl.org/' ); $worksheet->write_url(1, 0, 'http://www.perl.com/', 'Perl home' ); $worksheet->write_url('A3', 'http://www.perl.com/', $format ); $worksheet->write_url('A4', 'http://www.perl.com/', 'Perl', $format); $worksheet->write_url('A5', 'mailto:jmcnamara@cpan.org' ); There are two local URIs supported: C and C. These are used for hyperlinks to internal worksheet references or external workbook and worksheet references: $worksheet->write_url('A6', 'internal:Sheet2!A1' ); $worksheet->write_url('A7', 'internal:Sheet2!A1', $format ); $worksheet->write_url('A8', 'internal:Sheet2!A1:B2' ); $worksheet->write_url('A9', q{internal:'Sales Data'!A1} ); $worksheet->write_url('A10', 'external:c:\temp\foo.xls' ); $worksheet->write_url('A11', 'external:c:\temp\foo.xls#Sheet2!A1' ); $worksheet->write_url('A12', 'external:..\..\..\foo.xls' ); $worksheet->write_url('A13', 'external:..\..\..\foo.xls#Sheet2!A1' ); $worksheet->write_url('A13', 'external:\\\\NETWORK\share\foo.xls' ); All of the these URI types are recognised by the C method, see above. Worksheet references are typically of the form C. You can also refer to a worksheet range using the standard Excel notation: C. In external links the workbook and worksheet name must be separated by the C<#> character: C. You can also link to a named range in the target worksheet. For example say you have a named range called C in the workbook C you could link to it as follows: $worksheet->write_url('A14', 'external:c:\temp\foo.xls#my_name'); Note, you cannot currently create named ranges with C. Excel requires that worksheet names containing spaces or non alphanumeric characters are single quoted as follows C<'Sales Data'!A1>. If you need to do this in a single quoted string then you can either escape the single quotes C<\'> or use the quote operator C as described in C in the main Perl documentation. Links to network files are also supported. MS/Novell Network files normally begin with two back slashes as follows C<\\NETWORK\etc>. In order to generate this in a single or double quoted string you will have to escape the backslashes, C<'\\\\NETWORK\etc'>. If you are using double quote strings then you should be careful to escape anything that looks like a metacharacter. For more information see C. Finally, you can avoid most of these quoting problems by using forward slashes. These are translated internally to backslashes: $worksheet->write_url('A14', "external:c:/temp/foo.xls" ); $worksheet->write_url('A15', 'external://NETWORK/share/foo.xls' ); See also, the note about L. =head2 write_url_range($row1, $col1, $row2, $col2, $url, $string, $format) This method is essentially the same as the C method described above. The main difference is that you can specify a link for a range of cells: $worksheet->write_url(0, 0, 0, 3, 'ftp://www.perl.org/' ); $worksheet->write_url(1, 0, 0, 3, 'http://www.perl.com/', 'Perl home'); $worksheet->write_url('A3:D3', 'internal:Sheet2!A1' ); $worksheet->write_url('A4:D4', 'external:c:\temp\foo.xls' ); This method is generally only required when used in conjunction with merged cells. See the C method and the C property of a Format object, L. There is no way to force this behaviour through the C method. The parameters C<$string> and the C<$format> are optional and their position is interchangeable. However, they are applied only to the first cell in the range. See also, the note about L. =head2 write_formula($row, $column, $formula, $format, $value) Write a formula or function to the cell specified by C<$row> and C<$column>: $worksheet->write_formula(0, 0, '=$B$3 + B4' ); $worksheet->write_formula(1, 0, '=SIN(PI()/4)'); $worksheet->write_formula(2, 0, '=SUM(B1:B5)' ); $worksheet->write_formula('A4', '=IF(A3>1,"Yes", "No")' ); $worksheet->write_formula('A5', '=AVERAGE(1, 2, 3, 4)' ); $worksheet->write_formula('A6', '=DATEVALUE("1-Jan-2001")'); See the note about L. For more information about writing Excel formulas see L See also the section "Improving performance when working with formulas" and the C and C methods. If required, it is also possible to specify the calculated value of the formula. This is occasionally necessary when working with non-Excel applications that don't calculate the value of the formula. The calculated C<$value> is added at the end of the argument list: $worksheet->write('A1', '=2+2', $format, 4); However, this probably isn't something that will ever need to do. If you do use this feature then do so with care. =head2 store_formula($formula) The C method is used in conjunction with C to speed up the generation of repeated formulas. See "Improving performance when working with formulas" in L. The C method pre-parses a textual representation of a formula and stores it for use at a later stage by the C method. C carries the same speed penalty as C. However, in practice it will be used less frequently. The return value of this method is a scalar that can be thought of as a reference to a formula. my $sin = $worksheet->store_formula('=SIN(A1)'); my $cos = $worksheet->store_formula('=COS(A1)'); $worksheet->repeat_formula('B1', $sin, $format, 'A1', 'A2'); $worksheet->repeat_formula('C1', $cos, $format, 'A1', 'A2'); Although C is a worksheet method the return value can be used in any worksheet: my $now = $worksheet->store_formula('=NOW()'); $worksheet1->repeat_formula('B1', $now); $worksheet2->repeat_formula('B1', $now); $worksheet3->repeat_formula('B1', $now); =head2 repeat_formula($row, $col, $formula, $format, ($pattern => $replace, ...)) The C method is used in conjunction with C to speed up the generation of repeated formulas. See "Improving performance when working with formulas" in L. In many respects C behaves like C except that it is significantly faster. The C method creates a new formula based on the pre-parsed tokens returned by C. The new formula is generated by substituting C<$pattern>, C<$replace> pairs in the stored formula: my $formula = $worksheet->store_formula('=A1 * 3 + 50'); for my $row (0..99) { $worksheet->repeat_formula($row, 1, $formula, $format, 'A1', 'A'.($row +1)); } It should be noted that C doesn't modify the tokens. In the above example the substitution is always made against the original token, C, which doesn't change. As usual, you can use C if you don't wish to specify a C<$format>: $worksheet->repeat_formula('B2', $formula, $format, 'A1', 'A2'); $worksheet->repeat_formula('B3', $formula, undef, 'A1', 'A3'); The substitutions are made from left to right and you can use as many C<$pattern>, C<$replace> pairs as you need. However, each substitution is made only once: my $formula = $worksheet->store_formula('=A1 + A1'); # Gives '=B1 + A1' $worksheet->repeat_formula('B1', $formula, undef, 'A1', 'B1'); # Gives '=B1 + B1' $worksheet->repeat_formula('B2', $formula, undef, ('A1', 'B1') x 2); Since the C<$pattern> is interpolated each time that it is used it is worth using the C operator to quote the pattern. The C operator is explained in the C man page. $worksheet->repeat_formula('B1', $formula, $format, qr/A1/, 'A2'); Care should be taken with the values that are substituted. The formula returned by C contains several other tokens in addition to those in the formula and these might also match the pattern that you are trying to replace. In particular you should avoid substituting a single 0, 1, 2 or 3. You should also be careful to avoid false matches. For example the following snippet is meant to change the stored formula in steps from C<=A1 + SIN(A1)> to C<=A10 + SIN(A10)>. my $formula = $worksheet->store_formula('=A1 + SIN(A1)'); for my $row (1 .. 10) { $worksheet->repeat_formula($row -1, 1, $formula, undef, qw/A1/, 'A' . $row, #! Bad. qw/A1/, 'A' . $row #! Bad. ); } However it contains a bug. In the last iteration of the loop when C<$row> is 10 the following substitutions will occur: s/A1/A10/; changes =A1 + SIN(A1) to =A10 + SIN(A1) s/A1/A10/; changes =A10 + SIN(A1) to =A100 + SIN(A1) # !! The solution in this case is to use a more explicit match such as C: $worksheet->repeat_formula($row -1, 1, $formula, undef, qw/^A1$/, 'A' . $row, qw/^A1$/, 'A' . $row ); Another similar problem occurs due to the fact that substitutions are made in order. For example the following snippet is meant to change the stored formula from C<=A10 + A11> to C<=A11 + A12>: my $formula = $worksheet->store_formula('=A10 + A11'); $worksheet->repeat_formula('A1', $formula, undef, qw/A10/, 'A11', #! Bad. qw/A11/, 'A12' #! Bad. ); However, the actual substitution yields C<=A12 + A11>: s/A10/A11/; changes =A10 + A11 to =A11 + A11 s/A11/A12/; changes =A11 + A11 to =A12 + A11 # !! The solution here would be to reverse the order of the substitutions or to start with a stored formula that won't yield a false match such as C<=X10 + Y11>: my $formula = $worksheet->store_formula('=X10 + Y11'); $worksheet->repeat_formula('A1', $formula, undef, qw/X10/, 'A11', qw/Y11/, 'A12' ); If you think that you have a problem related to a false match you can check the tokens that you are substituting against as follows. my $formula = $worksheet->store_formula('=A1*5+4'); print "@$formula\n"; See also the C program in the C directory of the distro. =head2 write_comment($row, $column, $string, ...) The C method is used to add a comment to a cell. A cell comment is indicated in Excel by a small red triangle in the upper right-hand corner of the cell. Moving the cursor over the red triangle will reveal the comment. The following example shows how to add a comment to a cell: $worksheet->write (2, 2, 'Hello'); $worksheet->write_comment(2, 2, 'This is a comment.'); As usual you can replace the C<$row> and C<$column> parameters with an C cell reference. See the note about L. $worksheet->write ('C3', 'Hello'); $worksheet->write_comment('C3', 'This is a comment.'); On systems with C and later the C method will also handle strings in C format. $worksheet->write_comment('C3', "\x{263a}"); # Smiley $worksheet->write_comment('C4', 'Comment ca va?'); In addition to the basic 3 argument form of C you can pass in several optional key/value pairs to control the format of the comment. For example: $worksheet->write_comment('C3', 'Hello', visible => 1, author => 'Perl'); Most of these options are quite specific and in general the default comment behaviour will be all that you need. However, should you need greater control over the format of the cell comment the following options are available: encoding author author_encoding visible x_scale width y_scale height color start_cell start_row start_col x_offset y_offset =over 4 =item Option: encoding This option is used to indicate that the comment string is encoded as C. my $comment = pack 'n', 0x263a; # UTF-16BE Smiley symbol $worksheet->write_comment('C3', $comment, encoding => 1); If you wish to use Unicode characters in the comment string then the preferred method is to use perl 5.8 and C strings, see L. =item Option: author This option is used to indicate who the author of the comment is. Excel displays the author of the comment in the status bar at the bottom of the worksheet. This is usually of interest in corporate environments where several people might review and provide comments to a workbook. $worksheet->write_comment('C3', 'Atonement', author => 'Ian McEwan'); =item Option: author_encoding This option is used to indicate that the author string is encoded as C. =item Option: visible This option is used to make a cell comment visible when the worksheet is opened. The default behaviour in Excel is that comments are initially hidden. However, it is also possible in Excel to make individual or all comments visible. In Spreadsheet::WriteExcel individual comments can be made visible as follows: $worksheet->write_comment('C3', 'Hello', visible => 1); It is possible to make all comments in a worksheet visible using the C worksheet method (see below). Alternatively, if all of the cell comments have been made visible you can hide individual comments: $worksheet->write_comment('C3', 'Hello', visible => 0); =item Option: x_scale This option is used to set the width of the cell comment box as a factor of the default width. $worksheet->write_comment('C3', 'Hello', x_scale => 2); $worksheet->write_comment('C4', 'Hello', x_scale => 4.2); =item Option: width This option is used to set the width of the cell comment box explicitly in pixels. $worksheet->write_comment('C3', 'Hello', width => 200); =item Option: y_scale This option is used to set the height of the cell comment box as a factor of the default height. $worksheet->write_comment('C3', 'Hello', y_scale => 2); $worksheet->write_comment('C4', 'Hello', y_scale => 4.2); =item Option: height This option is used to set the height of the cell comment box explicitly in pixels. $worksheet->write_comment('C3', 'Hello', height => 200); =item Option: color This option is used to set the background colour of cell comment box. You can use one of the named colours recognised by Spreadsheet::WriteExcel or a colour index. See L. $worksheet->write_comment('C3', 'Hello', color => 'green'); $worksheet->write_comment('C4', 'Hello', color => 0x35); # Orange =item Option: start_cell This option is used to set the cell in which the comment will appear. By default Excel displays comments one cell to the right and one cell above the cell to which the comment relates. However, you can change this behaviour if you wish. In the following example the comment which would appear by default in cell C is moved to C. $worksheet->write_comment('C3', 'Hello', start_cell => 'E2'); =item Option: start_row This option is used to set the row in which the comment will appear. See the C option above. The row is zero indexed. $worksheet->write_comment('C3', 'Hello', start_row => 0); =item Option: start_col This option is used to set the column in which the comment will appear. See the C option above. The column is zero indexed. $worksheet->write_comment('C3', 'Hello', start_col => 4); =item Option: x_offset This option is used to change the x offset, in pixels, of a comment within a cell: $worksheet->write_comment('C3', $comment, x_offset => 30); =item Option: y_offset This option is used to change the y offset, in pixels, of a comment within a cell: $worksheet->write_comment('C3', $comment, x_offset => 30); =back You can apply as many of these options as you require. See also L. =head2 show_comments() This method is used to make all cell comments visible when a worksheet is opened. Individual comments can be made visible using the C parameter of the C method (see above): $worksheet->write_comment('C3', 'Hello', visible => 1); If all of the cell comments have been made visible you can hide individual comments as follows: $worksheet->write_comment('C3', 'Hello', visible => 0); =head2 add_write_handler($re, $code_ref) This method is used to extend the Spreadsheet::WriteExcel write() method to handle user defined data. If you refer to the section on C above you will see that it acts as an alias for several more specific C methods. However, it doesn't always act in exactly the way that you would like it to. One solution is to filter the input data yourself and call the appropriate C method. Another approach is to use the C method to add your own automated behaviour to C. The C method take two arguments, C<$re>, a regular expression to match incoming data and C<$code_ref> a callback function to handle the matched data: $worksheet->add_write_handler(qr/^\d\d\d\d$/, \&my_write); (In the these examples the C operator is used to quote the regular expression strings, see L for more details). The method is used as follows. say you wished to write 7 digit ID numbers as a string so that any leading zeros were preserved*, you could do something like the following: $worksheet->add_write_handler(qr/^\d{7}$/, \&write_my_id); sub write_my_id { my $worksheet = shift; return $worksheet->write_string(@_); } * You could also use the C method for this. Then if you call C with an appropriate string it will be handled automatically: # Writes 0000000. It would normally be written as a number; 0. $worksheet->write('A1', '0000000'); The callback function will receive a reference to the calling worksheet and all of the other arguments that were passed to C. The callback will see an C<@_> argument list that looks like the following: $_[0] A ref to the calling worksheet. * $_[1] Zero based row number. $_[2] Zero based column number. $_[3] A number or string or token. $_[4] A format ref if any. $_[5] Any other arguments. ... * It is good style to shift this off the list so the @_ is the same as the argument list seen by write(). Your callback should C the return value of the C method that was called or C to indicate that you rejected the match and want C to continue as normal. So for example if you wished to apply the previous filter only to ID values that occur in the first column you could modify your callback function as follows: sub write_my_id { my $worksheet = shift; my $col = $_[1]; if ($col == 0) { return $worksheet->write_string(@_); } else { # Reject the match and return control to write() return undef; } } Now, you will get different behaviour for the first column and other columns: $worksheet->write('A1', '0000000'); # Writes 0000000 $worksheet->write('B1', '0000000'); # Writes 0 You may add more than one handler in which case they will be called in the order that they were added. Note, the C method is particularly suited for handling dates. See the C programs in the C directory for further examples. =head2 insert_image($row, $col, $filename, $x, $y, $scale_x, $scale_y) This method can be used to insert a image into a worksheet. The image can be in PNG, JPEG or BMP format. The C<$x>, C<$y>, C<$scale_x> and C<$scale_y> parameters are optional. $worksheet1->insert_image('A1', 'perl.bmp'); $worksheet2->insert_image('A1', '../images/perl.bmp'); $worksheet3->insert_image('A1', '.c:\images\perl.bmp'); The parameters C<$x> and C<$y> can be used to specify an offset from the top left hand corner of the cell specified by C<$row> and C<$col>. The offset values are in pixels. $worksheet1->insert_image('A1', 'perl.bmp', 32, 10); The default width of a cell is 63 pixels. The default height of a cell is 17 pixels. The pixels offsets can be calculated using the following relationships: Wp = int(12We) if We < 1 Wp = int(7We +5) if We >= 1 Hp = int(4/3He) where: We is the cell width in Excels units Wp is width in pixels He is the cell height in Excels units Hp is height in pixels The offsets can be greater than the width or height of the underlying cell. This can be occasionally useful if you wish to align two or more images relative to the same cell. The parameters C<$scale_x> and C<$scale_y> can be used to scale the inserted image horizontally and vertically: # Scale the inserted image: width x 2.0, height x 0.8 $worksheet->insert_image('A1', 'perl.bmp', 0, 0, 2, 0.8); See also the C program in the C directory of the distro. BMP images must be 24 bit, true colour, bitmaps. In general it is best to avoid BMP images since they aren't compressed. The older C method is still supported but deprecated. See also L. =head2 insert_chart($row, $col, $chart, $x, $y, $scale_x, $scale_y) This method can be used to insert a Chart object into a worksheet. The Chart must be created by the C Workbook method and it must have the C option set. my $chart = $workbook->add_chart( type => 'line', embedded => 1 ); # Configure the chart. ... # Insert the chart into the a worksheet. $worksheet->insert_chart('E2', $chart); See C for details on how to create the Chart object and L for details on how to configure it. See also the C programs in the examples directory of the distro. The C<$x>, C<$y>, C<$scale_x> and C<$scale_y> parameters are optional. The parameters C<$x> and C<$y> can be used to specify an offset from the top left hand corner of the cell specified by C<$row> and C<$col>. The offset values are in pixels. See the C method above for more information on sizes. $worksheet1->insert_chart('E2', $chart, 3, 3); The parameters C<$scale_x> and C<$scale_y> can be used to scale the inserted image horizontally and vertically: # Scale the width by 120% and the height by 150% $worksheet->insert_chart('E2', $chart, 0, 0, 1.2, 1.5); The easiest way to calculate the required scaling is to create a test chart worksheet with Spreadsheet::WriteExcel. Then open the file, select the chart and drag the corner to get the required size. While holding down the mouse the scale of the resized chart is shown to the left of the formula bar. See also L. =head2 embed_chart($row, $col, $filename, $x, $y, $scale_x, $scale_y) This method can be used to insert a externally generated chart into a worksheet. The chart must first be extracted from an existing Excel file. This feature is semi-deprecated in favour of the "native" charts created using C. Read C (or C<.pod>) in the external_charts directory of the distro for a full explanation. Here is an example: $worksheet->embed_chart('B2', 'sales_chart.bin'); The C<$x>, C<$y>, C<$scale_x> and C<$scale_y> parameters are optional. See C above for details. =head2 data_validation() The C method is used to construct an Excel data validation or to limit the user input to a dropdown list of values. $worksheet->data_validation('B3', { validate => 'integer', criteria => '>', value => 100, }); $worksheet->data_validation('B5:B9', { validate => 'list', value => ['open', 'high', 'close'], }); This method contains a lot of parameters and is described in detail in a separate section L. See also the C program in the examples directory of the distro =head2 get_name() The C method is used to retrieve the name of a worksheet. For example: foreach my $sheet ($workbook->sheets()) { print $sheet->get_name(); } For reasons related to the design of Spreadsheet::WriteExcel and to the internals of Excel there is no C method. The only way to set the worksheet name is via the C method. =head2 activate() The C method is used to specify which worksheet is initially visible in a multi-sheet workbook: $worksheet1 = $workbook->add_worksheet('To'); $worksheet2 = $workbook->add_worksheet('the'); $worksheet3 = $workbook->add_worksheet('wind'); $worksheet3->activate(); This is similar to the Excel VBA activate method. More than one worksheet can be selected via the C method, see below, however only one worksheet can be active. The default active worksheet is the first worksheet. =head2 select() The C method is used to indicate that a worksheet is selected in a multi-sheet workbook: $worksheet1->activate(); $worksheet2->select(); $worksheet3->select(); A selected worksheet has its tab highlighted. Selecting worksheets is a way of grouping them together so that, for example, several worksheets could be printed in one go. A worksheet that has been activated via the C method will also appear as selected. =head2 hide() The C method is used to hide a worksheet: $worksheet2->hide(); You may wish to hide a worksheet in order to avoid confusing a user with intermediate data or calculations. A hidden worksheet can not be activated or selected so this method is mutually exclusive with the C and C methods. In addition, since the first worksheet will default to being the active worksheet, you cannot hide the first worksheet without activating another sheet: $worksheet2->activate(); $worksheet1->hide(); =head2 set_first_sheet() The C method determines which worksheet is initially selected. However, if there are a large number of worksheets the selected worksheet may not appear on the screen. To avoid this you can select which is the leftmost visible worksheet using C: for (1..20) { $workbook->add_worksheet; } $worksheet21 = $workbook->add_worksheet(); $worksheet22 = $workbook->add_worksheet(); $worksheet21->set_first_sheet(); $worksheet22->activate(); This method is not required very often. The default value is the first worksheet. =head2 protect($password) The C method is used to protect a worksheet from modification: $worksheet->protect(); It can be turned off in Excel via the CProtection-EUnprotect Sheet> menu command. The C method also has the effect of enabling a cell's C and C properties if they have been set. A "locked" cell cannot be edited. A "hidden" cell will display the results of a formula but not the formula itself. In Excel a cell's locked property is on by default. # Set some format properties my $unlocked = $workbook->add_format(locked => 0); my $hidden = $workbook->add_format(hidden => 1); # Enable worksheet protection $worksheet->protect(); # This cell cannot be edited, it is locked by default $worksheet->write('A1', '=1+2'); # This cell can be edited $worksheet->write('A2', '=1+2', $unlocked); # The formula in this cell isn't visible $worksheet->write('A3', '=1+2', $hidden); See also the C and C format methods in L. You can optionally add a password to the worksheet protection: $worksheet->protect('drowssap'); Note, the worksheet level password in Excel provides very weak protection. It does not encrypt your data in any way and it is very easy to deactivate. Therefore, do not use the above method if you wish to protect sensitive data or calculations. However, before you get worried, Excel's own workbook level password protection does provide strong encryption in Excel 97+. For technical reasons this will never be supported by C. =head2 set_selection($first_row, $first_col, $last_row, $last_col) This method can be used to specify which cell or cells are selected in a worksheet. The most common requirement is to select a single cell, in which case C<$last_row> and C<$last_col> can be omitted. The active cell within a selected range is determined by the order in which C<$first> and C<$last> are specified. It is also possible to specify a cell or a range using A1 notation. See the note about L. Examples: $worksheet1->set_selection(3, 3); # 1. Cell D4. $worksheet2->set_selection(3, 3, 6, 6); # 2. Cells D4 to G7. $worksheet3->set_selection(6, 6, 3, 3); # 3. Cells G7 to D4. $worksheet4->set_selection('D4'); # Same as 1. $worksheet5->set_selection('D4:G7'); # Same as 2. $worksheet6->set_selection('G7:D4'); # Same as 3. The default cell selections is (0, 0), 'A1'. =head2 set_row($row, $height, $format, $hidden, $level, $collapsed) This method can be used to change the default properties of a row. All parameters apart from C<$row> are optional. The most common use for this method is to change the height of a row: $worksheet->set_row(0, 20); # Row 1 height set to 20 If you wish to set the format without changing the height you can pass C as the height parameter: $worksheet->set_row(0, undef, $format); The C<$format> parameter will be applied to any cells in the row that don't have a format. For example $worksheet->set_row(0, undef, $format1); # Set the format for row 1 $worksheet->write('A1', 'Hello'); # Defaults to $format1 $worksheet->write('B1', 'Hello', $format2); # Keeps $format2 If you wish to define a row format in this way you should call the method before any calls to C. Calling it afterwards will overwrite any format that was previously specified. The C<$hidden> parameter should be set to 1 if you wish to hide a row. This can be used, for example, to hide intermediary steps in a complicated calculation: $worksheet->set_row(0, 20, $format, 1); $worksheet->set_row(1, undef, undef, 1); The C<$level> parameter is used to set the outline level of the row. Outlines are described in L. Adjacent rows with the same outline level are grouped together into a single outline. The following example sets an outline level of 1 for rows 1 and 2 (zero-indexed): $worksheet->set_row(1, undef, undef, 0, 1); $worksheet->set_row(2, undef, undef, 0, 1); The C<$hidden> parameter can also be used to hide collapsed outlined rows when used in conjunction with the C<$level> parameter. $worksheet->set_row(1, undef, undef, 1, 1); $worksheet->set_row(2, undef, undef, 1, 1); For collapsed outlines you should also indicate which row has the collapsed C<+> symbol using the optional C<$collapsed> parameter. $worksheet->set_row(3, undef, undef, 0, 0, 1); For a more complete example see the C and C programs in the examples directory of the distro. Excel allows up to 7 outline levels. Therefore the C<$level> parameter should be in the range C<0 E= $level E= 7>. =head2 set_column($first_col, $last_col, $width, $format, $hidden, $level, $collapsed) This method can be used to change the default properties of a single column or a range of columns. All parameters apart from C<$first_col> and C<$last_col> are optional. If C is applied to a single column the value of C<$first_col> and C<$last_col> should be the same. In the case where C<$last_col> is zero it is set to the same value as C<$first_col>. It is also possible, and generally clearer, to specify a column range using the form of A1 notation used for columns. See the note about L. Examples: $worksheet->set_column(0, 0, 20); # Column A width set to 20 $worksheet->set_column(1, 3, 30); # Columns B-D width set to 30 $worksheet->set_column('E:E', 20); # Column E width set to 20 $worksheet->set_column('F:H', 30); # Columns F-H width set to 30 The width corresponds to the column width value that is specified in Excel. It is approximately equal to the length of a string in the default font of Arial 10. Unfortunately, there is no way to specify "AutoFit" for a column in the Excel file format. This feature is only available at runtime from within Excel. As usual the C<$format> parameter is optional, for additional information, see L. If you wish to set the format without changing the width you can pass C as the width parameter: $worksheet->set_column(0, 0, undef, $format); The C<$format> parameter will be applied to any cells in the column that don't have a format. For example $worksheet->set_column('A:A', undef, $format1); # Set format for col 1 $worksheet->write('A1', 'Hello'); # Defaults to $format1 $worksheet->write('A2', 'Hello', $format2); # Keeps $format2 If you wish to define a column format in this way you should call the method before any calls to C. If you call it afterwards it won't have any effect. A default row format takes precedence over a default column format $worksheet->set_row(0, undef, $format1); # Set format for row 1 $worksheet->set_column('A:A', undef, $format2); # Set format for col 1 $worksheet->write('A1', 'Hello'); # Defaults to $format1 $worksheet->write('A2', 'Hello'); # Defaults to $format2 The C<$hidden> parameter should be set to 1 if you wish to hide a column. This can be used, for example, to hide intermediary steps in a complicated calculation: $worksheet->set_column('D:D', 20, $format, 1); $worksheet->set_column('E:E', undef, undef, 1); The C<$level> parameter is used to set the outline level of the column. Outlines are described in L. Adjacent columns with the same outline level are grouped together into a single outline. The following example sets an outline level of 1 for columns B to G: $worksheet->set_column('B:G', undef, undef, 0, 1); The C<$hidden> parameter can also be used to hide collapsed outlined columns when used in conjunction with the C<$level> parameter. $worksheet->set_column('B:G', undef, undef, 1, 1); For collapsed outlines you should also indicate which row has the collapsed C<+> symbol using the optional C<$collapsed> parameter. $worksheet->set_column('H:H', undef, undef, 0, 0, 1); For a more complete example see the C and C programs in the examples directory of the distro. Excel allows up to 7 outline levels. Therefore the C<$level> parameter should be in the range C<0 E= $level E= 7>. =head2 outline_settings($visible, $symbols_below, $symbols_right, $auto_style) The C method is used to control the appearance of outlines in Excel. Outlines are described in L. The C<$visible> parameter is used to control whether or not outlines are visible. Setting this parameter to 0 will cause all outlines on the worksheet to be hidden. They can be unhidden in Excel by means of the "Show Outline Symbols" command button. The default setting is 1 for visible outlines. $worksheet->outline_settings(0); The C<$symbols_below> parameter is used to control whether the row outline symbol will appear above or below the outline level bar. The default setting is 1 for symbols to appear below the outline level bar. The C parameter is used to control whether the column outline symbol will appear to the left or the right of the outline level bar. The default setting is 1 for symbols to appear to the right of the outline level bar. The C<$auto_style> parameter is used to control whether the automatic outline generator in Excel uses automatic styles when creating an outline. This has no effect on a file generated by C but it does have an effect on how the worksheet behaves after it is created. The default setting is 0 for "Automatic Styles" to be turned off. The default settings for all of these parameters correspond to Excel's default parameters. The worksheet parameters controlled by C are rarely used. =head2 freeze_panes($row, $col, $top_row, $left_col) This method can be used to divide a worksheet into horizontal or vertical regions known as panes and to also "freeze" these panes so that the splitter bars are not visible. This is the same as the CFreeze Panes> menu command in Excel The parameters C<$row> and C<$col> are used to specify the location of the split. It should be noted that the split is specified at the top or left of a cell and that the method uses zero based indexing. Therefore to freeze the first row of a worksheet it is necessary to specify the split at row 2 (which is 1 as the zero-based index). This might lead you to think that you are using a 1 based index but this is not the case. You can set one of the C<$row> and C<$col> parameters as zero if you do not want either a vertical or horizontal split. Examples: $worksheet->freeze_panes(1, 0); # Freeze the first row $worksheet->freeze_panes('A2'); # Same using A1 notation $worksheet->freeze_panes(0, 1); # Freeze the first column $worksheet->freeze_panes('B1'); # Same using A1 notation $worksheet->freeze_panes(1, 2); # Freeze first row and first 2 columns $worksheet->freeze_panes('C2'); # Same using A1 notation The parameters C<$top_row> and C<$left_col> are optional. They are used to specify the top-most or left-most visible row or column in the scrolling region of the panes. For example to freeze the first row and to have the scrolling region begin at row twenty: $worksheet->freeze_panes(1, 0, 20, 0); You cannot use A1 notation for the C<$top_row> and C<$left_col> parameters. See also the C program in the C directory of the distribution. =head2 split_panes($y, $x, $top_row, $left_col) This method can be used to divide a worksheet into horizontal or vertical regions known as panes. This method is different from the C method in that the splits between the panes will be visible to the user and each pane will have its own scroll bars. The parameters C<$y> and C<$x> are used to specify the vertical and horizontal position of the split. The units for C<$y> and C<$x> are the same as those used by Excel to specify row height and column width. However, the vertical and horizontal units are different from each other. Therefore you must specify the C<$y> and C<$x> parameters in terms of the row heights and column widths that you have set or the default values which are C<12.75> for a row and C<8.43> for a column. You can set one of the C<$y> and C<$x> parameters as zero if you do not want either a vertical or horizontal split. The parameters C<$top_row> and C<$left_col> are optional. They are used to specify the top-most or left-most visible row or column in the bottom-right pane. Example: $worksheet->split_panes(12.75, 0, 1, 0); # First row $worksheet->split_panes(0, 8.43, 0, 1); # First column $worksheet->split_panes(12.75, 8.43, 1, 1); # First row and column You cannot use A1 notation with this method. See also the C method and the C program in the C directory of the distribution. Note: This C method was called C in older versions. The older name is still available for backwards compatibility. =head2 merge_range($first_row, $first_col, $last_row, $last_col, $token, $format, $utf_16_be) Merging cells can be achieved by setting the C property of a Format object, see L. However, this only allows simple Excel5 style horizontal merging which Excel refers to as "center across selection". The C method allows you to do Excel97+ style formatting where the cells can contain other types of alignment in addition to the merging: my $format = $workbook->add_format( border => 6, valign => 'vcenter', align => 'center', ); $worksheet->merge_range('B3:D4', 'Vertical and horizontal', $format); B. The format object that is used with a C method call is marked internally as being associated with a merged range. It is a fatal error to use a merged format in a non-merged cell. Instead you should use separate formats for merged and non-merged cells. This restriction will be removed in a future release. The C<$utf_16_be> parameter is optional, see below. C writes its C<$token> argument using the worksheet C method. Therefore it will handle numbers, strings, formulas or urls as required. Setting the C property of the format isn't required when you are using C. In fact using it will exclude the use of any other horizontal alignment option. On systems with C and later the C method will also handle strings in C format. $worksheet->merge_range('B3:D4', "\x{263a}", $format); # Smiley On earlier Perl systems your can specify C worksheet names using an additional optional parameter: my $str = pack 'n', 0x263a; $worksheet->merge_range('B3:D4', $str, $format, 1); # Smiley The full possibilities of this method are shown in the C to C programs in the C directory of the distribution. =head2 set_zoom($scale) Set the worksheet zoom factor in the range C<10 E= $scale E= 400>: $worksheet1->set_zoom(50); $worksheet2->set_zoom(75); $worksheet3->set_zoom(300); $worksheet4->set_zoom(400); The default zoom factor is 100. You cannot zoom to "Selection" because it is calculated by Excel at run-time. Note, C does not affect the scale of the printed page. For that you should use C. =head2 right_to_left() The C method is used to change the default direction of the worksheet from left-to-right, with the A1 cell in the top left, to right-to-left, with the he A1 cell in the top right. $worksheet->right_to_left(); This is useful when creating Arabic, Hebrew or other near or far eastern worksheets that use right-to-left as the default direction. =head2 hide_zero() The C method is used to hide any zero values that appear in cells. $worksheet->hide_zero(); In Excel this option is found under Tools->Options->View. =head2 set_tab_color() The C method is used to change the colour of the worksheet tab. This feature is only available in Excel 2002 and later. You can use one of the standard colour names provided by the Format object or a colour index. See L and the C method. $worksheet1->set_tab_color('red'); $worksheet2->set_tab_color(0x0C); See the C program in the examples directory of the distro. =head2 autofilter($first_row, $first_col, $last_row, $last_col) This method allows an autofilter to be added to a worksheet. An autofilter is a way of adding drop down lists to the headers of a 2D range of worksheet data. This in turn allow users to filter the data based on simple criteria so that some data is shown and some is hidden. To add an autofilter to a worksheet: $worksheet->autofilter(0, 0, 10, 3); $worksheet->autofilter('A1:D11'); # Same as above in A1 notation. Filter conditions can be applied using the C method. See the C program in the examples directory of the distro for a more detailed example. =head2 filter_column($column, $expression) The C method can be used to filter columns in a autofilter range based on simple conditions. B It isn't sufficient to just specify the filter condition. You must also hide any rows that don't match the filter condition. Rows are hidden using the C C parameter. C cannot do this automatically since it isn't part of the file format. See the C program in the examples directory of the distro for an example. The conditions for the filter are specified using simple expressions: $worksheet->filter_column('A', 'x > 2000'); $worksheet->filter_column('B', 'x > 2000 and x < 5000'); The C<$column> parameter can either be a zero indexed column number or a string column name. The following operators are available: Operator Synonyms == = eq =~ != <> ne != > < >= <= and && or || The operator synonyms are just syntactic sugar to make you more comfortable using the expressions. It is important to remember that the expressions will be interpreted by Excel and not by perl. An expression can comprise a single statement or two statements separated by the C and C operators. For example: 'x < 2000' 'x > 2000' 'x == 2000' 'x > 2000 and x < 5000' 'x == 2000 or x == 5000' Filtering of blank or non-blank data can be achieved by using a value of C or C in the expression: 'x == Blanks' 'x == NonBlanks' Top 10 style filters can be specified using a expression like the following: Top|Bottom 1-500 Items|% For example: 'Top 10 Items' 'Bottom 5 Items' 'Top 25 %' 'Bottom 50 %' Excel also allows some simple string matching operations: 'x =~ b*' # begins with b 'x !~ b*' # doesn't begin with b 'x =~ *b' # ends with b 'x !~ *b' # doesn't end with b 'x =~ *b*' # contains b 'x !~ *b*' # doesn't contains b You can also use C<*> to match any character or number and C to match any single character or number. No other regular expression quantifier is supported by Excel's filters. Excel's regular expression characters can be escaped using C<~>. The placeholder variable C in the above examples can be replaced by any simple string. The actual placeholder name is ignored internally so the following are all equivalent: 'x < 2000' 'col < 2000' 'Price < 2000' Also, note that a filter condition can only be applied to a column in a range specified by the C Worksheet method. See the C program in the examples directory of the distro for a more detailed example. =head1 PAGE SET-UP METHODS Page set-up methods affect the way that a worksheet looks when it is printed. They control features such as page headers and footers and margins. These methods are really just standard worksheet methods. They are documented here in a separate section for the sake of clarity. The following methods are available for page set-up: set_landscape() set_portrait() set_page_view() set_paper() center_horizontally() center_vertically() set_margins() set_header() set_footer() repeat_rows() repeat_columns() hide_gridlines() print_row_col_headers() print_area() print_across() fit_to_pages() set_start_page() set_print_scale() set_h_pagebreaks() set_v_pagebreaks() A common requirement when working with Spreadsheet::WriteExcel is to apply the same page set-up features to all of the worksheets in a workbook. To do this you can use the C method of the C class to access the array of worksheets in a workbook: foreach $worksheet ($workbook->sheets()) { $worksheet->set_landscape(); } =head2 set_landscape() This method is used to set the orientation of a worksheet's printed page to landscape: $worksheet->set_landscape(); # Landscape mode =head2 set_portrait() This method is used to set the orientation of a worksheet's printed page to portrait. The default worksheet orientation is portrait, so you won't generally need to call this method. $worksheet->set_portrait(); # Portrait mode =head2 set_page_view() This method is used to display the worksheet in "Page View" mode. This is currently only supported by Mac Excel, where it is the default. $worksheet->set_page_view(); =head2 set_paper($index) This method is used to set the paper format for the printed output of a worksheet. The following paper styles are available: Index Paper format Paper size ===== ============ ========== 0 Printer default - 1 Letter 8 1/2 x 11 in 2 Letter Small 8 1/2 x 11 in 3 Tabloid 11 x 17 in 4 Ledger 17 x 11 in 5 Legal 8 1/2 x 14 in 6 Statement 5 1/2 x 8 1/2 in 7 Executive 7 1/4 x 10 1/2 in 8 A3 297 x 420 mm 9 A4 210 x 297 mm 10 A4 Small 210 x 297 mm 11 A5 148 x 210 mm 12 B4 250 x 354 mm 13 B5 182 x 257 mm 14 Folio 8 1/2 x 13 in 15 Quarto 215 x 275 mm 16 - 10x14 in 17 - 11x17 in 18 Note 8 1/2 x 11 in 19 Envelope 9 3 7/8 x 8 7/8 20 Envelope 10 4 1/8 x 9 1/2 21 Envelope 11 4 1/2 x 10 3/8 22 Envelope 12 4 3/4 x 11 23 Envelope 14 5 x 11 1/2 24 C size sheet - 25 D size sheet - 26 E size sheet - 27 Envelope DL 110 x 220 mm 28 Envelope C3 324 x 458 mm 29 Envelope C4 229 x 324 mm 30 Envelope C5 162 x 229 mm 31 Envelope C6 114 x 162 mm 32 Envelope C65 114 x 229 mm 33 Envelope B4 250 x 353 mm 34 Envelope B5 176 x 250 mm 35 Envelope B6 176 x 125 mm 36 Envelope 110 x 230 mm 37 Monarch 3.875 x 7.5 in 38 Envelope 3 5/8 x 6 1/2 in 39 Fanfold 14 7/8 x 11 in 40 German Std Fanfold 8 1/2 x 12 in 41 German Legal Fanfold 8 1/2 x 13 in Note, it is likely that not all of these paper types will be available to the end user since it will depend on the paper formats that the user's printer supports. Therefore, it is best to stick to standard paper types. $worksheet->set_paper(1); # US Letter $worksheet->set_paper(9); # A4 If you do not specify a paper type the worksheet will print using the printer's default paper. =head2 center_horizontally() Center the worksheet data horizontally between the margins on the printed page: $worksheet->center_horizontally(); =head2 center_vertically() Center the worksheet data vertically between the margins on the printed page: $worksheet->center_vertically(); =head2 set_margins($inches) There are several methods available for setting the worksheet margins on the printed page: set_margins() # Set all margins to the same value set_margins_LR() # Set left and right margins to the same value set_margins_TB() # Set top and bottom margins to the same value set_margin_left(); # Set left margin set_margin_right(); # Set right margin set_margin_top(); # Set top margin set_margin_bottom(); # Set bottom margin All of these methods take a distance in inches as a parameter. Note: 1 inch = 25.4mm. ;-) The default left and right margin is 0.75 inch. The default top and bottom margin is 1.00 inch. =head2 set_header($string, $margin) Headers and footers are generated using a C<$string> which is a combination of plain text and control characters. The C<$margin> parameter is optional. The available control character are: Control Category Description ======= ======== =========== &L Justification Left &C Center &R Right &P Information Page number &N Total number of pages &D Date &T Time &F File name &A Worksheet name &Z Workbook path &fontsize Font Font size &"font,style" Font name and style &U Single underline &E Double underline &S Strikethrough &X Superscript &Y Subscript && Miscellaneous Literal ampersand & Text in headers and footers can be justified (aligned) to the left, center and right by prefixing the text with the control characters C<&L>, C<&C> and C<&R>. For example (with ASCII art representation of the results): $worksheet->set_header('&LHello'); --------------------------------------------------------------- | | | Hello | | | $worksheet->set_header('&CHello'); --------------------------------------------------------------- | | | Hello | | | $worksheet->set_header('&RHello'); --------------------------------------------------------------- | | | Hello | | | For simple text, if you do not specify any justification the text will be centred. However, you must prefix the text with C<&C> if you specify a font name or any other formatting: $worksheet->set_header('Hello'); --------------------------------------------------------------- | | | Hello | | | You can have text in each of the justification regions: $worksheet->set_header('&LCiao&CBello&RCielo'); --------------------------------------------------------------- | | | Ciao Bello Cielo | | | The information control characters act as variables that Excel will update as the workbook or worksheet changes. Times and dates are in the users default format: $worksheet->set_header('&CPage &P of &N'); --------------------------------------------------------------- | | | Page 1 of 6 | | | $worksheet->set_header('&CUpdated at &T'); --------------------------------------------------------------- | | | Updated at 12:30 PM | | | You can specify the font size of a section of the text by prefixing it with the control character C<&n> where C is the font size: $worksheet1->set_header('&C&30Hello Big' ); $worksheet2->set_header('&C&10Hello Small'); You can specify the font of a section of the text by prefixing it with the control sequence C<&"font,style"> where C is a font name such as "Courier New" or "Times New Roman" and C Spreadsheet::WriteExcel logo

Spreadsheet::WriteExcel

The Spreadsheet::WriteExcel Perl module can be used to create an Excel binary file on any platform that perl runs on. Multiple worksheets can be added to a workbook and formatting can be applied to cells. Text, numbers, formulas, hyperlinks, images and charts can be added.

The file produced by this module is compatible with Excel 97, 2000, 2003 and 2007.

This module will work on Windows, Linux, UNIX and Mac platforms. Generated files are also compatible with the Linux/UNIX spreadsheet applications Gnumeric and OpenOffice.org.

This module cannot be used to write to an existing Excel file (see the Modifying and Rewriting Excel Files section of the main documentation for more details).

An example

The following example shows some of the basic features of Spreadsheet::WriteExcel.

#!/usr/bin/perl -w

use strict;
use Spreadsheet::WriteExcel;

# Create a new workbook called simple.xls and add a worksheet.
my $workbook  = Spreadsheet::WriteExcel->new('simple.xls');
my $worksheet = $workbook->add_worksheet();

# The general syntax is write($row, $column, $token).
# Note that row and column are zero indexed.

# Write some text.
$worksheet->write(0, 0,  'Hi Excel!');


# Write some numbers.
$worksheet->write(2, 0,  3);          # Writes 3
$worksheet->write(3, 0,  3.00000);    # Writes 3
$worksheet->write(4, 0,  3.00001);    # Writes 3.00001
$worksheet->write(5, 0,  3.14159);    # An approximation


# Write some formulas.
$worksheet->write(7, 0,  '=A3 + A6');
$worksheet->write(8, 0,  '=IF(A5>3,"Yes", "No")');


# Write a hyperlink.
$worksheet->write(10, 0, '"http://www.perl.com/"

__END__

This will generate an output file that looks like this:

Output from example

Documentation

There is a lot of documentation for Spreadsheet::WriteExcel, both on-line:

And locally:

Other Resources

Donations

If you found Spreadsheet::WriteExcel useful and would like to donate to the project, you can do so via PayPal.

__END__

Spreadsheet-WriteExcel-2.40/docs/number_formats1.html000644 000765 000024 00000222121 11304413713 022737 0ustar00Johnstaff000000 000000
Sample Custom Number Formats
Format Code Description Data with General Format Data with Custom Number Format
       
00000 Always displays 5 digits. Pads with leading 983 00983
  zeros if the number contains fewer than 5 digits. 23589 23589
  This custom format is very useful when you 9856 09856
  work with zip codes. 85632 85632
       
0;-0;;@ Suppresses zeros in cells. 98 98
  This format displays positive values (0) and 0  
  negative values (-0), hides zero values, and -9 -9
  displays text (@). 0  
    hello hello
       
;;;@ Suppresses numbers in cells. 98  
  This format hides positive, negative, and zero 0  
  values, and displays only text (@). -9  
    0  
    hello hello
       
[Black]General Suppresses errors in cells. 0.333333333 0.333333333
  This format hides or displays positive, #DIV/0! #DIV/0!
(the cells are also formatted negative, zero, and text values as black #N/A #N/A
with a blue font) characters in the General format. 1.666666667 1.666666667
  The text is formatted with a blue font 0.5 0.5
  so that errors do not appear. The other    
  values appear because the number format    
  color overrides the font color for the cell.    
       
#.??? Lines numbers up with the decimal. 3.256 3.256
  The ? code leaves a space for 5.2 5.2
  insignificant zeros but does not 9.652 9.652
  display them. 98.2568 98.257
       
#, Displays numbers in thousands. 4058.34 4
  The comma is the thousands 52865 53
  placeholder. If you wish to display 236  
  the numbers in millions, use the 5502235623 5502236
  format #,, instead. 999555 1000
       
#,###,, "M" Displays numbers in millions. 32654236 33 M
  The comma is the thousands 4563258963 4,563 M
  placeholder. The letter "M" is displayed 1.2357E+12 1,235,699 M
  after each number. 22333666 22 M
    12345678 12 M
       
0.00,, Represents numbers in millions. 1000000 1.00
  This number format displays numbers 12000000 12.00
  so that 1 represents one million. 12200000 12.20
    120000 0.12
       
0;[Red]"Error!";0;[Red]"Error!" Displays "Error!" in red for negative numbers 10 10
  and text. hello Error!
  This format may be useful to alert users -10 Error!
  that they entered invalid text in a cell. 0 0
       
0.0 Displays numbers with the degree symbol. 32.63 32.6
  The degree symbol uses the character 63.258 63.3
  map code ALT+0176. 96.75 96.8
    -5.36 -5.4
       
0.00 Displays numbers with the British pounds 100 100.00
  symbol. 67.63 67.63
  The pound symbol uses the character 0 0.00
  map code ALT+0163. 9.63 9.63
       
0.0_-;0.0- Displays the negative sign on the right side -5 5.0-
  of the number. -40.3 40.3-
  This format also pads space at the right 50 50.0
  of a postive number so that the decimals line up. -10.99 11.0-
       
@ Displays 5 spaces and then the text to this this
  give the appearance of a tab (or indent). is is
(5 spaces, then @)   a a
    test test
       
@*- Shows text leaders. Apples Apples ---------------------------
  In a number format, the asterisk (*) Oranges Oranges -------------------------
  causes Microsoft Excel to repeat the next Bananas Bananas ------------------------
  character until the width of the column Pears Pears -----------------------------
  is filled. Text leaders are commonly used in Peaches Peaches ------------------------
  tables of contents. Plums Plums ----------------------------
    Grapes Grapes ---------------------------
       
0 "dollars and" .00 "cents" Displays a currency value with words. 20.36 20 dollars and .36 cents
  This format displays the whole number 2.55 2 dollars and .55 cents
  portion of the number followed by the 45.36 45 dollars and .36 cents
  words "dollars and," followed by the fractional 69 69 dollars and .00 cents
  portion of the number and the word "cents." 36.25 36 dollars and .25 cents
    1003.33 1003 dollars and .33 cents
       
0"."00 Displays a value in hundreds. 2300 23.00
  The comma is used only for scaling 23 0.23
  numbers in multiples of one thousand. Use 400 4.00
  a number format with a decimal character 5000 50.00
  between the placeholders to display a -90 -0.90
  number as a multiple of 100 or 10 (0"."0). 0 0.00

Spreadsheet-WriteExcel-2.40/docs/number_formats2.html000644 000765 000024 00000071217 11304413723 022751 0ustar00Johnstaff000000 000000
Sample Custom Number Formats
Format Code Description Data with General Format Data with Custom Number Format
       
[>9999999](000)000-0000;000-0000 Displays a telephone number with or without an 7045556325 (704)555-6325
  area code. 9106325689 (910)632-5689
  If the number is greater than 9,999,999, this code 8896523 889-6523
  displays the number with an area code 5362563 536-2563
  ((000)000-0000); otherwise the number appears 2065896325 (206)589-6325
  without the area code (000-0000). 3369856 336-9856
       
[<1].00;$0.00_ Shows currency values in dollars or cents. 1.25 $1.25
  This code displays values less than 1 in 3 $3.00
  cents notation (.00), and display values 0.35 .35
  greater than or equal to 1 in dollars, and 0.95 .95
  leaves a space on the right so that the 22.36 $22.36
  decimals line up ($0.00_). 0.75 .75
       
[<=2]"Low"* 0;[>=4]"High"* 0;"Average"* 0 Using "If, ElseIf, Else" in a number format: 1 Low 1
  If the value is <=2, display the word "low" 2 Low 2
  with the value, 3 Average 3
  Else If the value is >=4, display the word 4 High 4
  "high" with the value, 5 High 5
  Else display the word "Average" with    
  value.    
       
[Red][<=2]0;[Green][>=4]0;[Black]0 Using "If, ElseIf, Else" in a number format: 1 1
  If the value is <=2, display the value 2 2
  with red text, 3 3
  Else If the value is >=4, display the value 4 4
  with green text, 5 5
  Else display the value with black text.    

Spreadsheet-WriteExcel-2.40/docs/palette.html000644 000765 000024 00000120546 11304413104 021273 0ustar00Johnstaff000000 000000 Spreadsheet::WriteExcel Documentation

The Spreadsheet::WriteExcel Colour Palette

Index Index Color Name
8 0x08   black
9 0x09   white
10 0x0A   red
11 0x0B   lime
12 0x0C   blue
13 0x0D   yellow
14 0x0E   magenta
15 0x0F   cyan
16 0x10   brown
17 0x11   green
18 0x12   navy
19 0x13    
20 0x14   purple
21 0x15    
22 0x16   silver
23 0x17   gray
24 0x18    
25 0x19    
26 0x1A    
27 0x1B    
28 0x1C    
29 0x1D    
30 0x1E    
31 0x1F    
32 0x20    
33 0x21    
34 0x22    
35 0x23    
36 0x24    
37 0x25    
38 0x26    
39 0x27    
40 0x28    
41 0x29    
42 0x2A    
43 0x2B    
44 0x2C    
45 0x2D    
46 0x2E    
47 0x2F    
48 0x30    
49 0x31    
50 0x32    
51 0x33    
52 0x34    
53 0x35   orange
54 0x36    
55 0x37    
56 0x38    
57 0x39    
58 0x3A    
59 0x3B    
60 0x3C    
61 0x3D    
62 0x3E    
63 0x3F    
Spreadsheet-WriteExcel-2.40/docs/Perl_Journal_Article.html000644 000765 000024 00000070464 11304413755 023713 0ustar00Johnstaff000000 000000 Spreadsheet::WriteExcel - The Perl Journal, Fall 2000
The following article appeared in The Perl Journal, Fall 2000. It is reprinted here by kind permission of Jon Orwant and The Perl Journal.
Copyright (c) 2000, The Perl Journal.

Spreadsheet::WriteExcel

John McNamara

Resources:
Spreadsheet::WriteExcel ................................................................... CPAN
OLE::Storage ................................................................................... CPAN
Win32::OLE ..................................................................................... CPAN
XML spces for Excel ................. http://msdn.microsoft.com/library/officedev/
Gnumeric ............................................................... http://www.gnumeric.org
HTML::TableExtract ........................................................................ CPAN
Excel SDK newsgroup .............................. news://microsoft.public.excel.sdk
OLE Compound File ..... http://user.cs.tu-berlin.de/~schwartz/pmh/guide.html
Herbert ........................................ http://user.cs.tu-berlin.de/~schwartz/pmh/
Filters .................................................. http://arturo.directmail.org/filtersweb/
xlHtml ....................................................................... http://www.xlhtml.org/

One of Perl's great strengths is the ability to filter data from one format into another. Data goes in one end of a Perl program and miraculously comes out the other end as something more useful. Your Sybase file goes into Perl counselling and after a few short sessions comes out feeling like a brand new Oracle file.

However, not all file formats are readily accessible. Certain proprietary file formats, and in particular binary files, can be difficult to handle. One such format is the Microsoft Excel spreadsheet file.

Excel is the spreadsheet application at the heart of the Microsoft Office suite. It is a popular tool for data analysis and reporting, and even though it is only available on Windows and Macintosh platforms there is often a requirement to produce Excel compatible files on Unix platforms. (Several rumors and some evidence of a Linux port of Microsoft Office have recently come to light on Slashdot.)

This article describes Spreadsheet::WriteExcel, a cross-platform Perl module designed to write data in the Microsoft Excel binary format. It highlights the fact that although Perl is most often associated with text files, it can readily handle binary files as well. This article also looks at alternative methods for producing Excel files and suggests some methods for reading them.

Using Spreadsheet::WriteExcel

A single Excel file is generally referred to as a workbook. A workbook is composed of one or more worksheets, which are pages of data in rows and columns. Each row and column position within a workbook is referred to as a cell.

Spreadsheet::WriteExcel creates a new workbook to which you can add new worksheets. You can then write text and numbers to the cells of these worksheets. The following Perl program is a simple example:

    #!/usr/bin/perl -w

    use strict;
    use Spreadsheet::WriteExcel;

    # Create a new Excel workbook called perl.xls

    my $workbook = Spreadsheet::WriteExcel->new("perl.xls");

    my $worksheet = $workbook->addworksheet();

    # Write some text and some numbers
    # Row and column are zero indexed
    $worksheet->write(0, 0, "The Perl Journal");
    $worksheet->write(1, 0, "One"            );
    $worksheet->write(2, 0, "Two"            );
    $worksheet->write(3, 0,  3               );
    $worksheet->write(4, 0,  4.0000001        );

Figure 1: Example file Written with Spreadsheet::WriteExcel

What is happening here is that we are using the Spreadsheet::WriteExcel module to create a variable that acts like an Excel workbook. We add a single worksheet to this workbook and then write some text and numbers. Figure 1 shows how the resulting file looks when opened in Excel.

The Spreadsheet::WriteExcel module provides an object-oriented interface to a new Excel workbook. This workbook is an object (a variable) that acts as a container for worksheet objects (more variables), which themselves provide methods (functions) for writing to their cells.

The primary method of the module is the new() constructor, which takes a filename as its argument and creates a new Excel workbook:

    $workbook = Spreadsheet::WriteExcel->new($filename);

The workbook is then used to create new worksheets using the addworksheet() method:

    $worksheet = $workbook->addworksheet($sheetname);

If no $sheetname is specified, the general Excel convention for worksheet naming will be followed: Sheet1, Sheet2, and so on. The worksheets are stored in an array called @worksheets which can be accessed through the workbook object.

In a multi-sheet workbook you can select which worksheet is initially visible with the activate() method.

The worksheet objects provide the following methods for writing to cells:

    write($row, $column, $token)
    write_number($row, $column, $number)
    write_string($row, $column, $string)

The write() method is an alias for one of the other two write methods. It calls write_number() if $token looks like a number according to the following regex:

    $token =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/

Otherwise it calls write_string(). If you know in advance what type of data needs to be written, you can call the specific method, and otherwise you can just use write().

Here is another example that demonstrates some of these features:

    #!/usr/bin/perl -w

    use strict;
    use Spreadsheet::WriteExcel;

    # Create a new Excel workbook
    my $workbook = Spreadsheet::WriteExcel->new("regions.xls");

    # Add some worksheets
    my $north = $workbook->addworksheet("North");
    my $south = $workbook->addworksheet("South");
    my $east  = $workbook->addworksheet("East");
    my $west  = $workbook->addworksheet("West");

    # Add a caption to each worksheet
    foreach my $worksheet (@{$workbook->{worksheets}}) {
       $worksheet->write(0, 0, "Sales");
    }

    # Write some data
    $north->write(0, 1, 200000);
    $south->write(0, 1, 100000);
    $east->write (0, 1, 150000);
    $west->write (0, 1, 100000);

    # Set the active worksheet
    $south->activate();

The output from this program is shown in Figure 2.

Figure 2: A multiple worksheet example

You can also create a new Excel file using the special Perl filehandle -, which redirects the output to STDOUT. This is useful for CGI programs generating data with a content-type of application/vnd.ms-excel.

    #!/usr/bin/perl -w

    use strict;
    use Spreadsheet::WriteExcel;

    # Send the content type
    print "Content-type: application/vnd.ms-excel\n\n";

    # Redirect the output to STDOUT
    my $workbook  = Spreadsheet::WriteExcel->new("-");

    my $worksheet = $workbook->addworksheet();
    $worksheet->write(0, 0, "The Perl Journal");

The Spreadsheet::WriteExcel module also provides a close() method which can be used to close the Excel file explicitly. As usual, the file will be closed automatically when the object reference goes out of scope or when the program ends.

Finally, the following is a slightly more useful example - a Perl program that converts a tab-delimited file into an Excel file:

    #!/usr/bin/perl -w

    use strict;
    use Spreadsheet::WriteExcel;

    # Check for valid number of arguments
    if (($#ARGV < 1) || ($#ARGV > 2)) {
       die("Usage: tab2xls tabfile.txt newfile.xls\n");
    };

    # Open the tab-delimited file
    open (TABFILE, $ARGV[0]) or die "$ARGV[0]: $!";

    # Create a new Excel workbook
    my $workbook  = Spreadsheet::WriteExcel->new($ARGV[1]);
    my $worksheet = $workbook->addworksheet();
    # Row and column are zero indexed
    my $row = 0;

    while (<TABFILE>) {
       chomp;
       # Split on single tab
       my @Fld = split('\t', $_);

       my $col = 0;
       foreach my $token (@Fld) {
           $worksheet->write($row, $col, $token);
           $col++;
       }
       $row++;
    }

How the Spreadsheet::WriteExcel Module Works

We'll now turn to the structure of the module, discussing the Excel format, a history of the module's development, and the glory of Perl's pack builtin.

The Excel Binary Interchange File Format

Excel data is stored in the Binary Interchange File Format, also known as BIFF. Details of this format are given in the Excel SDK, the "Excel Developer's Kit" from Microsoft Press. It is also included in the MSDN CD library, but is no longer available on the MSDN web site. Issues relating to the Excel SDK are discussed, occasionally, on the newsgroup microsoft.public.excel.sdk.

The BIFF portion of the Excel file is composed of contiguous binary records that have different functions and hold different types of data. Each BIFF record is composed of the following three parts:

Record name A hexadecimal identifier (2 bytes)
Record length The length of following data (2 bytes)
Record data The data, which can be of variable length

The BIFF data is stored along with other data in an OLE Compound File. This is a structured storage format that acts like a filesystem within a file. A Compound File is composed of storages and streams which, to follow the file system analogy, are like directories and files. This is shown schematically in Figure 3.

Figure 3: The compound File system used to store Excel data.

One effect of the file system structure is that the BIFF data within the Compound Files is often fragmented, and the files occasionally contain lost blocks of data. The location of the data within a Compound File is controlled by a file allocation table (FAT).

The documentation for the OLE::Storage module contains one of the few descriptions of the OLE Compound File in the public domain, at http://user.cs.tu-berlin.de/~schwartz/pmh/guide.html. The source code for the Gnumeric spreadsheet Excel plugin also contains information relevant to the Excel BIFF format and the OLE container at http://www.gnumeric.org/.

A Brief History of Time Wasted

Spreadsheet::WriteExcel started life as a C program written to convert the numerical output of a Fortran Finite Element Analysis program into an Excel spreadsheet. The prototype version produced a tab-delimited file that Excel digested without problem. However, I thought a native binary file would be nicer. Therein lies a true tale of human vanity.

The SDK documentation for Excel 5 lists 127 binary records that can be included in a file, but never says which records are required. By a painful process of trial and error, I removed binary records from a standard Excel file until I reached a minimum configuration that would load without crashing the application. This process has a nice name: reverse engineering. The memory of this drudgery came back to me two years later when I came across the following article in MSDN: "Records needed to make a BIFF5 file Microsoft Excel can use, Q147732", at http://support.microsoft.com/support/kb/articles/Q147/7/32.asp.

Everything went fine until Excel 97.

Excel 4 files are pure BIFF files. Excel 5 files aren't. They're Compound Files with BIFF files embedded inside. However, Excel 5 will also accept a pure BIFF file for backward compatibility. Excel 97 will not. The Fortran to Excel filter that had worked so successfully with Excel 5 caused Excel 97 to choke and die.

The solution was to open a Compound Document stream using a C++ interface and write the BIFF records into it. In C++ on Windows this is relatively easy; for a brief example, see "How to Create a BIFF5 File, Q150447", http://support.microsoft.com/support/kb/articles/Q150/4/47.ASP.

In Perl, or in C for that matter, this approach is more difficult since the OLE interface is closely tied to C++. The only cross-platform resource available for use with Perl is the OLE::Storage module, which is an interface to OLE documents. However, it doesn't provide any facility for writing into a document stream.

The first version of the Spreadsheet::WriteExcel exploited a backward compatibility feature in Excel to avoid using the OLE container. However, this limited the file to a single worksheet, and features like formatting weren't possible.

So it was back to the hex editor, although this time I was also armed with the OLE::Storage documentation and the Gnumeric source code. The current version of Spreadsheet::WriteExcel supports the OLE container, paving the way for the addition of other Excel features. At the moment the main thrust of the work is toward adding formatting for cells, rows, and columns.

The addition of the OLE container meant that the files produced by this module are also compatible with the Linux/Unix spreadsheet applications Star Office, Gnumeric, and XESS.

The pack Programming Language

Perl contains several mini-languages each with its own syntax: format, pod, regexen, sprintf, and pack. The pack function is described in perlfunc as follows: "pack(template, list) takes an array or list of values and packs it into a binary structure, returning the string containing the structure." This function is ideal for writing the BIFF records contained in an Excel file. For example, consider how pack is used to write the BOF binary record in the following subroutine from Spreadsheet::WriteExcel:

    sub _store_bof {

        my $self    = shift;
        my $name    = 0x0809;        # Record identifier
        my $length  = 0x0008;        # Number of bytes to follow

        my $version = $BIFF_version; # 0x0500 for Excel 5
        my $type    = $_[0];         # 0x05 = workbook, 0x10 = worksheet

        my $build   = 0x096C;
        my $year    = 0x07C9;

        my $header  = pack("vv",   $name, $length);
        my $data    = pack("vvvv", $version, $type, $build, $year);

        $self->_prepend($header, $data);
    }

The string written to the Excel file looks like this in hexadecimal:

    09 08 08 00 00 00 10 00 00 00 00 00

The v template produces a two-byte integer in little-endian order regardless of the native byte order of the underlying hardware. Since the majority of the BIFF and OLE data in an Excel file is composed of little-endian integers, it's possible to write a cross-platform binary file with very little effort. The complementary function for reading fixed format structures is unpack. Perl is most often associated with text processing, but has features that handle binary data in a relatively straightforward manner.

One problem I encountered was with the binary representation of a floating-point number, since Excel requires a 64-bit IEEE float. pack provides the d template for a double precision float, but its format depends on the native hardware. If Spreadsheet::WriteExcel cannot generate the required number format, it will croak() with an error message. During installation, make test will also catch this. Nobody has reported a problem yet, probably because the owners of PDPs or Crays are involved in real computing and aren't interested in such fripperies as Microsoft Excel.

There is one feature of writing binary files that traps everyone at least once. Consider the following example, which writes the Excel end-of-file record identifier, 0x000A. What file size is printed out?

    #!/usr/bin/perl -w

    use strict;

    open (TMP, "+> testfile.tmp") or die "testfile.tmp: $!";
    print TMP pack("v", 0x000A);
    seek (TMP,0,1);
    my $filesize = -s TMP;

    print  "Filesize is $filesize bytes.\n";

The answer depends on your operating system. On Unix the answer is 2, and on Windows the answer is 3. This is because 0x0A is the newline character, \n, which your Windows's I/O libraries will translate to 0x0D 0x0A or \r\n. This is a "feature" of Windows, not Perl. To write a binary file with exactly the data you want and nothing else, you need to use the binmode() function on the filehandle.

The Structure of the Module

Spreadsheet::WriteExcel was designed with a object-oriented interface so that it most closely represents Excel's own interface. The fact that Excel relies heavily on an object-oriented model can be seen from the Excel object hierarchy, and from its interaction with Visual Basic for Applications (VBA). The Excel object hierarchy is shown in the help file that comes with Excel VBA under the entry "Microsoft Excel Objects". The main strand of the hierarchy of interest is:

    Application->Workbook->Worksheet

For us, "Application" means Excel. In other contexts it might mean Word or PowerPoint.

Spreadsheet::WriteExcel mimics this hierarchy with five classes, each split into its own packages. For ease of development, each package is contained in its own module.

    WriteExcel  - The main module
    Workbook    - A container for worksheets
    Worksheet   - Provides the write methods
    BIFFwriter  - Writes data in BIFF format
    OLEwriter   - Write data into an OLE storage

From the user's point of view, these are seen as follows:

    WriteExcel->Workbook->Worksheet

The interaction of these packages is shown as low-tech UML in Figure 4. Only the documented public methods are included.

Figure 4: The structure of the Spreadsheet::WriteExcel module.

The relationships can be described as follows: WriteExcel is a Workbook. Workbook is a container for Worksheets, and it uses the OLEwriter class. Workbook and Worksheet are both derived from the abstract base class BIFFwriter.

Alternative Ways of Writing to Excel

Depending on your requirements, background, and general sensibilities, you may prefer one of the following methods for storing data in Excel.

• CSV (comma separated variables) or text. If the file extension is csv, Excel will open and convert this format automatically.
• HTML tables. This is an easy way to add formatting.
• DBI or ODBC. Connect to an Excel file as a database.
• Win32::OLE module and office automation. This is discussed in more detail in a later section.
• XML and HTML. There are XML and HTML specifications available for Excel Workbooks. The HTML specification goes beyond single tables and allows you access to all of Excel's features. However, there are no modules currently available to write Excel files in these formats. Interested parties should look at http://msdn.microsoft.com/library/officedev/ofxml2k/ofxml2k.htm.

Other sources of information: the Gnumeric source code (http://www.gnumeric.org/) and the soon to be GPL'ed Star Office.

Reading from Excel

Despite the title of the Spreadsheet::WriteExcel module, the most commonly asked questions that I receive are about reading Excel files. The following are some suggestions:

• HTML tables. If the files are saved from Excel as a HTML table the data can be accessed using HTML::TableExtract, http://search.cpan.org/search?dist=HTML-TableExtract.
• DBI or ODBC.
• OLE::Storage, formerly known as LAOLA. This is a Perl interface to OLE file formats. In particular, the distribution contains an Excel to HTML converter called Herbert, http://user.cs.tu-berlin.de/~schwartz/pmh/. There is also an open source C/C++ project based on the LAOLA work. Try the Filters Project at http://arturo.directmail.org/filtersweb/ and the xlHtml Project at http://www.xlhtml.org/. The xlHtml filter is more complete than Herbert.
• Win32::OLE module and office automation.

Win32::OLE

As is often said, only perl can parse Perl. Similarly, only Excel can grok and spew Excel. Tackling the binary file head on is fine up to a certain point. After that it's best to leave the dirty work to Excel.

By far the most powerful method of accessing an Excel file for either reading or writing is through OLE and OLE Automation. Automation is the process by which OLE objects, such as Excel, act as servers and allow other applications to control their functionality. When applied to the Microsoft Office suite of applications, this process is known as Office Automation.

The following is a textual description of how you might use Automation with Excel:

• Request Excel to start
• Request Excel to write some cells
• Request Excel to save the file
• Request Excel to close

To do this in Perl requires a Windows platform, the Win32::OLE module, and an installed copy of Excel. Here is an example:

    #!/usr/bin/perl -w

    use strict;
    use Cwd;
    use Win32::OLE;

    my $application = Win32::OLE->new("Excel.Application");
    my $workbook    = $application->Workbooks->Add;
    my $worksheet   = $workbook->Worksheets(1);

    $worksheet->Cells(1,1)->{Value} = "The Perl Journal";
    $worksheet->Cells(2,1)->{Value} = "One";
    $worksheet->Cells(3,1)->{Value} = "Two";
    $worksheet->Cells(4,1)->{Value} =  3;
    $worksheet->Cells(5,1)->{Value} =  4.0000001;

    # Add some formatting
    $worksheet->Cells(1,1)->Font->{Bold}       = "True";
    $worksheet->Cells(1,1)->Font->{Size}       = 16;
    $worksheet->Cells(1,1)->Font->{ColorIndex} = 3;
    $worksheet->Columns("A:A")->{ColumnWidth}  = 25;

    # Get current directory using Cwd.pm
    my $dir = cwd();

    $workbook->SaveAs($dir . '/perl_ole.xls');
    $workbook->Close;

Figure 5: An example file written with Win32::OLE and Excel.

The result is shown in Figure 5. Without the formatting code, this program produces an Excel file which is almost identical to the one shown in Figure 1.

There are some issues that we've skirted here, particularly in relation to starting and stopping an OLE server. A more detailed introduction to the Win32::OLE module is given by Jan Dubois in TPJ #10 at http://www.itknowledge.com/tpj/issues/vol3_2/tpj0302-0008.html.

For additional examples see http://www.activestate.com/Products/ActivePerl/docs/faq/Windows/ActivePerl-Winfaq12.html and http://www.activestate.com/Products/ActivePerl/docs/site/lib/Win32/OLE.html.

As a brief diversion, the following program uses Win32::OLE to expose the flight simulator Easter Egg in Excel 97 SR2.

    #!/usr/bin/perl -w

    use strict;
    use Win32::OLE;

    my $application = Win32::OLE->new("Excel.Application");
    my $workbook    = $application->Workbooks->Add;
    my $worksheet   = $workbook->Worksheets(1);

    $application->{Visible} = 1;

    $worksheet->Range("L97:X97")->Select;
    $worksheet->Range("M97")->Activate;

    my $message =  "Hold down Shift and Ctrl and click the ".
                   "Chart Wizard icon on the toolbar.\n\n".
                   "Use the mouse motion and buttons to control ".
                   "movement. Try to find the monolith. ".
                   "Close this dialog first.";

    $application->InputBox($message);

Obtaining Spreadsheet::WriteExcel

The latest version of the module will always be available at CPAN, at http://search.cpan.org/search?dist=Spreadsheet-WriteExcel.

ActivePerl users can download and install the module using PPM as follows:

    C:\> ppm
    PPM> set repository tmp http://homepage.eircom.net/~jmcnamara/perl
    PPM> install Spreadsheet-WriteExcel
    PPM> quit
    C:\>
_ _END_ _


John McNamara (jmcnamara@cpan.org) works as a software developer for Tecnomen Ltd. Apart from the usual things that engage us all, his main interest in life is the Saab 900 series. He lives in Limerick, Ireland.
Spreadsheet-WriteExcel-2.40/docs/tpj_images/000755 000765 000024 00000000000 12236561260 021075 5ustar00Johnstaff000000 000000 Spreadsheet-WriteExcel-2.40/docs/tpj_images/tpj0503-0004-01.gif000644 000765 000024 00000012740 11304413104 023460 0ustar00Johnstaff000000 000000 GIF87at999,tH*\ȰÇ#JHŋ3jXǏ CIɓ(S\ɲ˗0cʜIKrɳϟ@ JhH*] PJJիW ʵׯ`Êsٳhw:ZSpʭ5ݻxYv߿N e|k}W^2Gnw ghJ^HCHl[ elGA غ p""XFEnTQ,2VF!h-89Hd>|IBL" ̊$'IJZ!#'X3ՓBQ@AQ2SMkNG76ߓ5l%(͹t&θ |'f@(%}&U)y&_YO2y,UOvTU Jχ!}DQ!MtEiQM0#FW,$hG\ShIuRjΓ"{"t2ia=?UNTwFUAm93GpiR:bU&jզӬjU *Y/SUp4s}UVVhk+ZWgg&CJ׼s6UZT֯ k 85M-NߙXŚvlhg7ֺ򱫱m-,C:kR5nrZL()H.6G[-3 &%R- gjOΊ1tS|4.R{S$I}JYS9~Q/` سO4O$NvCAxDtoQ2lt8N"OQFxCq0y1a'T#9OiQjt5D xdQ^ʲwLTe8nLg|y5wVM#MJ)f>8ʅNmg"˷[Ers oË. \N.ˆgȔ1fLќ,ԉ@mDAqYR^+׀4[z?k}l['ӻv4}!-Ձ/kr$9e-**r/wY=ѵyKnjKڪFxhwN'o.+Fyea۳~vw}3o7:_/1j6;vFm'3Qug\kNI3۞v/yG9^kKI۽f<5?+}Oz$7Kڽ>{~gw{?Guoþҩgꓟg|>Fo)OI _{KEGKy_秺~sʏ?MnJִow4P}fhgv}v(vcWyobeu o vchA}hV`Vrb$y-y vYd$kEwC7|7ef'ȃვgEzgu!'GqYrńDžW|1X/{dXfY|^H|Vxt`zU8W=h[C~!"h8(J8yGcW熅)h>q`‰|x'pq8v/b'hXHxY$C!BATdt􋇢vxmx!XN5x!,؊{~HwSAᘃh!5&y؎'hs%^5ȏqX0j{H;HdyB!i5A9Yygؑy&ِ^Xq "U%xx->R*4 IWI`Dyr?)CF#'=iєsuh!;:֘WiP?dfX6In)ZXqIp]hIpRg9#}n(6ɐlFiO|v)p^)ObLjT {87IzS9) L)MH츖-]WT3ٛ阚9mi3a Aa'r깞Y>$)ĝ6 99)]"ɜ_hq|ZYĉ@#iʆڐO Iiu"ʟ\٘j`)1ڏ$*&j}8,YzO֠I% QEY:>+q ,Eǣ*;ڢy4ȤcejY hy"90#j*JqSN t!h#(. 4zju2ZJj 6K㉩4Jj= !ʥ!I ~:ZJzW ºv؍ʢ::zʙt:j劈k:wꮧ٫ qZb* {Mʫs*}Z sJ YĪʩtaqۖ0;:{yw+ z:zcH{į/ +[ ~Fۮ|۸K* Ș#wWڻ\雪Ǎ6';~Ӛ+uWc&y?HiKۿ{KZMyܳ bޛlK XBJܴ VV&^nB>:HD~H^4LNP9T^V~XZ\^N;Spreadsheet-WriteExcel-2.40/docs/tpj_images/tpj0503-0004-02.gif000644 000765 000024 00000022200 11304413104 023451 0ustar00Johnstaff000000 000000 GIF87a{{!!))119{{{!))1199BBJJRRZZZcckksss{{BBBZZZsss{{{{{{{,H*\ȰÇ#JHŋ3jȱ# CIɓ(S\2$0cߎ8sɳO9Z JѣH*]P*2M?j3ԯ`ÊK6Ӳhju-׷p{zMKݻxE˷۟mwȂ(]̸qٽ# *o TשϠC4gƔZp aMhx! /ͼ9t9Öo~SfӛcܹK޹1:kaE~&M$}[qWhߝ%G Y8Aj 6hvŵW^uj(t*ZW Ț}'a͹ 9֌ hew(F@VIVujH. WY)fbT9T)eaf&IXI9iwffrif~V{ff'6裐F*餔Vj饘f馜v駠*ꨑzdꩨꪬ>謴U뮼+쮸>5&6+l$@VKN[@vK@߆ .[.`K@`K`o,lp+` '@L@ww̱#@#?,2 A |@!9>%@B$4#MBL?t T@XgM % ^w d]6*'ж nB *=wx , +x.` /B WN .\ 1`.C 0 ~z꬯갷벛Gb iޭ+Ï+n못,G_ Po} \`=c# $ ?loAs\: @H@ ?0PipZI4z 4EHBp̈́[;A³Pl[ FöͰmu[At+a ' *Nq-pAD51HExnt鶨:.z`H-.W;VdkxpWŮѱ\E|},0P}1_´罃5a À0>Aa6p '7 O~R)7`TЀdixf6p AMg$ 2HLaPjQ 4:|֨¯ -4ަ͆ ZC|!8 ǵ@r+wẺX ZR&;v0'= $A0QQt[]ǭQ^ i  z^0]ޫ0LR} %'Iϧ>(w2R8?/<ZKZU /+HuuK@UT+kZOXk/4\o(Mn!ߌHD$ߔh8{ְԧb9ttd_bLVVecY(4Zi&0v:F89~ $G4HY/y/pIH;i01ic.'76R:D%)=ز߭w@2@/q94ӂRf0)5SjYߴj*ֵp7L ,z` ql)us ]F:YC'N"w @F[Z&Q[R>O^ @BRZORya4)N0^b4Y12Rf5o Urh>)8N[&Vo`;mbہJWn:[ &,NO{3 9I.s˧Q}3`<Ã,up"{hiK ě ֎ͣp;/>Kحz i;eJ: 6vҔEQ{O~|%@^wus1gFMЂnZ3hWk>%Ͷo08mm}4haRZ/~>Vt ΢Ck[dםyuZXi$@c='8VB2ׂ:s={ht-:t[''u|YG}X7RS|@Rؗ]RPRHSF>kekW~eeTb6]JJfjTK?Wxx g|ghyǀjpwyx`vCteCyzi5z"WzuraOzjWD:45x{k6kt7q-֊"ak&uKg||Ϸ}7|G!G aHL6H&\T&05ne$aJ~%o2Tpo{]4@q^@7_DpyXp5y 8`&q6Md7x7"X9,9*r279!sits !ъ9&cG'("I}ʷLR\u q]ue>Wx4esnZwuGf@5fv7fUo{wMfXKr dxdAthxgxVy_`iy3tW6z5Gzyzajrz,ljfb.8"='&kkŠ6|9|7ugB('}}G/7uO[…H7\Ʌnp~ s1ϕ~JuJٕfoG@dKh3W3H_Ȏ_ h 8aMqɁ# 薔O'9.sE4H𱗵vk94 tȑ8B=Cl<#E[GFRSTH3i)ui= 0IYvWaf_FIgHJiTl?MU@9N@Pyggg븇Zه 5`hbidyWe'z !ؖr*Oz E$Y Zjt(tR<Ř%["[ҖGG4H/Ɍ'< T11fJw#Sn674ip w_ 1ڀ'8hqՉW9ٝXGX OyP2cՒYQQYXZ<< URsR$=.S&.S99S]ȓSq*TwWiUKw~7K%@'SgWAxYYh[)yw7yuCj j8gzv3p*Q(Ђ2Fի[@ƥb[#u[c/e/ujzĦ[FIf1IJl֧{W3ƛ*L*u_ Ȝ5h2Tqد$))g Fs:hEkEk˶i2F;G?c?DdHfsbGiW0U>#I;YI]VbXexwhxfO?~Ӝ| 60@",Gk6}<ұC YA[RO lL,%,K+o+q+s+agjm,uM,k '׺b^.׀a),](ˁ+Xm̠ ؠڢ=ڤ]ڦm'N]Am]-Џ|ϸ۴M- ՏM#|ܠAݟaݒݑh=N<4bҝmu5P9mA'۫ ғ\ʿ1qDq#>ݎ-] >u.D߸=,Ֆ=G p"2%Q"}M),~FR:'4~Q^ >UW~Ynܭ׵߸IN`nSuti!hEl!:f>K P@NAeaƌ`QBi'gi~.刮Ɲi^}N^f邎:|ܢnݦ^O#(B޽~|y촑뽝nN>.ۮ4==Nn뵮)E.YA?_;+ ѻ.s^Ս6vƶ!>"_Ԇ٭QPq,ƭ\ -=@ .M2Ǯa+-oՁA9o !LrXZ.\|K?͎C!$=lq/kO? vos\pdOo_ğOԥ0'*_ofeK!S>O?o͏/?4 ğߏ׿}Y/?_n?Gv $Xp`wB%:h0?5nGw$Ydɑ'U4K]ƤrfM$VsÇHtaN$o&eqiSO":gU4}EW)V/Ѧ6Z-J'C`˳\ptL;yi'El;BUcϊJ4GS.6;s,K=TMs THϛR$TRUVlݍN2<5UXu% G^{+Q]a-YP}TA3v;\i۶Oմ[پ-]ZNr3Uv=4j]? x` 6`]-^ &MbӒNRxV8|S7Pn.d%16t"viu:]f(TvKm^G&7^LfTiGgϭޫqcXCcfiodVOCZ찇[שQ󹻻۬_\~;ݽz#J՞uplrԵ^Awt$o,]sl)OX?tK]r{qW ='fwU|vy裗~z!yߍ~x$/{ >rc(?hӯq_}oMifN[{HFN~AZ3πWd`0ITNX!A"~`Z8Bٛ H?s: f8;5k C/)t )m!PYfpΉ%$gFU1 !h)F,2֢8ʈVC#XCK {.cb~_t-} taZ}3'vzWA]jXٺƮk$$bzѸΠ17ڗesQ=^bۗD&f'7.4V-Y5ùux s;,˸q\nO0wp{7-~#EZt3 F`^'ۗNZÇcot¶)n_kF0Bsȅ]roZd(Q&1Nss.rwTtG}@zխ^YZֽu]c'փ*3c{|wm}E{mJMcaC9u(hAג=ck}?=>5<4#lkKAR:\@؊ӽ=%:d#8+,0\1 CS"$#|@A\BBt{B3)d>SGD`F2;LԿQOtC6?Sl 4CU?!# ً0DZAdB̀B&E 4q FYFWJIlj3|6i jCk-M:.“<@<u$KƋDŦLh<>Jh= D $ǺK+: vČ|CKI 엵kLǜ|L;LLT(̎KM\MlM|M،MٜMڬMۼMMWۗMM N#,Nkišrf]ګ|.jJ,gzj6};#f ն֢ˡ/WLmxNeIIb9H:뤳: ҄x^e.<$鶧1RLFJoG j<&Sg}:ra~T{1~ڊ*sT# 6M#ڧ/Jz)FR]jtNѣ"Vo5`xntЦ^ ^vk|,ǐ ў z p܄~u2k2=}k1%By7l9 NIMr?̻=i~yg|3 ,)q֌3#c'kW}!wکgNhiGnc_>Lң?%rvolۛ':%x§=vs׻s_e>#,SjYÝp^ G@dH,֫0X@Ja ƌվ>pgcD΀܋`)X34]=>-Pɜ&(EzF'ppt۩7@mṇC;̿ $ъ{!ԄhtT3/e| $sJ1%*% xEK!7AiF!+ÂT%>B0jbb~;Sb (ީ@_Oٱj^33s)\)XL5e1,=)mrR\?k'!'8Hv+UQY^Y)MT_%ERRIleJԔ)H/vEqR~ xG>rDB.0ᒐ*Ծt,4D&9ic_'>rӉrJR&TL;kzy UֶHYPE/EMyUͲHgQu\[}Ua3%Yc*Sc%U` KSr2RQ#})<;١ՇAMk_;SVdM[5zYZ2[iSoa@` lF<@UC:7TyG.׺U t+-i/7͋YcguhE_jϮXXBFB 4آ,pEݩ%΋Eqk}a,aR1q` Xuܺ G3qQ_2ԥ y@ꑑLQU>fR`;NI.Yc\Wj0#ͬ6Noի\S9ivkaGVNyeb֯gsB6J 4L3ZІ;}mG4j"4حmdAI\gLbn(KtqI\˖A'a)6.}k&׃̛Rd= 9^/Nܨ jtm2oF}a=`;Cp(mҸ4rwo=Z]W Qg]7m.E9Tr(.Oy̛{G漩w@wH@;Spreadsheet-WriteExcel-2.40/docs/tpj_images/tpj0503-0004-04.gif000644 000765 000024 00000011446 11304413104 023465 0ustar00Johnstaff000000 000000 GIF89a,@ H*\ȰÇ#JHŋ3jȱGBIɓ(S\ɲ˗0cʜI͛8sɳ'M> JѣH*]ʔ$ЦPJJիXbʵׯ`*lfMV,ٶp9wSoC潻/ھEfݸM̘CC|t17̹ϠCMӟ%c^2.U6N*hn[%)IH_3^KZfx'5'^I駜oIN>٦,Zey_BHIfYdyhE g uzhIh hM .jVL䕩(ʘd(Nױ!*cf4&Ej "v}fܴ^n_Yn;K6ho[i)mg^^k0l>̯6$^|ErX#,hb%*}EW27'2rMҼajg-վFrҹ2 NLtSRH_],ZWVu`ud_=vh}vlvputvxS=w|.8D}wk[LwmXo'ie.SQ'~3qQ^L-뙮;:\IϾ۾|#<.n<]sn з+}V}Y$_)kno>̾PySZƖ빧s2 w2" c7* u9r6&jT`⭎x>jFHq W0~. gUƃhAVN3V<b`•QNJ E*[!Upߦ& "OyX{H0YabUE }Vfp m"G2Bo"=Lѓ7J2;9R.D[,WЖ8%"u K^^їvyUf!x QYK`I{\Y5l햋64\{4и$> jZ9ǓΏs'8jBg|'B2 }D#Љ^D3Q/Ï蓿7?үΙy?+j=gkpWxN?)=e kv#obxC1~Wjv3ugLe`GvMvbBjh9ooYTt>umxeG{8L;vzx~sFs=w$Q"0AF]>(׀W\o^(bbWՖX_tacxnhZ\th؅jx`q-lȇ:H_g}{su؆(hHRXiXj9;IyVHKlXu8\Dy{~q}rhr]fE=ʸ\'| Ԉu|ˈ/7{8=ญA~m׎݈\58>ܦ6X](]yd񈏳>븐4 K9hj^Hm((u ؈nq#hkEEOp5kf[UUבnYci1o^Ʉ`T7o㖅+)b&n")%IfD`AaV`Gǂ'[Yɕ4^a,YQy9qv`5ReS\LEg`DcfC`dyg& c4w%{Tk a`!Ocf)MBi7ax.j9Z5}@Uc+9fDPXRx:qNbék9iɉٞ)؛)wiyę@/ȂbF`ӸVSSHhْE0vBNj5GkbB,֡dllvv #>H4jf(riI'g~d{$H""6f:s7)):IiM) kO:}&IZOYZ*+锴h6_vc~43wOnϔsLvJn jE$p>*xcd]zW:UAꗍj)xJJH*ɧ3zWv p*xʩ:zk)E.aUs{J kig F{_)(%L͒x"Z$ʪ* ִٟSʝ ʞUZơpꢡ֯J/zϊ=lmjyKZ:O[;*I:**Y, )٤ZԹٳ)@;2ǗhǴhՌGXKFW+¥[K"evŠg[ teI;2Gqks۶('/Ƿ]uKwky닩'[w곒E ]9gtTTiKcl/鲝*6I+fdY[Kyn{3 y 뭲hvC볥ˊNjy  Ȼ鹞]&NyTTK2 z-Q:/KyD62G {٫⋲Kz# 7H,UӽYj˚㉈ћi1*Oz!z }ܹKz)e+z-˻૽5(1 ,%<'<=A̠3Î;ML; ,ҋ5WyP b/d\"h;jR'up.r<ǧvx|>~Ǭ)UDŽ|!\ȊȌȎ\;Spreadsheet-WriteExcel-2.40/docs/tpj_images/tpj0503-0004-05.gif000644 000765 000024 00000013656 11304413104 023473 0ustar00Johnstaff000000 000000 GIF89a999,H*\ȰÇ#JHŋ3jb CIɓ(S\˗0c8͛8sɳg, JѣH*U*pӧKi,0ϫX ׯ`Ê-tY4RM[\ʝKݲvesշ% @L9/ʘIY5綃E^fpaɧIuiřcˆ:nꂘ7w_W~-jYͼyu.M=[^rho S>~6߂8gfh{_y( tscm6PN6]q~M'(M:5`VAH"4Y}G_vO)ޗc /֨df7yyX"o?X.b䗶MV܁%%TVٕXdp)heiXMީԙf`l)BIhQ|^矂+ )K`饘b(M駎Dr*P*무j뭸뮼+k&6*ԪJfv(Pk覫n+k,l'̯M G,Wlg,1:\U,$lTHb28<@L@DArD1 P $Gb=w`Z`@B~$߆CtH%V$*b*uk&79cM!R$*WVk~능,gIZڲJ.w^ 0ILVY%J2f:lHu@U R}xTK:қmfڸ (rj9Ms*)8N :yY9T<ٛS+;( Ƞy &=&5CQ$'C>Y IO׭<@G&Ty$MhS=mOQyL6~:}*Zzԉ:+/*Uԛ(-h'GzqEUlEPNU7ycE+&UU⵭\EVV56)XT*`ɪX=,KS&lHS&a%fazڳV!AkZZѕho0R`ٌ~[AA%zV7MmCK邴#*c7[jT 躱Ex^$x-ݨ ToxImٗpj\]h:'LaXvI5uEu8@Nχ/fE&Tѳ9#LT97fNgcj1v,"șLKLc! ɘreL+cu<.yWc哂hN4CejNU2ِ]\OeYn3㜀;+eІJK4yz6@YЈfsLk(Ie3 NZ ?$B3Ai:ηwjǭu1Od&1f7z.vMlg6vLnrt_, ՙ>O":#pJڽnb[ hgY>q܏m깇 OzΡï}kd?~{H.`18\^ xԶc~s;†8E+Gʷr Ϲ[׹5.{}66A<_#3 ^zo[{St)wҵ[rЩx{ݳ.wc<% R4'e jjx#ngws|>o}YOp". >t{mo6Η7D=ucoz'p9>퇝ݳşXcGM{[obWiZZec8nXN 8)xh()XH!8xhO c xp\)(#(35%h3,Ճ>bvԔa1a+c-r/ȁE8(7OQw~hM'S[x]k-a{U" Y~kշ*x@lVTpaPAZ 6iCz;+Х^`b:dZfzhjlڦe pYfRzxTz9L9۵&y*{zsZJ(aUz9z\Hi3i!q"s&J'qř5ѨE6ڄ$ZBIr ҙz=Lꪐ9.%"E<ԕ?!,rr$dzˊ6\ҪԊj!ʬܺ%Sj\rtZʬXU㊍֚ Xɪ *{CB躱 ?N2jYڭ)`jJ>#8Z"y-ZE ZH+BAS۟|:K+yxz VK-#;nl۶np۶qgKa`&J ;gJd!WYF;k˕f묔`c''z rc dJ9;A#랬˝K8ZIv*U K:[1z8H\ۼ0ʑ۹k؛R(JGG{⋼kݫTi˴2: ˅﫣;%ƚu Y{\D8^ؿh k" Vj[+I” •h,M[&|[  ++>\ ľþ6Ļڬ&Fً?|ĎۊW Q%L|([\N[:L@ЫG/( F\t;ܣj\lfP>RDzd [-ݰt ~(>x#=">t߳]Y &eᎽ?<"~_?ޔP&~QyotOmVA8[;e8Sͳe榁 NT%D^Y::߉e)d^tN趜SDjޑoqP~]`yY&[^na^jngL*>YQ'SNx~VS*MFXAV-,i"nËITbmj> 0Z~v6[wK ax?got4XP w oOɷZ>-B[mMc[ZB3"d& cf8ͼP/IE51ASW<"5%C+yRgG,p|EXjm;/`wډ jj1ij0nW1Ip qc,<[,rͅ1 {_&M3 0$f`%o\lNB РxlXМGEP&8& \r'YC`D{LbG/,W76hۑG)H24EȻhMi=DzVxaЖ[SXá-ֶPgnBAoF-NGVs3\!UyP'D]4Jf»;#f^m9[޳=i~Mg{ 7VJO4hKOLWyCwG}üͳջ+{|gCum/ԩap_V }@iw )_ (n􏵘wyz}ozόn5.[K*<S.?%3K] /6VᅵY մR=i~X']4JyP'Di|+>aE>3\!T|gCum/ԫKOO4h/w7twG;ȼ=[rg{ 7VJόn`iw )}@HYygwVap_SY մR.?%<S4Pk0"ln.[K*7ў8x5[oUk[z<Zy? '[}3#mݻp[c-k~Mf[7Z^2'eO-3?IgxLwnaZ߫Y ֵW#̿  Lu:?B?6۷62|!ַleCuo/~Ai_34mwЏͷvn͌n5l>k[z<Zy? '[}3#mݻp]AT|?|%w6ɋ6. ێ>7|[l1p-z/H%tp `cH$iinPvpY9f-eC@9r^)omWA# ,pcav6<8]aŶ|8Qe| T7x%x0 2~p]oq0 0j8mLěi)gdr.Bf^\׷3IroKG{m[C)Wȣ'p.}ۇ.W͗a~p <~|df7 d6^x?0mxqpc59sW`X[ eZ}T (Q:0cG:#  jƦNy=r A|$c!LEl _q{KUjӘE棃8|PIPAkilc8tUEFsjCFf{X\شUfkap^EA ǍZOKğX sq}:)Ţ3/mJ}XhVa7]i.}z~*ދ+Y6əV@*- d;mo~E|u6c0xՒjX,,vaf!Jd,lۼ6-]f>ۮ__.X.)8:rR"<47f.3Fױ1+0&bT6) ]M1g,7GUL#MDl9s*d s.̀>f&Q` - ͖[n9B!d-&Гn$\Oqϭ(B)^&#&Z4nYcLc/tJ#lGD o]sp,IHT)LH;5a;m,U?xw7]/ÇasYm+?d-*1n 14Ѿ6+%+)}K"FQYJKc{pf>L F5;X$/uvwJ\\^<3zC:A'Yyr_ײ9xc4RT>ӏ$SJŢVm)u"yK9;lK"RЍ0&&w3ZW2J"oI\C \"hXچ"SQ1E]ij85,N#㑹d(CVfig(6k˛avaăv(<O)'!4 /*4$R˛EYT knu1u<(/6TA6l1-kdĨ`<$$P{<$[%>`tPwDŽXPb-(1i߯"SQ1E]Wٲ1s5}]{iuaSF4<:WFO4g>w`6uI3Niik +Q >e̳`TyuJ#JUӔ^,(V>Fkv sxW|"UfѲ#5ɃbI}\8 6jb>th@r}uA6$$%Qo .bCT]q%LnzCqe%hV'6̣4]T2JBi.'15~jϥ %䳱{F" aYmM[YÅa\:۰KAһN3[cĊMDf`7wʰcTm`& LFf3`dԏ|Q_k$_+Ȟ$A 8;Xn@k\~&r3Ï)[)qůRf;~ÖO 4M6SzYe8rEZq5])hKAZ5)LFkؘEY $=$6 5QΰϩcE %̳o]l}mP*Hp/!%gypMQ9{r'X?}[2ˎ|Rʊb滎me4QKAƱ%@[ dlæ%:f)2ْٮ.uJv%soW.,ɘLYS0AC๦pS.'<'SMb:RFuPoÉx ȃۃJh>EQ"!!&Xt;Es"2~GZA߸ȟyU]cF$vw=s_sg;hqqPe͗ە kcjX=1>q<DT覡C(9'kbcr1$EO  ..W>Ӵjv6v$uE#|Ҹ1ih96$q:n#IO(ɾyY%R`` 10A#59HTFkv sxW|yv%K %WXYc1GV؜=OSs 9M%KB[~ k 9Z\ ,)I!@ҠQ+Vvq)Ŷ&(=myM~1s4뎰R[2J!fzCZǒbۏVf/3|]DY (kekmAmdoqa\m6nZos W LdFZ1RW J1ӸqpJgY¶Df`7wʿё"?wr?8G]/w?1daf yzEO<-̻7m] 6-v& i.<@y A=yJr?90Pm,mZiιTu ڹ!:v.ky2Hm[mp px0 sgx?oR:yx&ptKQO4ɨ#wG3O-CLԷ|[) OܫS|4 ɱp~98.{͋ JоUt>6WFة*咂J*>s,ld$OC(eoTd:\pkAsDNDeD-^TWTHc֟29*Q-eX7_ohK-1/|mq?ajg0ݘg=㏃sO0p AC`^ll1$}2aw"EHW17ڨR+A)CGn.,UYM}Ah^:/ Z뺏mkКY|&)ɐ&6^Zª&x+ѳV/0oՄ}oSfWf[m5Uԑ DOVlW fV-npr2@޲^eR/ ȎȌf% d&dzr3Kz|pk,~[}i뺏mr6$&Ռ^Rɓc6 Ds6)cN"s 6AT>̯K-6- eܔ8Rw_{ϹY<wHߊ|uȳn fa9R# A $I\c\Yr9{E%P/&6JP&NYO|9'Ӷ.'~͇bh&^Y\C4 53:=i88ߏ4uW{K.wlm 4:dɘɀ _s1A<]H?|m/˓ )+]슢! uUo6И,o2wQud6xاo2wQub DOVlS_Pf"}bt^?-Gֹ>q'˩O5K;|gvђ*#9ӛ[q:30 c)$_\ 2uw)-l&F;lQ$?4y]h&ʍR.|@Dc W e]۰f#*UxUK;WQPKn6Aq7dUŵMAx(%41sη) ]Tln9WTau5v#)c|2)]1dBX-/"<#⡳lfH4iom; FI?\zh_j|ľ֯\suTzhi?\#-O:v#VRFhSOX4iF9_j|ľ֗kYv(:s1e%^Z2NIsM3" 9ɬydi7rx?kH[/6n*yÒVj~rJcsօot9\Ύ2^sX3?7vT.|(XXVsrwcCQ(G-X᪒v>*o]XDW/NӠEcZ]{mkϘ*SG'I,I/Afݚ&]MOzM刚ro9٘Aog=L|瑛nMSN9=G e7emҔ'-M&bɖY4mu8 RÓezl䵩B>Qj TP~ Ԯ/8"ztDh K.9[{f9w6wdQdhdG2ܸx#QM񒔁2`ų& L(l&{nt^?-Gֺ&ƫ/ 4HApJq'LuD.}Br,i= (e# >+OMG/\dҝ녀p?.WSP:d!:e~Is`{ygkꋌ xeSPʹDg8,~[}i뺏mk_Pf"}b6mWw=R}}Ahk DOVlS֙]޻:ֱJm'+?)3m>YN:/ ZewzhZ+'xئʹDg8,~[}i뺏mk_Pf"}b6mWw=R}}Ahk DOVlS֙]޻:ֱJm'+?)3m>YN:/ ZewzhZ+'xئʹDg8,~[}i뺏mk_Pf"}b6mWw=R}}Ahk DOVlS֙]޻:ֱJm'+?)3m>YN:/ ZewzhZ+'xئʹDg8,~[}i뺏mk_Pf"}b6mWw=R}}Ahk DOVlS֙]޻:ֱJm'+?)3m>YN:/ ZewzhZ+'xئʹDg8,~[}i뺏mk_Pf"}b6mWw=R}}Ahk DOVlS֙]޻:ֱJm'+?)3m>YN:/ ZewzhZ+'xئʹDg8,~[}i뺏mk_Pf"}b6mWw=R}}Ahk DOVlS֙]޻:ֱJm'+?)3m>YN:/ ZewzhZ+'xئʹDg8,~[}i뺏mk?,f?kW6+s¢=uI@ujO<6dp͆\Q-x`&@0`ᴑuc*5'Z#O2g,-FѴl.;ɺFNp!(;6(bByq<̅wW?dﻕN}lq+[E?T'CJ[ -}ܬw1er&_5lB 욦0)&)e-"xF$h 6XP0c%YmvaG!/vN߸u]RGbqٽOGbqٽZ+w/Jé~̓4,؜GvocV5>6xJ52.qd*YYb8JR7.qX]6fZ[mrq -@Y_k'X$-)I}24}E@. ?Ihd@Lt5ѡ\'ٜNPOQE\0OkcGU5FrG]~&EOS=$s>КzI O b a|N':[RETT UMQ]w 52:Ñ4сV{.8H76SDVJPzX8ENhfE=}{f5b9M䬹)̱-*3g/8I]"vZb9uIvu1A318D!*NXM%VFJ2#79bpUXvA= 4x% |_UuΆg01aI)㑺}{f5b!,ˈq,d J L6rRsxzj4M>D &p,` K۰zsS!?G*`8_ kUv%B5؍akj b4# f=fdzǶkE9k-sHA נY\-cSK+Ųwi_<K az)es@[\c6=9™~@rU,<03ļ6Z͘Q$NW*W1Ȧ 0OMOm* *' Ժ>%(fk+[ H79b#d3AN@ɒpLKT.ՈM!iYp"6&Y9(݋)x#-\0ۻm.4 7n-jnb@SfD~Ycs/ersZ"E\g3_8G-rE1C'`Zy ?E, sw+dDSv>d;^2D (Mgh},M;4R:N&JnuL!E_+ocsps 7Pⷋ`, K+Ųws\"esYF ɪi*ɑC bRٲ*)NJ hFCec0"X%݇oۘO|[y9]wAM0:z)es@[OE, sw+}M9Raz)es@[\9 &/,YQ d2\KbU t!rt?(PjEEfƲKYOIV-B0l9™#E s쵦ZFq6s˃x+<1z͖b XO ;8lĒlpֱe4q'@6./>p<#$S!M3_} ̞"Y$RȔǐQOP&2♵(C襕nv;Rê덲y0W"vb`4j=, ZSx$ ׄ1ngbȥңkXvmp$thhhu 'Y4;6` CRl;re-șQ$,$CjJdP Alq$x2PaB0XA eao,|T<~Iۻë0_襕nv;=[ -}ܭ7<Jl9o:襕nv;q`ڌ4 e/T"|Jjs\k6`G\GH:nc\U\"*T+i=5=( (RR|Ƀ31lyj3 g82ueվk9 C#V!ij;JHJIP<\ѦjT%YLv܎ghs[wfmwBR@BمXݢۗցc̮LItܣLIb}Α1vHeI2Y2HdGtq$Dz\~6|q\W?dﻕx3rCDЗVkgbшe$D.fC>q5`$Ẃk4%y̷dLXT>f7Z\nn7:́h;6E襕nv;c.[29eHXHd4dȡAI1H)l4H#Ad„`,n÷m'>X ~~Iۻë0_襕nv;=[ -}ܭ7<Jl9o:襕nv;q`ڌ4 e/T"|Jjs\k6`G\GH:nc\U\"*T+i=5=( (RRIó pw(e9&KE3y/DMQV"4}edd_v,Yd5mI.!d"]aT;ClѳUaR4h|_`襕nv;c.[29eHXHd4dȡAI1H)l4H#Ad„`,n÷m'>X ~y9]wAM0:z)es@[OE, sw+}M9Raz)es@[\dQ ˳RQ1Q%ۄ(͗0U[.M T@MF#UL&Lz[\)l$fӒVbMw.nfhLk$Hy/x[[e2D $.++ 鰁dMqH'IF4 X6R@v٘_aٶ-}CjgOIU<]4]1.tll؞!%s׮X쪜aTUFJ%:e\J&襕nv;hQ[BX4p-F}@sk'!%sꪢڗϕԏK& #FejM)@w0`4, K+Ųws\"esYF ɪi*ɑC bRٲ*)NJ hFCec0"X%݇oۘO|RrI'n:Xsé~W?dﻔRl;rTu)XW?dﻕ͓aP?ʙFC!eı%P`Jr)Cu )FTVlk$\%,>TbD+sˑs)<] Q>``<Za5gg1/ <~NjɃ\mm&-0/=ey=PüI/mkvPHi l A71Rt hٲC n⇆&Qc%Zlܩ :ugXZ+O!ʨs?$ڌde(ceK+ŲwLA1I'd(ngW#fF# LW^z;3X! eVF5?N,=!t_+IvA"BO-l/E, sw+FYr܉e/BD;&&E 2 IDKfȨ(G#% #`[vX~f{na?KmvN߸u6ë0_襕nv;=[ -}ܭ7<Jl9o:襕nv;aN+Y֤LumZ2ѭqK=%9Ӟy;/ygH |兙e9e'h-n,q̐l˲\8S첹UWJ2 Ȫ$J$[2Y~Kl360m͂ٳ0ón_byR@Bk:j;TN}.]sc9f/zCΠ1nY\-cV#$emӝbBP܉ .fƈL$fH/x[Eep,;˩&!>鼁 jҷu ەX69qsb-H4}?~X`XW?dﻕ#,nD沍A!a"TU"$" dTS@#č  `DK-߬?30c%rI'n:Xsé~W?dﻔRl;rTu)XW?dﻕf!cwr~}cGӑñ&SGrHF x}) +WUH%DԂ>_Δ&;8%q{ wh6mGW:Rl;rY\-cVJM0R[ -}ܬw1er&_5lB 욦0)&)e-"xF$h 6XP0c%YmvaG!/<~IۻØuvK[ -}ܮ9Ƶe`8}45LT"F>%"+[u ~$,:V|C K-0\PédމgΟ4\bD;)NEGJo1~(gΟ4\bD;)NEGJo1cG׸u.gH~8~t=!߶>h]1Jr*?Sy߿>és?D; UL5_$b(аa<1f3(l<>9e9hjm=@' !ceAjX[%{uJb/aFwpY7D;pE*SQE)߿>és?D;pE*SQE'QK!߶>hODwO.1WLRb/a8p\pE*r a$t,O D&+&YE)NZ6D{|!OP*dXq@Z=V^<]R"%7Q:MpE*z$Cm?:|qbTD{ xp\pE*z$Cm?:|qbTD{ ?}{RDwO.1S"l拌U"%7N1:3$Cm?:|q\XEF"1  c2ɦQJSF "p6\`;V%UW/TȨM"g~(z$Cm?:|qgΟ4\b9)^ž1:3$Cm?:|qgΟ4\b9)^q^Թ"l拌TH~8~tt)ȨM"~(gΟ4\b)W)0|LGBBjy̢io(RidGH/xը cl*r*?Sy߻>édމgΟ4\bD;)NEGJo1~(gΟ4\bD;)NEGJo1cG׸u.gH~8~t=!߶>h]1Jr*?Sy߿>és?D; UL5_$b(аa<1f3(l<>9e9hjm=@' !ceAjX[%{uJb/aFwpY7D;pE*SQE)߿>és?D;pE*SQE'QK!߶>hODwO.1WLRb/a8p\pE*r a$t,O D&+&YE)NZ6D{|!OP*dXq@Z=V^<]R"%7Q:MpE*z$Cm?:|qbTD{ xp\pE*z$Cm?:|qbTD{ ?}{RDwO.1S"l拌U"%7N1:3$Cm?:|q\XEF"1  c2ɦQJSF "p6\`;V%UW/TȨM"g~(z$Cm?:|q9&?lXJQI#9d|6+blp! T*8\dDɗ#ʡKF\%0j 1<2⍤J$mXF5kif3CcS5h^x9%ІZuu㇏8~8c,<7<8j%PUOtD&Wb4%eӉ8ċ$SZdȾnH!,6Z >u\3miyr"hF%ZSU el(%l$d)Uc]QȞP{0IX ~ !%{EE׸{tw qaY^;J qm]o i--_һPڻC\[Wt[Z@hkjxkM qm]o jE--DIEѼ`i5|"loaS~|Hn q3 Zb\a`n,zy|DM qm]o i--_҈45ŵwWE5跆J"]֚ڻ(C\[Wt[ZZţyk 2c D§C 3y Ag ;ĹlݪX[%{uJڻC\[Wt[Z@hkjxkM qm]o jE--45ŵwWE5Q跆&F ׹@d:N (f1#3m55hw݉s ٻTJ45ŵwWE5跆J"]֚ڻ(C\[Wt[ZhkjxkW( qm]o j"Mj/Isu)d#{ $PcGl.fqk(j@ v cl*"hkjxkM qm]o jE--45ŵwWE5Q跆]֯DPڻD^-&^R&F:H'AĎ\Ͷ8Pաv%̀6 fPǪ+˪TD]֚ڻ(C\[Wt[ZhkjxkW( qm]o i--_҈45ŵwWE55Z7L &ς!0M*t0OyC7m&pCKl,ڠ%UW/T--45ŵwWE5Q跆]֯DPڻC\[Wt[Z@hkjxkQkQxo M{CaH!Ta o#;as6ۈ#\LCV}ؗ2X-@Kdp_.QC\[Wt[ZhkjxkW( qm]o i--_҈45ŵwWE5跆J"]֢$֢hA04> X‘6B7A?} F>$vmF†.d[7j=V^<]R&跆]֯DPڻC\[Wt[Z@hkjxkM qm]o jE--DIEѼ`i5|"loaS~|Hn q3 Zb\a`n,zy|DM qm]o k˞PfS!BfՔ J(V.*b:eB.'w b( ^EreP|#\ "T]CcV~+7Ƽy~'?qD߈w%&y~'?qD߈w%~?>-E?O].4@ d47nTr7Jy=ALsOm2\r&:/\B mb߿sg'ˏЌ^ZՔނ;-Ֆ}CO5WA4WtI<s]g7ʫpNEߪ¿'CNʰa̦:g#36QdzGκ= bb\nt^te_1s<"f,bɰp4Eb)c!HmNV7PJ(FW.H0a\P\|g}sS:pE`їR]8FVg}7:E=i6u=ٛG!/A4lkIiq]="-Hxpucuht&\\W7J]2`nzw+R9Rv.+ӛ.07j_҈9Rv.+ӛ.07j_҈bۥs>R@d.NjQn5ZmVq X В TYYT8U9-8njXEAYYe̴f;GH{5\$LQ}= ʳLgk.gv:C]FQ^$hr% &A2)%֋yC6و7,zy|DNndݫKL ڸW(ndݫKL ڸW(ndݫKL ڸW(ndݫ&,:IXupHxڂ84| ȤNZ- f ߻TJ9Rv.+ӛ.07j_҈9Rv.+ӛ.07j_҈9Rv.+ӛ.07j_҈9Rv.+DBH7o^$hr% &A2)%֋yC6و7,zy|DNndݫKL ڸW(ndݫKL ڸW(ndݫKL ڸW(ndݫ&,:IXupHxڂ84| ȤNZ- f ߻TJ9Rv.+ӛ.07j_҈9Rv.+ӛ.07j_҈9Rv.+ӛ.07j_҈9Rv.+DBH7o(mG.o#*373 VMb~#叐뢩= . ܆C99Hãe{lv8] `o)Šs\(WnJe.7E9RkyN.U(e.7E9RkyN.U( v":_W3DFKѾxUqGp\|B}u ĵeBf"x+)6Pa@YcF^cւJ+;i2ᆒt4$dXH U&D%ZRXKv1?us`Ƙk t-qde*\k~E(%"8ۮlyRR(E,2Is'k:M5qr3/jWs76GYuJБ)t5 )2CXqpDP)t5 )2CXqpDP)t5 )2CXqpDP)t5 *"M?$_6j2 ႒ @,LhȀ&7 aO q-+͖Fv cl*"s\(8] `o)Š@s\(8] `o)Š@s\(8] `o)Š@s\(4|o y,JCӆ H+MV1"08x%-E?O]V>Bcaqx6fEV76̇wZ>cWx aO:=qbF=NW9Ftj' ADʍfGeȲJ!k\cUs2DݹzJVʪiFEɍ $guivB%` e . LoVXtd51LbGQǦ0ɉ?D恳&"`]d䚖M]WI|h4dS^97yvM(eeMo/⦤2[e55uH %%MJ i㦱OC/B54̂|Z'ىxJ!]MjZ72b|kn&:\?yLzj㬮H5_H^S.hLJrnm$6 +%/ccyKG>o*d>Ǻfq3Q)҄S6փ3rsO[O0tqʏ&z%"CoZ]Y]5TC[9;\|~gJ9.?n`YDPP^['I*u挢KJ"#j LZHP0!*3xf*׻ .5 iغl+}?7o뜢V1&<ٛXр}fLadFF[Y%a.A) -𮇫ԢCkϲ;1)_/IDI#Jp욜XU<4#YKeEef2 Fjjlj&/,T!F+ԪW[睳DSsS. M )&Pf(-L3M y%fQLZZ Vvl~#叐렜H70c4ӡ$VF}oͧususj}f=fri|CT8jO'QT)g_LTfٖEFv3(N⍥)jɞ_agQƫe{og8]U :q.fr;oLe-Q]NYcpɠ R)Ms% D(ά5e2DKtSs4$(m I-rLN9l1.jlJdS * y{:+JOn7)>Qe:/0;qAeEzB7y`;1UF_,Q&ғZJV ѴN8Qϼoe)y syEP {kӔ\+0̋0K ]FMŒ%QtW07nb&r-L'7[-Ӥ0mj%dFR2Z; T䄧 NʡՍ[Y>tb={8aıT>n'21$]QA|efYq>fƏ4,䡝ز#g?3 <dɱ?~dQ$I$yz [7ʢ*zPeW3KB ۸3Ey`Ff(Ճ]Sq1$e0CP.ʭ;oQe8Ƚ_SwJ]ͦRm_҈5susiﳟW( e7}Dk)%.(YMg?Q)w6$o ee$+` ˌ<,H{n F6pK lwj=V^<]R&Rm5susjEﳟMe7}Dڿ@k)%.YMg?Q)w6DPn9K&('cy$ m,) [V\dbDub1*X<.eۻTJ5susiﳟW( e7}Dk)%.(YMg?Q)w6n9KJ"SwJ]ͨ6AA;$ clY`7I '²'5 $^ۯ0Rp/ڠ%UW/TﳟMe7}Dڿ@k)%.YMg?Q)w6DPn9KSwJ]ͫQRmDI A& d:yH VA>\C0.0B%yGlW7NQoaŭVo&yLĺ[="H叄?|?q]<1.܂~H$¼ry>vf}Z?rm3_Hr;(mVjÜWHy76)窊f +G$_ˎ6`x z=>r2h2Qb[6tXuڦ֘"Nd̚Xe/nV6QtRٳ U˨\SgX!A|ϭ.>?B3{.6qe+7F˴-e2yUm$KU҄qCǫ=Am< -&V)X`% !dlE}I5x. O;z-Y@so2ɼ"y> Ʊ,S[3kzǟٮʫv\EfQ{Y>Nї3tlFb2sU51%%2MeRSR>[(E98EMX ~ Āh ć\t7Vn,4Fݥ0>2nOo# %u2[:j*Hv6IތNXF,g;,qijZ+Z I*ӝ|=&_//b∱4z:fv;3Kdn]!ZyZrb*ѬzdCe)yqΜ]8 f[--P>in8t:E ˓%yHўdK顛,I~Yr3ٜpBy=SQ̈́w7XfQ1w`WXPeGϹ>W2e,VWTV3t#*VW.9 f0Zj(E8H>R=J"O39 g9S, $ ˸zNV#s6W|E,Sc/H~e"~.T4{=)jZQ4fzh;J%GG1)H;ŰJ[\BGB\犲Ng)='QM@Kdp_.QZnҘ7{F׋vɻ5J"ּ[MѦݥ0>2n_҈5)wixiLW( kŻJ`|ddo # :u&Lɣfb?xØkBlۮ 1ݪX[%{uJ׋vɻ4ּ[MѫQݥ0>2n5)wjExiLMkŻJ`|d@k^-S&hD&ytC2eh&M4e%. _`@(euaPǪ+˪TDּ[MѦݥ0>2n_҈5)wixiLW( kŻJ`|dk^-S&h(ZnҘ7{F$67?$H,Ne (A2h٣('.XpO0AD, wj=V^<]R&ݥ0>2n5)wjExiLMkŻJ`|d@k^-S&hZnҘ7{FDP׋vɻ5&ɱA"FAd:u(LGZ FA> rŋ~08 e %e]vcTJ5)wixiLW( kŻJ`|dk^-S&h(ZnҘ7{F׋vɻ5J"ּ[MѨ6M 2 ӬYBd:L6h K,\9ƾ+/Q/-ڠ%UW/TxiLMkŻJ`|d@k^-S&hZnҘ7{FDP׋vɻ4ּ[MѫQݥ0>2nDIln~7HYNd:&QւdѳFPO\ba5!Yx}Ym],zy|DMkŻJ`|dk^-S&h(ZnҘ7{F׋vɻ5J"ּ[MѦݥ0>2n_҈5)wj"McsD:t!P2&2|`q A Kn0v cl*"k^-S&hZnҘ7{FDP׋vɻ4ּ[MѫQݥ0>2n5)wjExiLQl$dAӧYɔu4lє,X's}V^ _m[ua;@Kdp_.QZnҘ7{FVJ\\T a0Y yq‚ \o"`@ɉw!}[W)U ]75%O?i5gxlkg"[Mg~bZg"[Mg~bZ'2S/Y\[9;\|~gJ+\frČHK^G8+)Mye//$O9Q2 $`;.ìlP7fJRߒ=͸OƲh E0M,MH2Yud# ^T~0*Zum;* 9x+C8d$e$:HѠW{ƢŨj*IS SSS6hu [X,qBC(dQH'eP=';Xҁ2r괭I ZPY">RHHH mk=Jm'>X ~g/Q:Q*jٹވ]1CFC&6v^Ԛk6Ni/.26=E\QfB,ѳ)ňYsHiRDD*$i"|A4LQ0XA ea!0]IC?+~%$-xql2̩u(942X#+:}ƫë)#ieӃKMʌN8/`lx\ b F]xOR̀~yu5y5k8#\?l R+9D!vSܻ79͈IIP{d$:1+-q-a2y)So⇞JN6?+םFWUk!O@tfI1]JSvVX6UdF:!'cHX,,瑗^#TʙɌ|!!rAimiIGGI_TUUTQNOLLO'! lcT2dёCKm%>Rrf`"[кDb$]~ nuDInB7Od:͓8QXCEO\c` 5 _` eu[8nԨ TE5`|j-٫gQ*UnX{:vj5EJD[VΦ"ݚ> nu_J5QՁkw"Mrx|$t!lDƊ,|bK`PF K-˭1v@j-٫gSUnX{:Pvj5D[V΢+T"ݚ> nu5QՁkw%UW/Tf[QlecA#dj 4Pd2X$, e0W BYm]m[*UnX{:vj5EJD[VΦ"ݚ> nu_Ҡ5QՁkwf[DW,zy|MTE5`|ꈓc(܄o #2 $u&pQ !`k,on p"ݩPvj5D[V΢+T"ݚ> nu5QՁkwf[MTE5`|" cl*j-٫gTDF#yH&C3%4Y̖0 Y`[}[ucJD[VΦ"ݚ> nu_Ҡ5QՁkwf[DW TE5`|j-٫gQ@Kdp_.SUnX{:$7!'̂GI2fɜ(A,h'.dHX0`@ۭjT"ݚ> nu5QՁkwf[MTE5`|"@j-٫gSUnX{:X[%{uJvj5&QAd:I6LF cE A>1s%B0X(#}%enEW\T7)wצڨvj5(zJR(W ͓ɖO _˕L c 8L;|ˆ-y~Dm:ƬVo?͍ylOc]≿ѬK\LOc]≿ѬK^9|{Z?U2Wj1!H{ՠٱߟ!9 VJr7yy5_-0|pܺK*,N+!-.:ý\s9XZW@R/>' 4ƒq)e)Pe"RZڎ"f#K~yYKuBĪHIGsj*Z!橤yɱ{Xf+ ;mi* | +267Wf7ivG,ྀ1If*p]WUVm!::ZColx/m-,FaH1>IbjLfɬh'^r .t*ڗmf|Nzܪ*ZC 0~\UVKCN^Iy ɯ9gY1Ӂ֓VJnf`Pz,EVSd5F:氒1;ɠ4tdK_ s |sSeı&\0JHS*2/%:^ (dƒ~8)Y}#D^w 5KHuUt3籯N-3gbv,56'R~SV$6E-U'&m4F2#;4=Iɿs[waGVe2_T/i,(TvQ"MuJKPsGJ^W&&Lɤ5R&7g%R|5Yo< ogl>jHdDUՏ6,y7''\J sz!S)+LhUe.JQ–N;[cSbOQwFk9%dX3~Tdރ$!HHWZ2LɏBnv+w5r,c>p'|fKG,;9G?>}u Ws>"LHs9grnf= 3:e\ZN̴Z&OgcE vo޲h4]\‰jHߜIteuGO &HIO>MM,䒈/ ipgOP I2~grɊ%К8Vlse)fԓGT>tE̷'ZrȊvI1VVu6qkuk|[012&r5UxɩE?SCϓSmy$$.oe3Ck9j"ɶX9ԧlI0D/dpEs=W]$ɚEˎ/gp(PDPu(&*sxɘ9!ÔL޻gis1;ZHQڌ"rLH{ت[{>MM,䒾<Ň-,cffXcfhs"X"Y*K-aY-XI&ÎLJehh's^E% FV@QAȵdV$Ӹ0m2:Ds-"b<뫿NO4dbT"aĆ(P:u+>d$eHm4 6*Ӊ<ɲv[^R_Ҡ5jz|V_הDW Z}޼*jջ.Q@Kdp_.SVh)wʢ$o 2fBܛ$+x L<2H{ (pLPoˈjT[^R5jz|V_הMZ}޼*"@jջ.SVh)wʈX[%{uJnEyKU&$y'Q2 [ZdYGKu`1C*dA%'>bY7& 'Ų$O:̒:^.9S!p&-ڕV_הMZ}޼*"@jջ.SVh)wʈPnEyKTիw/]+=V^<]R[^RDI (A8 d:̅a6H>VA>-"yd]f Pqʙ0}nԨ Z}޼*jջ.Q*Vh)wʚnEyKTEJիw/][^R_J5jz|"MIF O!d+ ɲA lγ$0b Tx E츋v@jջ.SVh)wʈPnEyKTիw/]+T[^R5jz|%UW/TV_הQl|J7N}x!XnM@OeHu$tY8\rCL(7e[*Vh)wʚnEyKTEJիw/][^R_Ҡ5jz|V_הDW,zy|MZ}޼*cQp{(u rl||[-2D#027`A."ݩPnEyKTիw/]+T[^R5jz|V_הMZ}޼*" cl*jջ.TD {1FCVdoi'fI/ua q^L &%҉k Bq,EP݅%G6'\OIjz|Pr@PyR0 рl1/L862mH/+~6ƀ< `|-UVfe}׺Ƭ6UfMBmo-aL;(ͼ%N;0sv_y{q-L[N(?kQѬK\D6;DFŗ'2#DрoPl4nn-7P )4+ƩCyxܝ>S ja Z@jl#:W-$ADvek'Yn؝sIK=a z X‘6B7A?} F>$vmF†.d[jT跆]ֈPڻC\[Wt[Z"@hkjxkM qm]o h%UW/T--DIEѼ`i5|"loaS~|Hn q3 Zb\a`nԨ qm]o i--_Ҡ45ŵwWE5跆EJ]֚ڻ@Kdp_.SC\[Wt[ZZţyk 2c D§C 3y Ag ;Ĺl"ݩPڻC\[Wt[Z"@hkjxkM qm]o h--45ŵwWE5+=V^<]R跆&F ׹@d:N (f1#3m55hw݉s EW|_T9F\! C\[Wt[Z8Te[0EE@ҁ>kˆoK yc8 :}#=X1>[ߪ:5_lGf"J◗7{׻uaIx |L4e?{W*NһuprngYyɎp],5t%{Vf6QXof# "Pqb䥵2w)Rh+!\xsr}ԊWņ cx#i7M|5{lޯY.Lk= ]va'k}l+/8k٤(PښL:`%Ya sTl_S ը۩tV:}YӣF.IyOPfX9CgǮe/ S&9&kFEgĭM sqXo<cZ.S~$74 t?Y`YOV=Z֓!Ak9SEI^n8UUD]DU(m1]!LU . Dޱq՝\FZbn8Iڈis$2MTHT˯H KeT:B&`*̛G DV"=R[Dcy[*x\er^!tn[-h.Aa{vX D:yƍZJ ;MUe8 ]jeEm;Қd ꌗkyXPH-hɦ2aT2s.07jw, )!{`Xb(k&99xdFH=:A"h7 xJKL ڸ_ άd/74Y5g;e6 W\.Y|Z**YCJJFN XÆ.Zj%"y瑐 ,t, (`sG1cKI HI&NVJ ;MUe8 ]jeEm;Қd ꌗkyXPH-hɦ2aT2s.07j%X4RBG ѸQ'P׀Ls$sB2{\@Eo#a*%UW/TҗLqq^;$9P/n ^486` q!-.jndݫKL ڸDW¬h.FY`@˕If3)K勌x/5 !q*JQsj$ʍ9Rv.+ I$1cRKa|2B2>HYQc\]gH踹/p H6;ly@st&\\W7J]2`nz_J9Rv.+XXq'ٵ&ŏv ,Ut8٭CHH²RQBTZE>hnA%(lkE!>q& q9glφ'FI3)|Lsl9[, ]"e8]%JKL ڸNndݫmJ@st&\\W7J]2`nz"@st&\\W7J]2`nz" cl*st&\\W4do y2JCGčV0(8_`&E"t0o(f1-ڕҗLqq^)tɁWҗLqq^)tɁWҗLqq^)tɁW%UW/TҗLqq^$Ey[ +)/49[PO`OP}ElȷjT7J]2`nzst&\\W+T7J]2`nzst&\\W+T7J]2`nzst&\\W+=V^<]R7J]2`nzHIF l$d:8\|HmA?KM> CdR'CKmo"ݩP)tɁWҗLqq^P)tɁWҗLqq^P)tɁWҗLqq^X[%{uJ)tɁW"M!$Y7u r#C&,pi4%  H .[Av@st&\\W7J]2`nz"@st&\\W7J]2`nz"@st&\\W7J]2`nz" cl*st&\\W4do y2JCGčV0(8_`&E"t0o(f1-ڕҗLqq^)tɁWҗLqq^)tɁWҗLqq^)tɁW%UW/TҗLqq^$Ey[ +)/49[PO`OP}ElȷjT7J]2`nzst&\\W+T7J]2`nzst&\\W+T7J]2`nzst&\\W+=V^<]R7J]2`nzHIF l$d:8\|HmA?KM> CdR'CKmo"ݫȮR?oknndݫ(xjBOPS N%G 7+p>A8ڲL!/T5冰A ipy~Dm:ƬVo?͍ylOc]≿ѬK\LOc]≿ѬK^9|{Z?2sg'ˏЌ]2dLXD"lLbKo$RW s١In3吞Zd᜚N^HjI, #ǬWKwraU (k ܖpj񡹶~zy!d R`H_3+6>[.6S˓BWk;ZNl[. + Vͥ--6Pzyw]9x6빒q+I˦qq\i2]Q_7':CU];5qӽvj,awUmef$$:30c%N2cyף3UϙǢFOfI(nQC_qY;$ZjN <^=8rh#͌lE^~P +IoP#:0/k`O-|h.t2pUo=k#bjh.ƈ~` 1u.a#w V`5Η+>x^&7>s[QC' ]I?m0+}eM' SES'VJ61iىG堵pf[ {xg|~ffd6ٟO! w:fsedi@FW"W*f%?go <'è6A23b^&1>'EI5jUR #<¹xxޒvb<";* B+!6 %iʎ6D3V"4ւlYForm'k$KbG85"#d7=9z%:\H๼[(Ϲ.%>`^p :p>j7ܶ\j>azfNk0~*\.ږKVYT2JrDלAVqsنoKI~`Ւh} ED>֍1QT(d #qU.͂Y|fYW<,_Co6]eruZm4OZe%TQCl*=jM䓆 }8\Q 㒹/@Cq"g ro[ &s]( +\sXSM<9@ىha[d2W [hpB}0@ԻĩotAb0;0&p6 ~["<jĚ&$foIEFCZ:=8d1xHg#(fdѡ> xЍ$udj7b|eZ& /= B$ˊw\`drf/nw*Ģpy!fUee$'_fL;0rb敐*~Լ1KOOTPI>%iY{a1pJr)ɪ7Rɐ#i-.)uE8p 9ko~NitQj\rOiC.9Zkpuث:ef^Ŕ\rcA<'7jFcgE,3b " ê9HE4o_'dա yH0}=.Kdj4mV1WϵZ4>ydt6N21{W!7fNq.X##ܛk:HiǏ~q3bZ~M{ iȧ٤>{`|'()}12Isf-E?Oʙ9Ftb&fyj5rҒkâw}[ OJJo5ںwuIqr} DULJm6)yxnm./p[#|8žtzČz)iXvKL%#4cs%Pk:92iK\ꄪ|9"&t|0_9x6Tfh$EFAѭtXN9#o9U]gŇ2^)#4Yobf{":(0Ӌ;rLRɣ15&Y]vUӭ-rrdH;/;Obɱ&$d6, i ۧRkJҷa4SIIzB EL 5,iMmLRZ̛#1eQ{/r.B{D+urw\f,8:7Vr)6q!vV  =ȹר,,mnks{i"NZ>N-?-U4ŲXżJj*L[([ĪiexDW 2ql1oN-?-U_J4ŲXżJ5ּf7K=;!*H1POy6Nkna&L٫ ll -ڕN-?-U4ŲXżJ+T8PT'c*PdCb%SL[([Ī" cl*iexQkxo 2{vCO?Tbfl# ;yr݈ZLWe,8[*L[([ĪiexDW 2ql1oN-?-U_Ҡ4ŲXżJ8PTEPǪ+˪T'c*$ZA.4d X"6~7 A?eFA:v孻3f˰.Xq+ȷjT8PT'c*PdCb%SL[([Ī"@iexM2ql1o%UW/TN-?-UDI1\h@EQ-PDeaP"&]y#hD;¯ȕ?PX՟g-_OlksQ715k_OlksQ715k!OtQOgSrN}lq+9UKYv3r٘ ݖ5BDu&!Gg-.ځH@ \l I1져ʙ/: !yL$\aZΡ^dh$8QynK5e- D=8aF_ٍKfΑ<Tm'2sfMg27BkiEJ%-0iM]X# itdfܯ`\wJnv nє'(4ĿNU>Q:h\+TRm5suskZJSwJ]ͦRm_Ҡ5susiﳟDW,zy|Me7}DڈdL{6u q@|+.2xP"E1, ``"ݩPn9KSwJ]͢+TRm5sushﳟMe7}D" cl*k)%.D `{1CWltYqǚ/m`c iJSwJ]ͦRm_Ҡ5susiﳟDW e7}Dk)%.@Kdp_.SYMg?Q)w6$o ee$+` ˌ<,H{n F6pK lHjTRm5sushﳟMe7}D"@k)%.YMg?Q)w6X[%{uJn9K&('cy$ m,) [V\dbDub1*X<.eER5susiﳟDW e7}Dk)%.*YMg?Q)w6n9KEPǪ+˪TSwJ]ͨ6AA;$ clY`7I '²'5 $^ۯ0Rp/-ڕﳟMe7}D"@k)%.YMg?Q)w6Pn9KSwJ]͢+=V^<]RRmDI A& d:yH VA>-E?Oʹ9FtEyKY#FGKi6$Fl }.Z-ת%% @Q]s96u5q+Ebh<HE3HƋ/˛0pa˽7-.bSː Ǣzԏ ]%4Bbtn|FO5uNx_76^[IT\e7$muu. N/bc6cd<r\Y!O6>e@ѻ nu6ѐPxWm#U1e>SpiIfo7s >B_=˗܃獳!:;\re^cIf.Ml6Xu>/ZF|"^4-:k6"L2++2pОْ(;+Б 9o'+S2^r,ΕPXSUY2n5)whxiLMkŻJ`|d" cl*k^-S&hD&ytC2eh&M4e%. _`@(euaJּ[MѦݥ0>2n_Ҡ5)wixiLDW kŻJ`|dk^-S&h@Kdp_.SZnҘ7{F$67?$H,Ne (A2h٣('.XpO0AD, HjTݥ0>2n5)whxiLMkŻJ`|d"@k^-S&hZnҘ7{FX[%{uJ׋vɻ5&ɱA"FAd:u(LGZ FA> rŋ~08 e %e]vbER5)wixiLDW kŻJ`|dk^-S&h*ZnҘ7{F׋vɻ4EPǪ+˪Tּ[MѨ6M 2 ӬYBd:L6h K,\9ƾ+/Q/--ڕxiLMkŻJ`|d"@k^-S&hZnҘ7{FP׋vɻ4ּ[MѢ+=V^<]Rݥ0>2nDIln~7HYNd:&QւdѳFPO\ba5!Yx}Ym]nԨ kŻJ`|dk^-S&h*ZnҘ7{F׋vɻ4EJּ[MѦݥ0>2n_J5)wj"McsD:t!P2&2|`q A Kn0ċv@k^-S&hZnҘ7{FP׋vɻ4ּ[MѢ+Tݥ0>2n5)wh%UW/TxiLQl$dAӧYɔu4lє,X's}V^ _m[ua$[*ZnҘ7{F׋vɻ4EJּ[MѦݥ0>2n_Ҡ5)wixiLDW,zy|MkŻJ`|ddo # :u&Lɣfb?xØkBlۮ 1"ݩP׋vɻ4ּ[MѢ+Tݥ0>2n5)whxiLMkŻJ`|d" cl*k^-S&hD&ytC2eh&M4e%. _`@(eua^EePs+]kŻJ`|d9Y)qnTESOXL5l)g (1 B 1qE_&%L` ]n !h)V-o~?oK)4+^"Wkg7?S7iWC?2/Y\[9;\|~gJ˔0RV>Ob'Sz8@n-/zI*o)Ay8g ˎs=_&k]Qj.`=fܜ#:Ẇ\ىkr<2:C%7IM4YH'gRRJ!U[(a\b*t I,(/e,a-*^o{u. kkWgW=iDS(^ yMr:\sĀb7f)zC:3uGqrE-UHRz`w]S( GW.R wjYl^Wn4/VΤ7ޯDt X䴟f@ˤ/GLX6s(rR~~-ع}Xk)pfQy|II()ɦ]4M5M1mUvtGwyәgyGyx4isi.&hkC$o}7j 7n5aiJWj)JR(PǪ+˪UPǪ+˪TEJR(JR,zy|W,zy|DW(JR(PǪ+˪UPǪ+˪TEJR(JR,zy|W,zy|DW(JR(PǪ+˪UPǪ+˪TEJR(JR,zy|W,zy|DW(JR(PǪ+˪UPǪ+˪TEJR(JR,zy|W,zy|DW(JR(PǪ+˪UPǪ+˪TE^EreP??Z+*.xV/T]BzjfG53?uaKq;?v{&F3F-x?c2S/Y\qiCmCu7TI ṴO=KS.h5v($xϵjlDf-}]uwdٱzGP#:WNFsƈK#9O\i9!ϸj"s$u]U GR:R>LCcv ii:/ΰ9MN,E]q-~NDro(<Ȟ09K/YQTs2]W/W ,xO+}Qe(:+2̱QH뱾DcҚK1[^[IO Sd'iXNpnWytByBb[ Hca9C4/'3Eu:1{0^Ilfby 鯉ȵ ZY6/4:ę#Ŗ !p;tMi@v?:V&si3=UoH\IQ2d: Ev5/f`qb%gdN-aGulڨvj5D[VΫWlJUnX{:vj5J"D[VΦ"ݚ> nu_҈5QՁkw"Mrx|$t!lDƊ,|bK`PF K-˭1v cl*"j-٫gSUnX{:DPvj5D[VΫQ"ݚ> nu5QՁkwEf[QlecA#dj 4Pd2X$, e0W BYm]m@Kdp_.QUnX{:vj5J"D[VΦ"ݚ> nu_҈5QՁkwf[W( TE5`|ꈓc(܄o #2 $u&pQ !`k,on pݪX[%{uJvj5D[VΫQ"ݚ> nu5QՁkwEf[MTE5`|꿥@j-٫gTDF#yH&C3%4Y̖0 Y`[}[ucPǪ+˪TDD[VΦ"ݚ> nu_҈5QՁkwf[W( TE5`|j-٫gU(UnX{:$7!'̂GI2fɜ(A,h'.dHX0`@ۭ7j=V^<]R&"ݚ> nu5QՁkwEf[MTE5`|꿥@j-٫gSUnX{:DPvj5&QAd:I6LF cE A>1s%B0X(#}%enTJ5QՁkwf[W( TE5`|j-٫gU(UnX{:vj5J"D[VΨ62F 1 LYg 5K(h ,` A+!,.p ڠ%UW/Tf[MTE5`|꿥@j-٫gSUnX{:DPvj5D[VΫQ"ݚ> nuDInB7Od:͓8QXCEO\c` 5 _` eu[8n,zy|DMTE5`|j-٫gU(UnX{:vj5J"D[VΦ"ݚ> nu_҈5QՁkw"Mrx|$t!lDƊ,|bK`PF K-˭1v cl*"j-٫gW<)HkZ*bz:a\&6NK&Y<.V07&P0K (0 o5kit4֩y! '_"oTLӒc5XF >4x{\ܶnhG8LD3=8 9"gw,kIldֻ"[ͳQH.^^|>XT)kYETbi[ˤlr*ҏI)dPU8)&iF3Ep_1=KNeFҚ'9wDuXS9 tci~s"m$Mu͚m]!@ee͕'8I]xr"2W'YU~N1z1Hr{30c%BѰ8 mV`m-]4Xk0]V_הMZ}޼*w+R5jz|V_הW( Z}޼*jջ.U(Vh)wʢ$o 2fBܛ$+x L<2H{ (pLPo˷j=V^<]R&[^R5jz|EV_הMZ}޼*@jջ.SVh)wʯDPnEyKU&$y'Q2 [ZdYGKu`1C*dA%'>bY7& 'Ų$O:̒:^.9S!p&ڠ%UW/TV_הMZ}޼*@jջ.SVh)wʯDPnEyKTիw/]Q[^RDI (A8 d:̅a6H>VA>-"yd]f Pqʙ0}n,zy|DMZ}޼*jջ.U(Vh)wʚnEyKUJ"իw/][^R_҈5jz|"MIF O!d+ ɲA lγ$0b Tx Ev cl*"jջ.SVh)wʯDPnEyKTիw/]Q[^R5jz|EV_הQl|J7N}x!XnM@OeHu$tY8\rCL(7e۵@Kdp_.QVh)wʚnEyKUJ"իw/][^R_҈5jz|V_הW( Z}޼*cQp{(u rl||[-2D#027`A.ݪX[%{uJnEyKTիw/]Q[^R5jz|EV_הMZ}޼*@jջ.TD {1FCVdoi'fI/ua vPǪ+˪TDիw/][^R_҈5jz|V_הW( Z}޼*jջ.U(Vh)wʢ$o 2fBܛ$+x L<2H{ (pLPo˷j=V^<]R&[^RObYPqA.\@ʫ TZ8kxZee+6 %?5U?.UYUߴ,^Lr/Ca׬ߴ,^Lr/Ca#_TiV%_?Կ]I>BZcqx9eF|76̀wM<D cPO yO5=icTgN2$!oW.v'8.P] k Ts;ʉ vB]10ߘ9Ft]k6\V^n!-cK)M]!=IA7m:t[릙kHºHl\h")mL{\\i }HckA1v:b3mg_abk)ߙ9GM4̪әSPTIoߨ9o<<[m=f]2y%NVeKaѶ[E^:S\G>hBWj:0Mm+qsDYJHH x'&SĚј+_IvNӝ3L17+A{qAm5]3H_5.ɾM[|앪er9cM*s\tC5@qWWC>x~hZso}\m F?tn6 7Z{ X‘6B7A?} F>$vmF†.d[7j=V^<]R&跆]֯DPڻC\[Wt[Z@hkjxkM qm]o jE--DIEѼ`i5|"loaS~|Hn q3 Zb\a`n,zy|DM qm]o i--_҈45ŵwWE5跆J"]֚ڻ(C\[Wt[ZZţyk 2c D§C 3y Ag ;ĹlݪX[%{uJڻC\[Wt[Z@hkjxkM qm]o jE--45ŵwWE5Q跆&F ׹@d:N (f1#3m55hw݉s ٻTJ45ŵwWE5跆J"]֚ڻ(C\[Wt[ZhkjxkW( qm]o j"Mj/Isu)d#{ $PcGl.fqk(j@ v cl*"hkjxkM qm]o jE--45ŵwWE5Q跆]֯DPڻD^-&^R&F:H'AĎ\Ͷ8Pաv%̀6 fPǪ+˪TD]֚ڻ(C\[Wt[ZhkjxkW( qm]o i--_҈45ŵwWE55Z7L &ς!0M*t0OyC7m&pCKl,ڠ%UW/T--45ŵwWE5Q跆]֯DPڻC\[Wt[Z@hkjxkQkQxo M{CaH!Ta o#;as6ۈ#\LCV}ؗ2X-@Kdp_.QC\[Wt[ZęCe (.@i@5 .*b:eB.'w b(نp)` 6yN^R0M^)l׹g(>&R z-.U9g/y!ݠƹǺpԧ;c{V6j .NcZc $d"Qn`ѣ*0bA+1A/|8ᇍp¹^Crk!=b;J,mG 0(X`倌 x`Yu[G *ՊJcF/Ccݱ ;DIUŸ:иZet)4B.<9Q>E+sm1<ˑrAțx q=jlc6\oW&{5v]0k6[n5lJDBDJ(mM]]LT0 `D2j6Q-B8N+&YO M L _"= + m2u Ǿ^3`H:5֚9{$ܰs0#r#QfKhYKnZKz 1 껖o/ n;,*Iɠ^Yڿט@2ף^ylYfsV֝f yP+ϼ8O1 /2l:}(vMXdHR:c)n!la%p&Q˒1xfFL>WyNr.`JH RlO ëɆ#[%0[zz$I Tꊮb^fO6 1- %ckrYUY9ACэF,܎:;TȠ &*&",mOz@XC]&kXϱt_FSIvav7ӫNqk[n뾋jrW|)tɁWҗLqq^;Qƪi҈uo5WQ'r7`w_uKL ڸNndݫi҈uo5SN>DxqPwy'&}7J]2`nzst&\\WVqeJ̏6PUF a5>a#q2$Byˣ-W|]4vɴ'{K}-jjZd9R t؃$Oѐ5i|jKL ڸQi "Ad] '0 cI(`PpLD` uPͶb ͜2˛;7y`vf 9gإze*ai3% =p27&¡ڢ deK=p>jAr1"@J;(v*Cr9'`+eK0nRmX3B'ڠP#FJ5Z7J]2`nzst&\\Wv;&s7l[}exSVUeas8V HAo$8prZqS'iKL ڸNndݫYzk.?Ԗ-2njU99hK;|͟P4ݍWUbFKѾgDxq;4PxDN R.uh""`B׍2 ,{@;DoKp˞Pwy'&}җLqq^)tɁW ӹϥ>j}(VU=nNM/3zvu^ndݫKL ڸXV}(VU4sDO'rriy۰;t&\\W7J]2`nz´sDOsJ"}ռ8O~;<ۓK޾݁}כ.07jDI$#yζAV2w.R>^$hr% &A2)%֋yC6و7҈uo5Wpf*t/:)JEΰUTLZAŀ}(h[ nsߪO67`w_yRv.+ӛ.07jaZw9'[ÍTӹϥ>jTmɥo_nҗLqq^)tɁW ӹϥ>j}(VU=nNM/3zvu^ndݫKL ڸXV}(VU4sDO'rriy۰;t&\\W4do y2JCGčV0(8_`&E"t0o(f1;Qƪ @\nvE_GSE8)H ^486` q!-.{Aܜ^fo7J]2`nzst&\\W+N>Dxqw9'[ÍTꃿ͹94yRv.+ӛ.07jf-'2VG0NP NsHdĹcy=/DܢwnZV`Bsh恣Rle.R^v>_riÞ Lv+y*ĥ#nhKʚ0 K +Rt լ/7J]2`nzst&\\WU; a& ,vpե "19L@.b mC4A{%\ fY)M$A` "i&9N=uDq`1l=Bʘ"=lbճ\XA +X{Ha6α9Rv.+DBH7ozsV{wG޻.07jDI$#yζAV2w.R>^$hr% &A2)%֋yC6و7Ot&VIƺPȩʀ<͗A$pRh3AU$9ypl1`#p7& B[6\<$FNSOoۣ)tɁWҗLqq^g&:*(]~>xӡ2L5Ut}N :n͎+Rv.+ӛ.07jpOt&VIƺ}$BeozVlwHzWst&\\W7J]2`nzᏤ4LtIN xO7cw9M?gn=+Rv.+DBH7ocUVI!qo G(`LYfIW89T@OKꂢ Q]]GJf #4ԡ8REqn˚+f/hysXbadl 0:jq 9krrr뛸@A4.'<.=Bsr`Rk!Je5g6J&ĸ[唖؞{t6v2f[i =a0Y},lBp5'MF$8 $Y!`E%jHa4a<؊%&NXC6~ڻi[> =/oDX8@3]+oh)o3k߃kF~eW[N94|rNceIٛ8YNL?Y[rTSƂ3 UIFBղ2KȴB(˿IޫOy&Wz?+u|26@iEş,DwM-{K\Bظ YQ ݧH|*W@ֹyɧ/]\ "[O6k/e%_94 U gD.} ~$B鵗RMZlS6~ڻiHAvx'nfn~Kܡ!b_ /nP5^hiW99BȼV͚Iv3qn&gUB Cr߉*meeu#/{|gVMZlR iݞ7FCۺٛ{w(HuP,m^.W<-w NXYi̘6'CMgq;opO(į6R'e(PG&7RL[_1Ns5ƵIe [j.Kq`6k9Ӡ:4''~AX>Uu)Xl44<6z('PH0nKߥwċr[s̻lwbIޫOj&g8tVhjkl}6u1h|9um&_ |gVMZlU%>z/kUY3vA_ |gVMZlSw9۷2T?m]اlwbK|^a$ݿy¥}$jUz/kS0wwnT!F%֒󢷌L[0L!k {K#g@QwHYk?>8oy$rf'!FcE[덨A}dupud;xaO+gmy<.n1^eG-8_r}n+P+atÅ[sbGs i{ޜxުnVۊmfM+m'NIItկf}ru~1W?ʏ[*qŶ86Zjwu>U}VͲnè^2i9wr'kٿCŲiT H+PGkcw_Ulp;׌NC4?g+ٿCŲiOw Up0> 1 f?Qǘ5Wl&:.??^ej?bOw Uq0@-jw=d{Q޼dr'kOPO<=aulq,<{|<x- 0aUwNTS5X))DN N#7QH i"otKVW+bRH  1UVaؤfJyd/xFܢ6$ L6\!nR伛6=_?c9GRz-~)o }Wht}v|Tfûvwl&?ӛ,u'R@Mb?Rj M6~t}ᅴKwn:Y͖:1Zi}F6pm|'o᮵~KIհPނ#q7X[~_2v8?ęc=)1Y俚~4ge&eU3W%f$Ӛe8eu9v2~w&&?_F!܌-陷6%զS5Y\˓Z,#,K*^$쏞C1ΜLx~[ˤkWg&0Ǖ\/p`%a˫77ԷF6vr%i9K$pJ)oOӞLhS_jʾ5sRlƺUg,R .J &1;g,yuEOk>هU+!"'C(mK!L0؊%MQQ <8:wF|3҂d:Y1~FoA$` O,!Iֳ2,S- U-v伓2̾*ASTbY0(Aaa}g8Fy/eZ-<3vE}tn1@Y&+(6Fc3Y-OC μ;w5H3>Ք(VH/'KYsBc]̎r#XtҮSN\Ys6xKm< ^Q :d9louz&ܺ(RQ!31?1+iNU=\3IEf'I sZ:75Xxi=z>ˍ%)JJRD)DJRD)DJRD)DJRD)DJRD)DJRD)DJRD)DJRE D<&PߒJH~t\O6VOij|7obD2WWqLM`أvML ! ';e׀iblepnpp<>璟RKYo53qpn"8 .Two:}{F$Oc%uqO1"{+}9ԪgSJ}JQ.vuS?ލ>w~F:\xE#6*VIZ`v-(≅x/|4\ĉd)5LyC*xE,%P,m h8`ü{q D ma~<^\S@g 3 s[Hlkhdk.ZVV$Oc%uqO1"{+}9ԪgSJ}JQ.vuXw~FW=>ĉd)4璟RKNy)*Dw~FCwrs'Ә=>SUzs%>W;:s.ܟwwnNbD2WWs'ӞJ}JQ.vu9ԪgS}ލHJsNbD2WWsOU%Χ<^\|swr~߻ѹ9]\SiHJsNy)*D璟RKO9wnO?z7)*jq2V L"bwX_uno;nn۱|m㇇ <\ĉd)5sK&UHAmRp%P %  oq0Km۱ƾ<^\Ra^83^ܙӖ5RܙmvUKu}նHJs_5i$uaBIM PO+`aQn/,.n pp1+sOU%ί2Uc cYR5-ƺm0TCıfx~2-k^-e'MZ>Nǣr&S z0 -*-;ŲmÎ> m<a9]\SkC0ڤ'Z:]`G``aa|>0y)*Dr#rݗ/.n-Zmm#rema~Bs'RIJ, q&'T?` [|#\ W r8Ym+krA]pۆ[0F^g aqw73n]}mkEC&˺[ܼvr#.~QF[/i fؽ`GQDEWayBڅН]ٜ[B)dž/e\,ĵ\XoDD8= t_HJ_vk+?sG*zrkXK6V03-2&L'S+/]E cCx I˳zjfұYXOѦJ5 b@}Eа .qq p pdLO05꫞8Ó,(G@m8- paQYH)Je.x Ƴo( ;|k[e5auۇv8aƿrR[TkGVˬX`,lۃ1?ÆSɀgHϓf|Sg&wZm^ڽ y]\SiHJsNy)*D璟RKV>9]߻ѹU>w~F:`F" p*I]vxwZ(v[⇅x,<\ĉd)5y*`F1%P\n  `$ļ ap {C-c><^\RjL1N[ o[VY+o}yRIJ, q&'T?` [ 1yLTbR66l=֜mUmsְkmo^h򔊴k++)pWbzHW2@zN Vq.w^/!N=ixPxd\To%'wI0N7 C,P1J漪HkWr]No;2Kl d!O1;A'Kt: D\ I}JPQQ.ϛo=e84qBs3t~gDRTZ>)Kt:rƩHNfzN@}>RGv|.A D\ I}JPQQ.ϛo=e84qBs3t~gDRTZ>)Kt:rƩHNfzN@}>RGv|/)o:i>}!9:?Qԥ _WNQQ I}JPPh.ϛ'(cIgDS I((|Rgt7qiG'}O'3='G >:]7S*<aiCCPICC ProfilexTkA6n"Zkx"IYhE6bk Ed3In6&*Ezd/JZE(ޫ(b-nL~7}ov r4 Ril|Bj A4%UN$As{z[V{wwҶ@G*q Y<ߡ)t9Nyx+=Y"|@5-MS%@H8qR>׋infObN~N>! ?F?aĆ=5`5_M'Tq. VJp8dasZHOLn}&wVQygE0  HPEaP@<14r?#{2u$jtbDA{6=Q<("qCA*Oy\V;噹sM^|vWGyz?W15s-_̗)UKuZ17ߟl;=..s7VgjHUO^gc)1&v!.K `m)m$``/]?[xF QT*d4o(/lșmSqens}nk~8X<R5 vz)Ӗ9R,bRPCRR%eKUbvؙn9BħJeRR~NցoEx pHYs  JIDATx=]]U3s涎}؉8%HN4QѪB O  /Di!P@%8%BIخk;7N8_=5gfٳ{9={Z֬Ykzݥ_UUhDIJR]1T%#7 Y'S{ɶ6kyyÇcǎsw;w`0( l4l(z͋oHʼʼzk Oy иNw|^]x [[[ G~Ze`[d z$#%pD =wDUX؝@—bx)?W%4$>(BKKKH{)$v'{_vP/{.-A1B:*(~zE+*nQ>2B7_] ?湹;BCGg:tJ!0Eܰ;o QI`Լ7sS#W\ POv !jƷ!>20WC4C HF*M8F(,'C S>~ÿ q }ChPz}'*Ոmk0S,_ м'n=׋ʰ r1 ZݽЫeX|4| =4q HHx5$h-/2|!z~ğ&(]zx 14Әj|h`y(ʐJI~?Ƣ7 @ 4 cjKyE0r0 Q3oGZWNh-Lchx L[(M'~{WW^yɕ )ΧV%$z_0Y^ v <"m0*EmZw>c鯐j]O2zw1dmmJ 5MO),.}ø0R'@V6<|?Lls;h/8I* ^%(y(a)Ө&`/7ɡc_3'_"o]}%cIx|a"~mFE^h:8?>nn;^OP[ܵ|?93۷^%_װC/gO65 p2K 9r]Tծʝ&>Ɗ'^΍WOLMf,^[}=rۥkZ d8#eoa`Gw/e/Z,0#8|@WKjb uq?{EzQ5 ?aϏAj':G,=H|l75SH]=;ѹ \~ey\aw\9uit};e/x_K(` P:xŨPE,E@a> <O6V?y `]iT$oBW~: +:ٓ#+j|//%YٽCnVe}dKK\z{iFOڥ L/T` @ Mx}x*?M=b%OzsKT#ƦnaAE.}ZǛzP>)*;b]3G嗗0; +WBy;;ϓ]G;wWdX >x\YwCI#'_?$a1(k l~WeLFEDԷ29sb?~OVW/#?XGAoڷi*EɌN ['w@t?{KO>ounJ?10iA)*QH NIَIg޻6u#+7_Sۓr)j^FB2gLr"IdoÖ}s: L\5+7WwӠC?6ۗtJOO@Rkoc W{Bh]R b(ZB Qwѣ;ٰT<4b1j>c/V<;T?WG=7& 'Ѣ0ޥKb7<|3El`lPƧy&"ob?>3mǶi*:9+@y `!Ø d1H5UԼ>ucN}jGbWiOiCtXVٳ8ɱǖq-1B.ab#KD"%|\ w%f)EFBW]F{)L0Ƕ@Z#XɅ]_ܱS"ʻ;Te=\,GTWT:M=;ŸEj/%-k&6C_JvXJVګIN${="t…^z -N[)s@j4& 0"ݻ@A8u+ڱ0Eu}[ +bYSO|?b3F|83$*u>kY`ku:LHSw!U5˷9>JXL}&0l" `V#>@Ei>Oj`%c:/v=( Й:d9ٿP3B̼;0d.W/|m n]7!U>,~n0!YTu;BF!B/oXcwVgrϙ4%\F/Sf̜HTkX5t)R\@J\k' Z'~.bY0>\VJoz8omm1]8Vr߆СcM 𲯘ɃgIL{^|}ߋ!J̨@"Ǭ֒F0EiM[CLak6f7WX>u4>v3Vɴ[ x銠tsqAKֶYo1{\7c SQ2-Zj`ε1C,7Nc. 1%k}zI d-ϲ5 ,a0]YOH1r e1SI:tDHQZ>7>OfM}#2͙[Qiti`Ukٶ찵נݼ=Wg#ydul`3.6It} uWEߚ%<]|o\\@H10(|@6[Bwh= & qgF,$¾aݥί47Sg>V7ͥ廗F {÷_|sRs Ι a#1M4&s]b ÛszO[7&!=5$ * `,BŎqm@rs(>@i@\2:1ϔq$7|$m.3(Sh%ܾ}bU*}K[{#ƴ}N5$i#7ĐSʻ`;PX P3 /7䋯EKHk <\b99PB-%z@ɬQ^ ڮkL S4t ls6fX*zEPFp/LNeWUR:\0͡o5| z vzct^~*gnf.և򠊮 8looז@!=Da "vz]k`ХG ۾>7c&#*Vkb%ï]Rn:  1F e/@. `L=h'{i-=7ã} &݊痗O*kx, #b(5y&Fb㪕#`4pK3 _g <Ļlh=t? 8^;1R60=ѐT,F;œY!$&~(`$3T(GA 3^`dh88(-DCQQC=R0EDU{ Z ='R38yv1*0D/*4IgLkP*u㨔kO~$'j{ J&@ZÅRhqT2P~E|TK& T0qTX3:+}%". _HkA@ .]RPB(`Qwpե9$>h]/x Wr`/Q(=?; z!Qx{jWpW%@>a*:UԞHIENDB`Spreadsheet-WriteExcel-2.40/bin/chartex000644 000765 000024 00000013271 12153741327 020162 0ustar00Johnstaff000000 000000 #!/usr/bin/perl -w ####################################################################### # # chartex - A utility to extract charts from an Excel file for # insertion into a Spreadsheet::WriteExcel file. # # reverse(''), September 2007, John McNamara, jmcnamara@cpan.org # # Documentation after __END__ # use strict; use OLE::Storage_Lite; use Getopt::Long; use Pod::Usage; my $man = 0; my $help = 0; my $in_chart = 0; my $chart_name = 'chart'; my $chart_index = 1; my $sheet_index = -1; my @sheetnames; my @exrefs; my $depth_count = 0; my $max_font = 0; # # Do the Getopt and Pod::Usage routines. # GetOptions( 'help|?' => \$help, 'man' => \$man, 'chart=s' => \$chart_name, ) or pod2usage(2); pod2usage(1) if $help; pod2usage(-verbose => 2) if $man; # From the Pod::Usage pod: # If no arguments were given, then allow STDIN to be used only # if it's not connected to a terminal (otherwise print usage) pod2usage() if @ARGV == 0 && -t STDIN; # Check that the file can be opened because OLE::Storage_Lite won't tell us. # Possible race condition here. Could fix with latest OLE::Storage_Lite. TODO. # my $file = $ARGV[0]; open TMP, $file or die "Couldn't open $file. $!\n"; close TMP; my $ole = OLE::Storage_Lite->new($file); my $book97 = pack 'v*', unpack 'C*', 'Workbook'; my $workbook = ($ole->getPpsSearch([$book97], 1, 1))[0]; die "Couldn't find Excel97 data in file $file.\n" unless $workbook; # Write the data to a file so that we can access it with read(). my $tmpfile = IO::File->new_tmpfile(); binmode $tmpfile; my $biff = $workbook->{Data}; print {$tmpfile} $biff; seek $tmpfile, 0, 0; my $header; my $data; # Read the file record by record and look for a chart BOF record. # while (read $tmpfile, $header, 4) { my ($record, $length) = unpack "vv", $header; next unless $record; read $tmpfile, $data, $length; # BOUNDSHEET if ($record == 0x0085) { push @sheetnames, substr $data, 8; } # EXTERNSHEET if ($record == 0x0017) { my $count = unpack 'v', $data; for my $i (1 .. $count) { my @tmp = unpack 'vvv', substr($data, 2 +6*($i-1)); push @exrefs, [@tmp]; } } # BOF if ($record == 0x0809) { my $type = unpack 'xx v', $data; if ($type == 0x0020) { my $filename = sprintf "%s%02d.bin", $chart_name, $chart_index; open CHART, ">$filename" or die "Couldn't open $filename: $!"; binmode CHART; my $sheet_name = $sheetnames[$sheet_index]; $sheet_name .= ' embedded' if $depth_count; printf "\nExtracting \%s\ to %s", $sheet_name, $filename; $in_chart = 1; $chart_index++; } $depth_count++; } # FBI, Chart fonts if ($record == 0x1060) { my $index = substr $data, 8, 2, ''; $index = unpack 'v', $index; # Ignore the inbuilt fonts. if ($index >= 5) { $max_font = $index if $index > $max_font; # Shift index past S::WE fonts $index += 2; } $data .= pack 'v', $index; } # FONTX, Chart fonts if ($record == 0x1026) { my $index = unpack 'v', $data; # Ignore the inbuilt fonts. if ($index >= 5) { $max_font = $index if $index > $max_font; # Shift index past S::WE fonts $index += 2; } $data = pack 'v', $index; } if ($in_chart) { print CHART $header, $data; } # EOF if ($record == 0x000A) { $in_chart = 0; $depth_count--; $sheet_index++ if $depth_count == 0; ; } } if ($chart_index > 1) { print "\n\n"; print "Add the following near the start of your program\n"; print "and change the variable names if required.\n\n"; } else { print "\nNo charts found in workbook\n"; } for my $aref (@exrefs) { my $sheet1 = $sheetnames[$aref->[1]]; my $sheet2 = $sheetnames[$aref->[2]]; my $range; if ($sheet1 ne $sheet2) { $range = $sheet1 . ":" . $sheet2; } else { $range = $sheet1; } $range = "'$range'" if $range =~ /[^\w:]/; print " \$worksheet->store_formula('=$range!A1');\n"; } print "\n"; for my $i (5 .. $max_font) { printf " my \$chart_font_%d = \$workbook->add_format(font_only => 1);\n", $i -4; } __END__ =encoding latin1 =head1 NAME chartex - A utility to extract charts from an Excel file for insertion into a Spreadsheet::WriteExcel file. =head1 DESCRIPTION This program is used for extracting one or more charts from an Excel file in binary format. The charts can then be included in a C file. See the C section of the Spreadsheet::WriteExcel documentation for more details. =head1 SYNOPSIS chartex [--chartname --help --man] file.xls Options: --chartname -c The root name for the extracted charts, defaults to "chart". =head1 OPTIONS =over 4 =item B<--chartname or -c> This sets the root name for the extracted charts, defaults to "chart". For example: $ chartex file.xls Extracting "Chart1" to chart01.bin $ chartex -c mychart file.xls Extracting "Chart1" to mychart01.bin =item B<--help or -h> Print a brief help message and exits. =item B<--man or -m> Prints the manual page and exits. =back =head1 AUTHOR John McNamara jmcnamara@cpan.org =head1 VERSION Version 0.02. =head1 COPYRIGHT MMV, John McNamara. All Rights Reserved. This program is free software. It may be used, redistributed and/or modified under the same terms as Perl itself. =cut