Annotation of loncom/xml/lontable.pm, revision 1.5
1.1 foxr 1: # The LearningOnline Network with CAPA
2: # Generating TeX tables.
3: #
1.5 ! foxr 4: # $Id: lontable.pm,v 1.4 2008/12/02 11:57:25 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
1.5 ! foxr 42: # the print generator must have added the following to the LaTeX
1.1 foxr 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)) {
1.5 ! foxr 281: $self->{'alignment'} = $new_value;
1.2 foxr 282: }
1.5 ! foxr 283: return $self->{'alignment'};
1.2 foxr 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)) {
1.5 ! foxr 306: $self->{'outer_border'} = $new_value;
1.2 foxr 307: }
1.5 ! foxr 308: return $self->{'outer_border'};
1.2 foxr 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:
1.4 foxr 323: my $cell_border = $table->cell_border(); # ask if cell borders are requested.
1.2 foxr 324: $table->cell_border(1); # Request cell borders.
325:
326: =cut
327:
1.4 foxr 328: sub cell_border {
1.2 foxr 329: my ($self, $new_value) = @_;
330:
331: if (defined($new_value)) {
1.5 ! foxr 332: $self->{'inner_border'} = $new_value;
1.2 foxr 333: }
1.5 ! foxr 334: return $self->{'inner_border'};
1.2 foxr 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)) {
1.5 ! foxr 356: $self->{'caption'} = $new_value;
1.2 foxr 357: }
358:
1.5 ! foxr 359: return $self->{'caption'};
1.2 foxr 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)) {
1.5 ! foxr 381: $self->{'theme'} = $new_value;
1.2 foxr 382: }
1.5 ! foxr 383: return $self->{'theme'};
1.2 foxr 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 {
1.5 ! foxr 415: my ($self, $config) = @_;
1.2 foxr 416:
1.5 ! foxr 417: if ($self->{'row_open'}) {
1.4 foxr 418: $self->end_row();
1.2 foxr 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:
1.5 ! foxr 428: if (defined($config)) {
! 429: foreach my $key (keys %$config) {
! 430: $row_hash->{$key} = $config->{$key};
1.2 foxr 431: }
432: }
1.5 ! foxr 433:
1.2 foxr 434:
1.5 ! foxr 435: my $rows = $self->{'rows'};
1.2 foxr 436: push(@$rows, $row_hash);
437:
1.5 ! foxr 438: $self->{"row_open"} = 1; # Row is now open and ready for business.
1.2 foxr 439: }
440:
441: =pod
442:
443: =head2 end_row
444:
445: Closes off a row. Once closed, cells cannot be added to this row again.
446:
447: =head3 Examples:
448:
1.4 foxr 449: $table->end_row();
1.2 foxr 450:
451:
452: =cut
453:
1.4 foxr 454: sub end_row {
1.2 foxr 455: my ($self) = @_;
456:
1.5 ! foxr 457: if ($self->{'row_open'}) {
1.2 foxr 458:
459: # Mostly we need to determine if this row has the maximum
460: # cell count of any row in existence in the table:
461:
1.5 ! foxr 462: my $row = $self->{'rows'}[-1];
! 463: my $cells = $row->{'cells'};
1.3 foxr 464: my $raw_cell_count = scalar(@$cells);
465:
466: # Need to iterate through the columns as
467: # colspans affect the count:
468: #
469: my $cell_count = 0;
470: for (my $i =0; $i < $raw_cell_count; $i++) {
1.5 ! foxr 471: $cell_count = $cell_count + $cells->[$i]->{'colspan'};
1.3 foxr 472: }
1.5 ! foxr 473: if ($cell_count > $self->{'column_count'}) {
! 474: $self->{'column_count'} = $cell_count;
1.2 foxr 475: }
476:
1.5 ! foxr 477: $self->{'row_open'} = 0;;
1.2 foxr 478: }
479: }
480:
481: =pod
482:
1.3 foxr 483: =head2 configure_row
484:
485: Modify the configuration of a row. If a row is not open, a new one will be opened.
486:
487: =head3 Parameters:
1.2 foxr 488:
1.3 foxr 489: config_hash - A hash that contains new values for the set of row confiuguration
490: items to be modified. There is currently no check/penalty for items that are not in
491: the set of defined configuration properties which are:
1.2 foxr 492:
1.3 foxr 493: =over 2
494:
495: =item default_halign
496:
497: The default horizontal alignment for text in cells in the row. This can be any of:
498: "left", "right" or "center".
499:
500: =item default_valign
501:
502: The default vertical alignment for text in cells in the row. This can be any of:
503:
504: "top", "bottom" or "center"
505:
506: =back
1.2 foxr 507:
508: =cut
509:
1.3 foxr 510: sub configure_row {
511: my ($self, $config) = @_;
1.2 foxr 512:
1.5 ! foxr 513: if (!$self->{'row_open'}) {
1.3 foxr 514: $self->start_row();
515: }
516:
1.5 ! foxr 517: my $row = $self->{'rows'}[-1];
1.3 foxr 518: foreach my $config_item (keys %$config) {
519: $row->{$config_item} = $config->{$config_item};
520: }
1.2 foxr 521: }
1.1 foxr 522:
523:
1.3 foxr 524: =pod
525:
526: =head2 add_cell
527:
528: Add a new cell to a row. If there is a row above us, we need to
529: watch out for row spans that may force additional blank cell entries
530: to fill in the span.
531:
532: =head3 Parameters:
533:
534: =over 2
535:
536: =item text
537:
538: Text to put in the cell.
539:
540: =item cell_config
541:
542: Hash of configuration options that override the defaults. The recognized options,
543: and their defaults are:
544:
545: =over 2
546:
547: =item halign
548:
549: If nonblank overrides the row's default for the cell's horizontal alignment.
550:
551: =item valign
552:
553: If nonblank, overrides the row's default for the cdell's vertical alignment.
554:
555: =item rowspan
556:
557: Number of rows the cell spans.
558:
559: =item colspan
560:
561: Number of columns the cell spans.
562:
563: =back
564:
565: =cut
566:
567: sub add_cell {
568: my ($self, $text, $config) = @_;
569:
570: # If a row is not open, we must open it:
571:
1.5 ! foxr 572: if (!$self->{'row_open'}) {
1.3 foxr 573: $self->start_row();
574: }
575:
1.5 ! foxr 576: my $current_row = $self->{'rows'}->[-1];
! 577: my $current_cells = $current_row->{'cells'};
1.3 foxr 578:
579: # The way we handle row spans is to insert additional
580: # blank cells as needed to reach this column. Each
581: # cell that is inserted is empty, but has a row span decreased by one
582: # from the row above. Column spans are propagated down from the row above
583: # and handled when the table's LaTeX is generated.
584: # There must be at least two rows in the row table to need to do this:
585:
1.5 ! foxr 586: my $rows = $self->{'rows'};
! 587: my $row_count = scalar(@$rows);
1.3 foxr 588: if ($row_count > 1) {
1.5 ! foxr 589: my $prior_row = $rows->[-2];
! 590: my $cells = $current_row->{'cells'};
! 591: my $prior_cells = $prior_row->{'cells'};
! 592: my $curr_colcount = scalar(@$cells);
! 593:
! 594: my $prior_colcount = scalar(@$prior_cells);
1.3 foxr 595:
596: while (($curr_colcount < $prior_colcount) &&
1.5 ! foxr 597: $prior_cells->[$curr_colcount]->{'rowspan'} > 1) {
! 598: my %cell;
! 599: my $prior_cell = $prior_cells->[$curr_colcount];
! 600: %cell = %$prior_cell;
! 601: $cell{'rowspan'}--;
! 602: $cell{'contents'} = "";
1.3 foxr 603: push(@$current_cells, \%cell);
1.5 ! foxr 604: $curr_colcount += $prior_cells->[$curr_colcount]->{'colspan'}; # could be a colspan too.
1.3 foxr 605: }
606: }
607: #
608: # Now we're ready to build up our cell:
609:
610: my $cell = {
611: rowspan => 1,
612: colspan => 1,
613: contents => $text
614: };
615:
616: if (defined($config)) {
617: foreach my $key (keys(%$config)) {
618: $cell->{$key} = $config->{$key};
619: }
620: }
621: push(@$current_cells, $cell);
622: }
623:
1.5 ! foxr 624: # The following methods allow for testability.
1.4 foxr 625:
626:
627: sub get_object_attribute {
628: my ($self, $attribute) = @_;
629: return $self->{$attribute};
630: }
631:
1.5 ! foxr 632: sub get_row {
! 633: my ($self, $row) = @_;
! 634: my $rows = $self->{'rows'}; # ref to an array....
! 635: return $rows->[$row]; # ref to the row hash for the selected row.
! 636: }
1.1 foxr 637: # Mandatory initialization.
1.4 foxr 638: BEGIN{
639: }
1.1 foxr 640:
641: 1;
642: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>