Annotation of loncom/xml/lontable.pm, revision 1.4
1.1 foxr 1: # The LearningOnline Network with CAPA
2: # Generating TeX tables.
3: #
1.4 ! foxr 4: # $Id: lontable.pm,v 1.3 2008/12/01 12:25:06 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:
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)) {
332: $self->{inner_border} = $new_value;
333: }
1.4 ! 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.4 ! foxr 356: $self->{caption} = $new_value;
1.2 foxr 357: }
358:
1.4 ! 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.4 ! foxr 381: $self->{theme} = $new_value;
1.2 foxr 382: }
1.4 ! 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 {
415: my ($self, %config) = @_;
416:
1.4 ! foxr 417: if ($self->{row_open}) {
! 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:
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:
1.4 ! foxr 437: $self->{row_open} = 1; # Row is now open and ready for business.
1.2 foxr 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:
1.4 ! foxr 448: $table->end_row();
1.2 foxr 449:
450:
451: =cut
452:
1.4 ! foxr 453: sub end_row {
1.2 foxr 454: my ($self) = @_;
455:
1.4 ! foxr 456: if ($self->{row_open}) {
1.2 foxr 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:
1.4 ! foxr 476: $self->{row_open} = 0;;
1.2 foxr 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.4 ! foxr 512: if (!$self->{row_open}) {
1.3 foxr 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:
1.4 ! foxr 571: if (!$self->{row_open}) {
1.3 foxr 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.4 ! foxr 616: # The following method allows for testability.
! 617:
! 618:
! 619: sub get_object_attribute {
! 620: my ($self, $attribute) = @_;
! 621: return $self->{$attribute};
! 622: }
! 623:
1.1 foxr 624:
625: # Mandatory initialization.
1.4 ! foxr 626: BEGIN{
! 627: }
1.1 foxr 628:
629: 1;
630: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>