++ed by:
2 non-PAUSE users
Author image Jeffrey Hayes Anderson


Spreadsheet::HTML - Just another HTML table generator.


Object oriented interface:

    use Spreadsheet::HTML;

    my @data = ( [qw(foo b&r b&z)], [1,2,3], [4,5,6], [7,8,9] );
    my $gen  = Spreadsheet::HTML->new( data => \@data, encode => 1 );

    print $gen->portrait( indent => '   ' );
    print $gen->landscape( indent => "\t" );

    $gen = Spreadsheet::HTML->new( file => 'data.xls', worksheet => 2 );
    print $gen->generate( preserve => 1 );

Procedural interface:

    use Spreadsheet::HTML qw( portrait landscape );

    print portrait( \@data, td => sub { sprintf "%02d", shift } );
    print landscape( \@data, tr => { class => [qw(odd even)] } );


Generate HTML tables with ease (HTML4, XHTML and HTML5). Generate portrait, landscape and other rotated views, Handsontable tables, HTML calendars, checkerboard patterns, games such as sudoku, banners and mazes, and create animations of cell values and backgrounds via jQuery. Transform Excel, HTML, JSON, CSV, YAML, PNG, JPEG and GIF files instantly into HTML tables.


  • mktable

    Quickly generate tables without writing a script:

      $ mktable landscape --param file=data.xls --param preserve=1 > out.html

    If you have HTML::Display installed, you can direct the output to your default browser:

      $ mktable landscape --param data=[[a..d],[1..4],[5..8]] --display
      $ mktable conway --param data=[1..300] --param wrap=20 --param matrix=1 --display
      $ mktable sudoku --display
      $ mktable tictactoe --display
      $ mktable calendar --param today='{bgcolor=>"red"}' --display
      $ mktable beadwork --param preset=dk --display

    Run mktable --man for more documentation. You can also use this tool to quickly look up documentation for methods and (most) parameters:

      $ mktable --help handson
      $ mktable --help theta
      $ mktable --help td
  • benchmark-spreadsheet-html

    Run benchmarks against several different HTML table generators available on CPAN. See benchmark-spreadsheet-html --man for more.


All methods (except new) are exportable as functions. They all accept the same named parameters (see PARAMETERS below). With the exception of new, all methods return an HTML table as a scalar string.

  • new( %params )

      my $generator = Spreadsheet::HTML->new( data => $data );

    Constructs object. Accepts the same named parameters as the table generating methods below:

  • generate( %params )

    Generate an HTML table.

      print $generator->generate();
  • portrait( %params )

    Generate an HTML table with headings positioned at the top.

      print $generator->portrait();
      heading1    heading2    heading3    heading4  
      row1col1    row1col2    row1col3    row1col4  
      row2col1    row2col2    row2col3    row2col4  
      row3col1    row3col2    row3col3    row3col4  
  • north( %params )

    Alias for portrait().

      print $generator->north();
  • landscape( %params )

    Generate an HTML table with headings positioned at the left.

      print $generator->landscape();
      heading1    row1col1    row2col1    row3col1  
      heading2    row1col2    row2col2    row3col2  
      heading3    row1col3    row2col3    row3col3  
      heading4    row1col4    row2col4    row3col4  
  • west( %params )

    Alias for landscape.

      print $generator->west();
  • south( %params )

    Generate an HTML table with headings positioned at the bottom.

      print $generator->south();
      row1col1    row1col2    row1col3    row1col4  
      row2col1    row2col2    row2col3    row2col4  
      row3col1    row3col2    row3col3    row3col4  
      heading1    heading2    heading3    heading4  
  • east( %params )

    This method generates an HTML table with headings positioned at the right.

      print $generator->east();
      row1col1    row2col1    row3col1    heading1  
      row1col2    row2col2    row3col2    heading2  
      row1col3    row2col3    row3col3    heading3  
      row1col4    row2col4    row3col4    heading4  

For most cases, portrait (headings at top) and landscape (headings at left) are all you need.


All methods/procedures accept the same named parameters. Some methods pre-define parameters on your behalf, for convenience. If these parameters are defined, they may be overriden where applicable. You do not necessarily have to specify data, any bare array references will be collected and assigned to data. Just because you can, however, does not mean you should. Everything is meant to be convenient.


Literal parameters provides the means to modify the macro aspects of the table, such as indentation, encoding, data source and table orientation.

  • data

    The data to be rendered into table cells. Should be an array ref of array refs.

      data => [["a".."c"],[1..3],[4..6],[7..9]]
  • file

    String. The name of the data file to read. Supported formats are XLS, CSV, JSON, YAML, HTML, GIF, PNG and JPG.

      file => 'foo.json'

    file overrides data. You generally either specify data or file but not both (unless the file is an image in which the two can be combined).

      data => \@data, file => 'background.png'

    See Spreadsheet::HTML::File::Loader for more on supported file formats. See DBIx::HTML for formatting your SQL database queries.

    If you don't mind having a temp file created on your behalf, you can also supply a URI:

      file => 'http://your.site.com/path/to/foo.json'

    The temp file will be unlinked unless you set the super secret hidden param _unlink to zero (0):

      _unlink => 0
  • block

    Integer. Can be supplied in conjunction with file for image formats (JPEG, PNG and GIF). Defaults to 8. Cannot be less than 1. Representing an image as an HTML table on a pixel by pixel basis tends to produce tables that are too large. This parameter can be used to increase and decrease that size, by reading in blocks of pixels (8x8=64 pixels to be represented by one table cell) and determining the pixel color for that block (see blend below). The smaller the block size the longer the processing time.

      block => 4
  • blend

    Boolean. Default false. Can be supplied in conjuction with block to change the algorithm for determining block's pixel color. When set to true, the pixel color is determined by averaging the colors of all pixels in that block. When false (default), the pixel color is determined by finding the the most used color in the block.

      blend => 1

    Turning blend on tends to work better for photo realism. Keep it off for producing 1980's style pixel art images.

  • worksheet

    Integer. Can be supplied in conjunction with file. If multiple worksheets or data tables (or image frames) are present, use this parameter to select which one (index 1 based). Defaults to 1 (first found).

      worksheet => 3 # the third worksheet, data table or image frame found  
  • preserve

    Boolean. Can be supplied in conjunction with file. Attempts to copy over all formatting styles from original document to table. Styles are not currently deduped, so use with care as the final output will contain a lot of redundant cell styles.

      preserve => 1
  • fill

    String. Can be supplied instead of data to generate empty cells, or in conjunction with data to pad existing cells (currently pads the right and bottom sides only.)

      fill => '5x12'
  • wrap

    Integer. Can be supplied in conjunction with a 1D data to automatically wrap into a 2D array matrix. Can also "rewrap" existed 2D array matrices, but at the expense of likely mangling the headings.

      wrap => 10 
  • apply

    String. Applies formulas parsable by Spreadsheet::Engine to data.

      apply => 'set B6 formula SUM(B2:B5)'

    Accepts lists:

      apply => ['set B6 formula SUM(B2:B5)', 'set C6 formula SUM(C2:C5)']

    Can be used to create total and sub total rows. See Spreadsheet::Engine for more.

  • theta: 0, 90, 180, 270, -90, -180, -270

    Integer. Rotates table clockwise for positive values and counter-clockwise for negative values. Default to 0: headers at top. 90: headers at right. 180: headers at bottom. 270: headers at left. To achieve landscape, use -270.

      theta => -270
  • flip

    Boolean. Flips table horizontally from the perspective of the headings "row" by negating the value of theta.

      flip => 1
  • pinhead

    Boolean. Works in conjunction with theta to ensure reporting readability. Without it, south() and east() would have data cells arranged in reverse order.

      pinhead => 1
  • indent

    String. Render the table with nested indentation. Defaults to undefined which produces no indentation. Adds newlines when set to any value that is defined.

      indent => '    '
      indent => "\t"
  • level

    Integer. Start indentation at this level. Useful for matching nesting styles of original HTML text that you may want to insert into to.

      level => 4

    This value does not say 'use 4 spaces', it applies the repetition operator to the value of indent 4 times.

  • encode

    Boolean. Encode HTML entities. Defaults to false, which produces no encoding. If set to true without further specifying a value for encodes (see below), will encode all control chars, high bit chars and '<', '&', '>', ''' and '"'.

      encode => 1
  • encodes

    String. Set value to those characters you wish to have HTML encoded.

      encodes => '<>"'
  • empty

    String. Replace empty cells with this value. Defaults to &nbsp;. Set value to undef to avoid any substitutions.

      empty => '&#160;'
  • matrix

    Boolean. Render the headings row with only <td> tags, no <th> tags.

      matrix => 1
  • headless

    Boolean. Render the table with without the headings row at all. Any configuration to headings or -r0 will be discarded with the headings row.

      headless => 1
  • tgroups

    Integer. Group table rows into <thead>, <tbody> and <tfoot> sections.

    When tgroups is set to 1, the <tfoot> section is omitted. The last row of the data is found at the end of the <tbody> section instead. (loose)

      tgroups => 1

    When tgroups is set to 2, the <tfoot> section is found in between the <thead> and <tbody> sections. (strict)

      tgroups => 2
  • group

    Integer. Will chunk body rows into tbody groups of size group.

      group => 4

    Currently only accepts integers (not column names).

  • cache

    Boolean. Preserve data after it has been processed (and loaded). Useful for loading data from files only once.

      cache => 1
  • scroll

    Boolean. Scrolls the table cells. See Spreadsheet::HTML::Presets::Scroll.

      scroll => 1
  • headings

    Apply callback subroutine to each cell in headings row.

      headings => sub { join(" ", map {ucfirst lc $_} split "_", shift) }

    Or apply hash reference as attributes:

      headings => { class => 'some-class' }

    Or both via array reference:

      headings => [ sub { uc shift }, { class => "foo" } ]

    Since headings is a natural alias for the dynamic parameter -r0, it could be considered as a dynamic parameter. Be careful not to prepend a dash to headings ... only dynamic parameters use leading dashes.

  • sorted_attrs

    Boolean. This is useful for ensuring that attributes within tags are rendered in alphabetical order, for consistancy. You will most likely never need this feature.



Dynamic parameters provide a means to control the micro elements of the table, such as modifying headings by their name and rows and columns by their indices (X). They contain leading dashes to seperate them from literal and tag parameters.

  • -rX

    Apply this callback subroutine to all cells in row X. (0 index based)

      -r3 => sub { uc shift }

    Or apply hash ref as attributes:

      -r3 => { class => 'some-class' }

    Or both:

      -r3 => [ sub { uc shift }, { class => "foo" } ]
  • -cX

    Apply this callback to all cells in column X. (0 index based)

      -c4 => sub { sprintf "%02d", shift || 0 }

    Or apply hash ref as attributes:

      -c4 => { class => 'some-class' }

    Or both:

      -c4 => [ sub { uc shift }, { class => "foo" } ]

    You can alias any column number by the value of the heading name in that column:

      -occupation => sub { "<b>$_[0]"</b>" }
      -salary => { class => 'special-row' }
      -employee_no => [ sub { sprintf "%08d", shift }, { class => "foo" } ]
  • -rXcX

    Apply this callback or hash ref of attributres to the cell at row X and column X. (0 index based)

      -r3c4 => { class => 'special-cell' }


Tag parameters provide a means to control the attributes of the table's tags, and in the case of <th> and <td> the contents via callback subroutines. Although similar in form, they are differentiated from litertal parameters because they share the names of the actual HTML table tags.

  • table

    Hash ref. Apply these attributes to the specified tag.

      table => { border => 1 }
  • thead

    Hash ref. Apply these attributes to the specified tag.

      thead => { class => 'heading' }
  • tfoot

    Hash ref. Apply these attributes to the specified tag.

      tfoot => { class => 'footing' }
  • tbody

    Hash ref. Apply these attributes to the specified tag.

      tbody => { class => 'body' }
  • tr

    Hash ref. Apply these attributes to the specified tag.

      tr => { style => { background => [qw( color1 color2 )]' } }

    Does not apply to <tr> groups found within <thead> or <tfoot>. (See thead.tr and tfoot.tr below.)

  • th

    Hash ref, sub ref or array ref containing either.

      th => { class => 'heading' }
      th => sub { uc shift }
      th => [ sub { uc shift }, { class => 'heading' } ]
  • td

    Hash ref, sub ref or array ref containing either.

      td => { class => 'cell' }
      td => sub { uc shift }
      td => [ sub { uc shift }, { class => 'cell' } ]
  • caption

    Caption is special in that you can either pass a string to be used as CDATA or a hash whose only key is the string to be used as CDATA.

      caption => "Just Another Title"
      caption => { "A Title With Attributes" => { align => "bottom" } }
  • colgroup

    Add colgroup tag(s) to the table. Use an AoH for multiple.

      colgroup => { span => 2, style => { 'background-color' => 'orange' } }
      colgroup => [ { span => 20 }, { span => 1, class => 'end' } ]
  • col

    Add col tag(s) to the table. Use an AoH for multiple. Wraps tags within a colgroup tag. Same usage as colgroup.

      col => { span => 2, style => { 'background-color' => 'orange' } }
      col => [ { span => 20 }, { span => 1, class => 'end' } ]
  • thead.tr

    When tgroups is 1 or 2, this tag parameter is available to control the attributes of the <tr> tag within the <thead> group.

      'tr.head' => { class => 'heading-row' }
  • tfoot.tr

    When tgroups is 2, this tag parameter is available to control the attributes of the <tr> tag within the <tfoot> group.

      'tr.foot' => { class => 'footing-row' }


The following preset methods are availble for creating tables that can be used with little to no additional coding. All preset methods accept all of the above mentioned parameters (%params) in addition to those specific to themselves.

  • layout( %params )

    Generate layout tables.

  • list( ordered, col, row, %params )

    Generate <ol> and <ul> lists.

  • select( col, row, values, selected, placeholder, optgroup, label, %params )

    Generate <select> form elements.

  • handson( args, jquery, handsonjs, css, %params )

    Generate Handsontable tables. (Excel like interface for browsers.)

  • checkerboard( colors, %params )

    Generate checkerboard patterns in cell backgrounds.

  • banner( on, off, text, font, dir, emboss, %params )

    Generate banners via Text::FIGlet.

  • scroll( fgdirection, fx, fy, bgdirection, bx, by, interval, jquery, %params )

    Scroll table cell foregrounds and backgrounds.

  • calendar( month, year, today, -day, %params )

    Generate calendars.

  • calculator( jquery, %params )

    Generate a simple HTML table calculator.

  • beadwork( preset, art, map, bgcolor, %params )

    Turn cell backgrounds into 8-bit pixel art.

  • conway( on, off, colors, fade, interval, jquery, %params )

    Turn cell backgrounds into Conway's game of life.

  • sudoku( blanks, attempts, jquery, %params )

    Generate 9x9 HTML table sudoku boards.

  • maze( on, off, %params )

    Generates a static maze.

  • tictactoe( jquery, %params )

    Creates a playable Tic-Tac-Toe game board.

  • checkers( on, off, jquery, %params )

    Creates a NON playable Checkers game board.

  • chess( on, off, jquery, jqueryui, %params )

    Creates a NON playable Chess game board.

See Spreadsheet::HTML::Presets for more documentation (and source code for usage examples).



The following is used to apply formulas to data:

The following are used to load data from various different file formats:

The following are used by some presets to enhance output, if installed:



Please report any bugs or feature requests to either

I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.


You can find documentation for this module with the perldoc command.

    perldoc Spreadsheet::HTML

The Github project is https://github.com/jeffa/Spreadsheet-HTML

You can also look for information at:


Thank you very much! :)

  • Neil Bowers

    Helped with Makefile.PL suggestions and corrections.

  • Math::Matrix

    Implementation of 2D array transposition.


Jeff Anderson, <jeffa at cpan.org>


Copyright 2017 Jeff Anderson.

This program is free software; you can redistribute it and/or modify it under the terms of the the Artistic License (2.0). You may obtain a copy of the full license at:


Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license.

If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license.

This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder.

This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed.