Annotation of loncom/xml/lontable.pm, revision 1.3

1.1       foxr        1: # The LearningOnline Network with CAPA
                      2: #  Generating TeX tables.
                      3: #
1.3     ! foxr        4: # $Id: lontable.pm,v 1.2 2008/11/25 12:27:34 foxr Exp $
1.1       foxr        5: # 
                      6: #
                      7: # Copyright Michigan State University Board of Trustees
                      8: #
                      9: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                     10: #
                     11: # LON-CAPA is free software; you can redistribute it and/or modify
                     12: # it under the terms of the GNU General Public License as published by
                     13: # the Free Software Foundation; either version 2 of the License, or
                     14: # (at your option) any later version.
                     15: #
                     16: # LON-CAPA is distributed in the hope that it will be useful,
                     17: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     18: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     19: # GNU General Public License for more details.
                     20: #
                     21: # You should have received a copy of the GNU General Public License
                     22: # along with LON-CAPA; if not, write to the Free Software
                     23: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     24: #
                     25: # /home/httpd/html/adm/gpl.txt
                     26: #
                     27: # http://www.lon-capa.org/
                     28: ## Copyright for TtHfunc and TtMfunc by Ian Hutchinson. 
                     29: # TtHfunc and TtMfunc (the "Code") may be compiled and linked into 
                     30: # binary executable programs or libraries distributed by the 
                     31: # Michigan State University (the "Licensee"), but any binaries so 
                     32: # distributed are hereby licensed only for use in the context
                     33: # of a program or computational system for which the Licensee is the 
                     34: # primary author or distributor, and which performs substantial 
                     35: # additional tasks beyond the translation of (La)TeX into HTML.
                     36: # The C source of the Code may not be distributed by the Licensee
                     37: # to any other parties under any circumstances.
                     38: #
                     39: 
                     40: # This module is a support packkage that helps londefdef generate
                     41: # LaTeX tables using the LaTeX::Table package.  A prerequisite is that
                     42: # the print generator must have added the following to the LaTeX header:
                     43: #
                     44: #  \usepackage{xtab}
                     45: #  \usepackage{booktabs}
                     46: #  \usepackage{array}
                     47: #  \usepackage{colortbl}
                     48: #  \usepackage{xcolor}
                     49: #
                     50: #  These packages are installed in the packaged LaTeX distributions we know of as of
                     51: #  11/24/2008
                     52: #
                     53: 
                     54: 
                     55: 
                     56: package Apache::lontable;
                     57: use strict;
                     58: use LaTeX::Table;
                     59: 
                     60: 
                     61: =pod
                     62: 
                     63: =head1  lontable Table generation assistant for the LaTeX target
                     64: 
                     65: This module contains support software for generating tables in LaTeX output mode 
                     66: In this implementation, we use the LaTeX::Table package to do the actual final formatting.
                     67: Each table creates a new object.  Table objects can have global properties configured.
                     68: The main operations on a table object are:
                     69: 
                     70: =over 3
                     71: 
                     72: =item start_row  
                     73: 
                     74: Opens a new table row.
                     75: 
                     76: =item end_row
                     77: 
                     78: Closes a table row.
                     79: 
                     80: =item configure_row
                     81: 
                     82: Modifies a configuration item in the currently open row.
                     83: 
                     84: =item generate
                     85: 
                     86: Returns the generated table string.
                     87: 
                     88: =item configure
                     89: 
                     90: Configures a table's global configuration.
                     91: 
1.3     ! foxr       92: =item add_cell
        !            93: 
        !            94: Add and configure a cell to the current row.6
        !            95: 
1.1       foxr       96: =back
                     97: 
                     98: =cut
                     99: 
                    100: =pod
                    101: 
                    102: =head2 new - create a new object.
                    103: 
                    104: Create a new table object.  Any of the raw table configuration items can be
                    105: modified by this.  These configuration items include:
                    106: 
                    107:   my $table = lontable::new(\%config_hash)
                    108: 
                    109: =over3
                    110: 
                    111: =item alignment
                    112: 
                    113: Table alignment.  Some table styles support this but not all.
                    114: 
                    115: =item tableborder
                    116: 
                    117: If true, a border is drawn around the table.
                    118: 
                    119: =item cellborder
                    120: 
                    121: If true, borders are drawn around the cells inside a table.
                    122: 
                    123: =item caption
                    124: 
                    125: The table caption text.
                    126: 
                    127: =item theme
                    128: 
                    129: The theme of the table to use.  Defaults to Zurich.  Themes we know about are:
                    130: NYC, NYC2, Zurich, Berlin, Dresden, Houston, Miami, plain, Paris.  Other themes can be added
                    131: to the LaTeX::Table package, and they will become supported automatically, as theme names are
                    132: not error checked.  Any use of a non-existent theme is reported by the LaTeX::Table package
                    133: when the table text is generated.
                    134: 
                    135: =back
                    136: 
                    137: =head3 Member data
                    138: 
                    139: The object hash has the following members:
                    140: 
                    141: =over 3
                    142: 
                    143: =item column_count 
                    144: 
                    145: Maintained internally, the number of colums in the widest row.
                    146: 
                    147: =item alignment
                    148: 
                    149: Table alignment (configurable) "left", "center", or "right".
                    150: 
                    151: =item outer_border
                    152: 
                    153: True if a border should be drawn around the entire table (configurable)
                    154: 
                    155: =item inner_borders
                    156: 
                    157: True if a border should be drawn around all cells (configurable).
                    158: 
                    159: =item caption
                    160: 
                    161: Table caption (configurable).
                    162: 
                    163: =item theme
                    164: 
                    165: Theme desired (configurable).
                    166: 
                    167: =item row_open 
                    168: 
                    169: True if a row is open and not yet closed.
                    170: 
                    171: =item rows
                    172: 
                    173: Array of row data. This is an array of hashes described below.
                    174: 
                    175: =back
                    176: 
                    177: =head3 Row data.
                    178: 
                    179: Each row of table data is an element of the rows hash array.  Hash elements are
                    180: 
                    181: =over 3
                    182: 
                    183: 
                    184: =item default_halign 
1.3     ! foxr      185: 0
1.1       foxr      186: Default horizontal alignment for cells in this row.
                    187: 
                    188: =item default_valign
                    189: 
                    190: Default vertical alignment for cells in this row (may be ignored).
                    191: 
                    192: =item cells
                    193: 
                    194: Array of hashes where each element represents the data for a cell.
                    195: The contents of each element of this hash are described below:
                    196: 
                    197: =over 3
                    198: 
1.3     ! foxr      199: =item header
        !           200: 
        !           201: If present, the row is a 'header' that is it was made via the
        !           202: <th> tag.
        !           203: 
1.1       foxr      204: =item halign
                    205: 
                    206: If present, overrides the row default horizontal alignment.
                    207: 
                    208: =item valign
                    209: 
                    210: if present, override the row default vertical alignment.
                    211: 
                    212: =item rowspan
                    213: 
                    214: If present, indicates the number of rows this cell spans.
                    215: 
                    216: =item colspan
                    217: 
                    218: If present indicates the number of columns this cell spans.
                    219: Note that a cell can span both rows and columns.
                    220: 
                    221: =item contents
                    222: 
                    223: The contents of the cell.
                    224: 
                    225: =back
                    226: 
                    227: =back
                    228: 
                    229: =cut
                    230: 
                    231: sub new {
                    232:     my ($class, $configuration) = @_;
                    233: 
                    234:     #  Initialize the object member data with the default values
                    235:     #  then override with any stuff in $configuration.
                    236: 
                    237:     my $self = {
                    238: 	alignment      => "left",
                    239: 	outer_border   => 0,
1.2       foxr      240: 	inner_border  => 0,
1.1       foxr      241: 	caption        => "",
                    242: 	theme          => "Zurich",
                    243: 	column_count   => 0,
                    244: 	row_open       => 0,
                    245: 	rows           => [],
                    246:     };
                    247: 
                    248:     foreach my $key (keys %$configuration) {
                    249: 	$self->{$key} = $$configuration{$key};
                    250:     }
                    251: 
                    252:     bless($self, $class);
                    253: 
                    254:     return $self;
                    255: }
                    256: 
1.3     ! foxr      257: 
1.1       foxr      258: #-------------------------------------------------------------------------
                    259: #
                    260: #  Methods that get/set table global configuration.
1.2       foxr      261: #
                    262: 
                    263: =pod
                    264: 
                    265: =head2 Gets/set alignment.  
                    266: 
                    267: If the method is passed a new alignment value, that replaces the current one.
                    268: Regardless, the current alignment is used:
                    269: 
                    270: =head3 Examples:
                    271: 
                    272:  my $align = $table->alignment(); # Return current alignment
                    273:  $table->alignment("center");     # Attempt centered alignment.
                    274: 
                    275: =cut
                    276: 
                    277: sub alignment {
                    278:     my ($self, $new_value) = @_;
                    279: 
                    280:     if (defined($new_value)) {
                    281: 	$self->{alignment} = $new_value;
                    282:     }
                    283:     return $self->{alignment};
                    284: }
                    285: 
                    286: =pod
                    287: 
                    288: =head2 table_border
                    289: 
                    290: Set or get the presence of an outer border in the table.
                    291: If passed a parameter, that parameter replaces the current request
                    292: for or not for an outer border. Regardless, the function returns
                    293: the final value of the outer_border request.
                    294: 
                    295: =head3 Examples:
                    296: 
                    297:   $table->table_border(1);      # Request an outer border.
                    298:   my $outer_requested = $table->table_border();
                    299: 
                    300: =cut
                    301: 
                    302: sub table_border {
                    303:     my ($self, $new_value) = @_;
                    304: 
                    305:     if (defined($new_value)) {
                    306: 	$self->{outer_border} = $new_value;
                    307:     }
                    308:     return $self->{outer_border};
                    309: }
                    310: 
                    311: 
                    312: =pod
                    313: 
                    314: =head2 cell_border
                    315: 
                    316: Set or get the presence of a request for cells to have borders
                    317: drawn around them.  If a paramter is passed, it will be treated as
                    318: a new value for the cell border configuration.  Regardless,the final
                    319: value of that configuration parameter is returned.
                    320: 
                    321: =head3 Examples:
                    322: 
                    323:  my $cell_borders = $table->cell_border(); # ask if cell borders are requested.
                    324:  $table->cell_border(1);	# Request cell borders.
                    325: 
                    326: =cut
                    327: 
                    328: sub cell_borders {
                    329:     my ($self, $new_value) = @_;
                    330: 
                    331:     if (defined($new_value)) {
                    332: 	$self->{inner_border} = $new_value;
                    333:     }
                    334:     reurn $self->{inner_border};
                    335: }
                    336: 
                    337: =pod
                    338: 
                    339: =head2 caption
                    340: 
                    341: Gets and/or sets the caption string for the table.  The caption string appears to label
                    342: the table.  If a parameter is supplied it will become the new caption string.k
                    343: 
                    344: =head3 Examples:
                    345: 
                    346: 
                    347:   $my caption = $table->caption();
                    348:   $table->caption("This is the new table caption");
                    349: 
                    350: =cut
                    351: 
                    352: sub caption {
                    353:     my ($self, $new_value) = @_;
                    354: 
                    355:     if (defined($new_value)) {
                    356: 	$self->catpion = $new_value;
                    357:     }
                    358: 
                    359:     return $self->caption;
                    360: }
                    361: 
                    362: =pod
                    363: 
                    364: =head2 theme
                    365: 
                    366: Gets and optionally sets the table theme.  The table theme describes how the
                    367: table will be typset by the table package.  If a parameter is supplied it
                    368: will be the new theme selection.
                    369: 
                    370: =head3 Examples:
1.1       foxr      371: 
1.2       foxr      372:   my $theme = $table->theme();
                    373:   $table->theme("Dresden");
                    374: 
                    375: =cut
                    376: 
                    377: sub theme {
                    378:     my ($self, $new_value) = @_;
                    379: 
                    380:     if (defined($new_value)) {
                    381: 	$self->theme = $new_value;
                    382:     }
                    383:     return $self->theme;
                    384: }
                    385: 
                    386: =pod
                    387: 
                    388: =head2 start_row
                    389: 
                    390: Begins a new row in the table.  If a row is already open, that row is
                    391: closed off prior to starting the new row.  Rows can have the following attributes
                    392: which are specified by an optional hash passed in to this function.
                    393: 
                    394: =over 3
                    395: 
                    396: =item default_halign
                    397: 
                    398: The default horizontal alignment of the row. This can be "left", "center", or "right"
                    399: 
                    400: =item default_valign
                    401: 
                    402: The default vertical alignment of the row.  This can be "top", "center", or "bottom"
                    403: 
                    404: =back
                    405: 
                    406: =head3 Examples:
                    407: 
                    408:   $table_start_row();                  # no attributes.
                    409:   $table_start({default_halign => "center",
                    410:                 default_valign => "bottom"}); # Create setting the attrbutes.
                    411: 
                    412: =cut
                    413: 
                    414: sub start_row {
                    415:     my ($self, %config) = @_;
                    416: 
                    417:     if ($self->row_open) { 
                    418: 	$self->end_row;
                    419:     }
                    420:     my $row_hash = {
                    421: 	default_halign => "left",
                    422: 	default_valign => "top",
                    423: 	cells          => []
                    424:     };
                    425: 
                    426:     # Override the defaults if the config hash is present:
                    427: 
                    428:     if (defined(%config)) {
                    429: 	foreach my $key  (keys %config) {
                    430: 	    $row_hash->{$key} = $config{$key};
                    431: 	}
                    432:     }
                    433:     
                    434:     my $rows = $self->{rows};
                    435:     push(@$rows, $row_hash);
                    436: 
                    437:     $self->row_open = 1;	# Row is now open and ready for business.
                    438: }
                    439: 
                    440: =pod
                    441: 
                    442: =head2  end_row 
                    443: 
                    444: Closes off a row.  Once closed, cells cannot be added to this row again.
                    445: 
                    446: =head3 Examples:
                    447: 
                    448:    $table->close_row();
                    449: 
                    450: 
                    451: =cut
                    452: 
                    453: sub close_row {
                    454:     my ($self) = @_;
                    455: 
                    456:     if ($self->row_open) {
                    457: 	
                    458: 	# Mostly we need to determine if this row has the maximum
                    459: 	# cell count of any row in existence in the table:
                    460: 
                    461: 	my $row        = $self->{rows}[-1];
                    462: 	my $cells      = $row->{cells};
1.3     ! foxr      463: 	my $raw_cell_count = scalar(@$cells);
        !           464: 
        !           465: 	# Need to iterate through the columns as 
        !           466: 	# colspans affect the count:
        !           467: 	#
        !           468: 	my $cell_count = 0;
        !           469: 	for (my $i =0; $i < $raw_cell_count; $i++) {
        !           470: 	    $cell_count = $cell_count + $cells->[$i]->{colspan};
        !           471: 	}
1.2       foxr      472: 	if ($cell_count > $self->{column_count}) {
                    473: 	    $self->{column_count} = $cell_count;
                    474: 	}
                    475: 
                    476: 	$self->row_closed;
                    477:     }
                    478: }
                    479: 
                    480: =pod
                    481: 
1.3     ! foxr      482: =head2 configure_row
        !           483: 
        !           484: Modify the configuration of a row.   If a row is not open, a new one will be opened.
        !           485: 
        !           486: =head3 Parameters:
1.2       foxr      487: 
1.3     ! foxr      488: config_hash - A hash that contains new values for the set of row confiuguration 
        !           489: items to be modified.  There is currently no check/penalty for items that are not in
        !           490: the set of defined configuration properties which are:
1.2       foxr      491: 
1.3     ! foxr      492: =over 2
        !           493: 
        !           494: =item default_halign
        !           495: 
        !           496: The default horizontal alignment for text in  cells in the row.  This can be any of:
        !           497: "left", "right" or "center".
        !           498: 
        !           499: =item default_valign
        !           500: 
        !           501: The default vertical alignment for text in cells in the row.  This can be any of:
        !           502: 
        !           503: "top", "bottom" or "center"
        !           504: 
        !           505: =back 
1.2       foxr      506: 
                    507: =cut
                    508: 
1.3     ! foxr      509: sub configure_row {
        !           510:     my ($self, $config) = @_;
1.2       foxr      511: 
1.3     ! foxr      512:     if (!$self->row_open) {
        !           513: 	$self->start_row();
        !           514:     }
        !           515:     
        !           516:     my $row = $self->{rows}[-1];
        !           517:     foreach my $config_item (keys %$config) {
        !           518: 	$row->{$config_item} = $config->{$config_item};
        !           519:     }
1.2       foxr      520: }
1.1       foxr      521: 
                    522: 
1.3     ! foxr      523: =pod
        !           524: 
        !           525: =head2 add_cell
        !           526: 
        !           527: Add a new cell to a row.  If there is a row above us, we need to 
        !           528: watch out for row spans that may force additional blank cell entries
        !           529: to fill in the span. 
        !           530: 
        !           531: =head3 Parameters:
        !           532: 
        !           533: =over 2
        !           534: 
        !           535: =item text
        !           536: 
        !           537: Text to put in the cell.
        !           538: 
        !           539: =item cell_config
        !           540: 
        !           541: Hash of configuration options that override the defaults.   The recognized options,
        !           542: and their defaults are:
        !           543: 
        !           544: =over 2
        !           545: 
        !           546: =item halign 
        !           547: 
        !           548: If nonblank overrides the row's default for the cell's horizontal alignment.
        !           549: 
        !           550: =item valign
        !           551: 
        !           552: If nonblank, overrides the row's default for the cdell's vertical alignment.
        !           553: 
        !           554: =item rowspan
        !           555: 
        !           556: Number of rows the cell spans.
        !           557: 
        !           558: =item colspan
        !           559: 
        !           560: Number of columns the cell spans.
        !           561: 
        !           562: =back
        !           563: 
        !           564: =cut
        !           565: 
        !           566: sub add_cell {
        !           567:     my ($self, $text, $config) = @_;
        !           568: 
        !           569:     # If a row is not open, we must open it:
        !           570: 
        !           571:     if (!$self->row_open) {
        !           572: 	$self->start_row();
        !           573:     }
        !           574: 
        !           575:     my $current_row   = $self->{rows}->[-1];
        !           576:     my $current_cells = $current_row->{cells}; 
        !           577: 
        !           578:     # The way we handle row spans is to insert additional
        !           579:     # blank cells as needed to reach this column.  Each
        !           580:     # cell that is inserted is empty, but has a row span decreased by one
        !           581:     # from the row above.  Column spans are propagated down from the row above
        !           582:     # and handled when the table's LaTeX is generated.
        !           583:     # There must be at least two rows in the row table to need to do this:
        !           584: 
        !           585:     my $row_count = scalar(@$self->{rows});
        !           586:     if ($row_count > 1) {
        !           587: 	my $prior_row      = $self->{rows}->[-2];
        !           588: 	my $curr_colcount  = scaler(@$current_row->{cells});
        !           589: 	my $prior_colcount = scaler(@$prior_row->{cells});
        !           590: 
        !           591: 	while (($curr_colcount < $prior_colcount) &&
        !           592: 	       $prior_row->{cells}->[$curr_colcount]->{rowspan} > 1) {
        !           593: 	    my %cell = $prior_row->{cells}->[$curr_colcount];
        !           594: 	    %cell->{rowspan}--;
        !           595: 	    %cell->{contents} = "";
        !           596: 	    push(@$current_cells, \%cell);
        !           597: 	}
        !           598:     }
        !           599:     #
        !           600:     # Now we're ready to build up our cell:
        !           601: 
        !           602:     my $cell = {
        !           603: 	rowspan    => 1,
        !           604: 	colspan    => 1,
        !           605: 	contents   => $text
        !           606:     };
        !           607:     
        !           608:     if (defined($config)) {
        !           609: 	foreach my $key (keys(%$config)) {
        !           610: 	    $cell->{$key} = $config->{$key};
        !           611: 	}
        !           612:     }
        !           613:     push(@$current_cells, $cell);
        !           614: }
        !           615: 
1.1       foxr      616: 
                    617: #   Mandatory initialization.
                    618: 
                    619: 1;
                    620: __END__

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>