]> git.lyx.org Git - features.git/blob - lib/reLyX/RelyxTable.pm
*** empty log message ***
[features.git] / lib / reLyX / RelyxTable.pm
1 # This file is part of reLyX
2 # Copyright (c) 1998-9 Amir Karger karger@post.harvard.edu
3 # You are free to use and modify this code under the terms of
4 # the GNU General Public Licence version 2 or later.
5
6 package RelyxTable;
7
8 # This is a package to read LaTeX tables and print out LyX tables
9
10
11 # We declare here the sub-packages found in this package.
12 # This allows the parser to understand "indirect object" form of subroutines
13 {
14 package RelyxTable::Table;
15 package RelyxTable::Column;
16 package RelyxTable::Row;
17 }
18
19 use strict;
20
21 # Variables used by other packages
22 use vars qw(@table_array $TableBeginString $TableEndString);
23 # @table_array is the list of all arrays
24 # $TableBeginString is a string to write during one pass so that a later
25 #     pass knows to put the table info there
26 # $TableEndString is written at the end of the table so that we know
27 #     the table is done
28 $TableBeginString = '%%%%%Insert reLyX table here!';
29 $TableEndString =   '%%%%%End of reLyX table!';
30
31 # Debugging on?
32 my $debug_on;
33
34 # Are we currently inside a table?
35 # If we are, return the table
36 sub in_table {
37     return "" unless defined(@table_array); # no tables exist
38     my $thistable = $table_array[-1];
39     if ($thistable->{"active"}) {
40         return (bless $thistable, "RelyxTable::Table");
41     } else {
42         return "";
43     }
44 }
45
46
47 # Global variables###############
48 # LyX' enums corresponding to table alignments
49 my %TableAlignments = ("l" => 2, "r" => 4, "c" => 8);
50 # LyX' enums corresponding to multicol types
51 #    normal (non-multicol) cell, beginning of a multicol, part of a multicol
52 my %MulticolumnTypes = ("normal" => 0, "begin" => 1, "part" => 2);
53
54 # Subroutines used by tables and rows, e.g.
55 sub parse_cols {
56 # parse a table's columns' description
57 # Returns an array where each element is one column description
58 # arg0 is the description -- a Text::TeX::Group
59     my $groupref = shift;
60     my (@cols, @new_cols);
61     my ($tok, $description, $i);
62
63     # tokens in the group, not including '{' and '}'
64     my @group = $groupref->contents;
65
66     # Loop over the token(s) in the group
67     my $first = ""; my $tempfirst;
68     while (@group) {
69
70         $tok = shift(@group);
71         # Each $tok will consist of /^[clr|]*[p*@]?$/
72         # (Except first may have | and/or @ expressions before it)
73         # p*@ will end the $tok since after it comes a group in braces
74         # @ will be a TT::Token, everything else will be in TT::Text
75         $description = $tok->print;
76
77         # Chop off left lines for first column if any
78         ($tempfirst = $description) =~ s/(\|*).*/$1/;
79         if ($#cols == -1) { # |'s before any column description
80             $first .= $tempfirst;
81         } else {
82             $cols[-1] .= $tempfirst; # add it to end of current col
83         }
84
85         # Greedy searches, so only 0th column can possibly have left line
86         @new_cols = ($description =~ /[clr]\|*/g);
87         push @cols, @new_cols;
88
89         # parse a p or * or @ if necessary
90         # use exact_print in case there's weird stuff in the @ descriptions
91         $description = substr($description,-1);
92 #       if ($description eq 'p') {
93         # The m and p descriptors have identical form.
94         if ($description =~ /^[mp]$/) {
95             $tok = shift(@group);
96             my $pdes = $description . $tok->exact_print; # "p{foo}"
97             push @cols, $pdes;
98
99         } elsif ($description eq '@') {
100             $tok = shift(@group);
101             my $atdes = $description . $tok->exact_print;
102             if ($#cols == -1) { # it's an @ before any column description
103                 $first .= $atdes;
104             } else {
105                 $cols[-1] .= $atdes; # add it to end of current col
106             }
107
108         } elsif ($description eq '*') {
109
110             $tok = shift(@group); # TT::Group with number of repeats in it
111             my $rep = $tok->contents->print;
112             $tok = shift(@group); # Group to repeat $rep times
113             @new_cols = &parse_cols($tok);
114             foreach $i (1 .. $rep) {
115                 push @cols, @new_cols;
116             }
117         }
118     } # end loop over description tokens
119
120     # this handles description like {|*{3}{c}}
121     $cols[0] = $first . $cols[0];
122
123     return @cols;
124 } # end sub parse_cols
125
126 sub write_string {
127     my ($name, $s) = @_;
128     if (!$s) {
129         return '';
130     }
131     return ' ' . $name . '="' . $s . '"';
132 }
133
134 sub write_bool {
135     my ($name, $b) = @_;
136     if (!$b) {
137         return '';
138     }
139     write_string $name, "true";
140 }
141
142 sub write_int {
143     my ($name, $i) = @_;
144     if (!$i) {
145         return '';
146     }
147     write_string $name, $i;
148 }
149
150 ################################################################################
151 # This package handles tables for reLyX
152
153 {
154     package RelyxTable::Table;
155     # Table class
156     # Fields:
157     #    columns - array containing references to RelyxTable::Columns
158     #    rows    - array containing references to RelyxTable::Rows
159     #    active  - are we currently reading this table?
160     # Fields for printout
161     #    is_long_table
162     #    rotate
163     #    endhead
164     #    end_first_head
165     #    endfoot
166     #    end_last_foot
167
168
169 # Subroutines to read and create the table
170     sub new {
171     # 'new' takes an argument containing the LaTeX table description string,
172     #    which is a Text::TeX::Group token
173
174         my $class = shift; # should be "table"
175         my $description = shift;
176         my $thistable;
177         # This seems like a convenient place to declare this...
178         $debug_on= (defined($main::opt_d) && $main::opt_d);
179
180         # Initialize fields - including ones we don't support yet
181         $thistable->{"is_long_table"} = 0;
182         $thistable->{"rotate"} = 0;
183         $thistable->{"endhead"} = 0;
184         $thistable->{"end_first_head"} = 0;
185         $thistable->{"endfoot"} = 0;
186         $thistable->{"end_last_foot"} = 0;
187         $thistable->{"active"} = 1;
188
189         bless $thistable, $class;
190
191         # Parse the column descriptions: return an array, where each
192         #    element is a (regular text) single column description
193         my @cols = &RelyxTable::parse_cols($description);
194         my $colref;
195         my $col_description;
196         foreach $col_description (@cols) {
197             $colref = new RelyxTable::Column $col_description;
198             push @{$thistable->{"columns"}}, $colref;
199         }
200         # put the table into the table array
201         push @RelyxTable::table_array, $thistable;
202
203
204         # Now that it's blessed, put the 0th row into the table 
205         $thistable->addrow;
206
207         return $thistable;
208     } # end sub new
209
210     sub addrow {
211     # add a row to the table
212     # Since we're starting the row, we're in the 0th column
213         my $thistable = shift;
214         my $row = new RelyxTable::Row;
215         push (@{$thistable->{"rows"}}, $row);
216
217         # Also initialize the cells for this row
218         my $col;
219         foreach $col (@{$thistable->{"columns"}}) {
220             push (@{$row->{"cells"}}, RelyxTable::Cell->new($row, $col));
221         }
222     } # end sub addrow
223
224     sub nextcol {
225     # Go to next column - this just involves calling RT::Row->nextcol
226     #    on the current row
227         my $thistable = shift;
228         my $row = $thistable->current_row;
229         $row->nextcol;
230     } # end of sub nextcol
231
232     sub hcline {
233     # interpret an '\hline' or '\cline' command
234     # (It's cline if there's an arg1)
235     # hline:
236     # Add a bottom line to the row *before* the current row, unless it's
237     #    the top row. In that case, add a top line to the current (top) row
238     # Change the row and all the cells that make up the row
239     # cline:
240     # Change the cells from the row in the range given in arg1
241         my $thistable = shift;
242         my $range = shift;
243         my $is_cline = defined($range);
244         my ($rownum, $line_str, $lastrow, $cell);
245
246         if ($lastrow = $thistable->numrows - 1) { # not top row
247             $rownum = $lastrow - 1;
248             $line_str = "bottom_line";
249         } else {
250             $rownum = $lastrow;
251             $line_str = "top_line";
252         }
253
254         my $row = $thistable->{"rows"}[$rownum];
255         # Add a row line (only) if it's a \hline command
256         unless ($is_cline) {
257             $row->{"$line_str"} +=1;
258             if (defined($main::opt_d) && $row->{"$line_str"} == 2) {
259                 print "\nToo many \\hline's";
260             }
261         }
262
263         # Figure out which rows to change
264         my ($r1, $r2);
265         if ($is_cline) {
266             $range =~ /(\d+)-(\d+)/ or warn "weird \\cline range";
267             # LaTeX numbers columns from 1, we number from 0
268             ($r1, $r2) = ($1 - 1, $2 - 1);
269         } else {
270             $r1 = 0;
271             $r2 = $thistable->numcols - 1;
272         }
273
274         my $i;
275         foreach $i ($r1 .. $r2) {
276             $cell = $row->{"cells"}[$i];
277             $cell->{"$line_str"} +=1; # change the cells in the row
278         }
279     } # end sub hline
280
281     sub multicolumn {
282     # interpret a \multicolumn command
283     # This really just needs to call RT::Row->multicolumn for the correct row
284         my $thistable = shift;
285         my $row = $thistable->current_row;
286         $row->multicolumn(@_);
287     } # end sub multicolumn
288
289     sub done_reading {
290     # Finished reading a table
291         my $thistable = shift;
292         # If we just had \hlines at the end, it's not a real row
293         # But if numcols==1, curr_col *has* to be zero!
294         # HACK HACK HACK. If numcols==1 but we need to subtract a row, we
295         # won't know until LastLyX. At that point, we'll subtract a row.
296         my $row = $thistable->current_row;
297         if ($thistable->numcols > 1 && $row->{"curr_col"} == 0) {
298             pop @{$thistable->{"rows"}}
299         }
300
301         # We're no longer reading this table
302         $thistable->{"active"} = 0;
303
304         if ($debug_on) {
305             print "\nDone with table ",$#RelyxTable::table_array,", which has ",
306                 $thistable->numrows," rows and ",
307                 $thistable->numcols," columns";
308             print"\nNumber of rows may be 1 too high" if $thistable->numcols==1;
309         }
310     } # end sub done_reading
311
312     sub print_info {
313     # Subroutine to print out the table once it's created
314         &print_info_215(@_);
315     }
316
317     sub print_info_221 {
318     # Subroutine to print out the table in \lyxformat 221
319         my $thistable = shift;
320         my $to_print = '';
321         # header line
322         $to_print .= "\n<lyxtabular" .
323             RelyxTable::write_int("version", 3) .
324             RelyxTable::write_int("rows", $thistable->numrows) .
325             RelyxTable::write_int("columns", $thistable->numcols) .
326             ">\n";
327         # global longtable options
328         $to_print .= "<features" .
329            RelyxTable::write_int ("rotate", $thistable->{"rotate"}) .
330            RelyxTable::write_bool("islongtable", $thistable->{"is_long_table"}) .
331            RelyxTable::write_int ("firstHeadTopDL", 0) .
332            RelyxTable::write_int ("firstHeadBottomDL", 0) .
333            RelyxTable::write_bool("firstHeadEmpty", 0) .
334            RelyxTable::write_int ("headTopDL", 0) .
335            RelyxTable::write_int ("headBottomDL", 0) .
336            RelyxTable::write_int ("footTopDL", 0) .
337            RelyxTable::write_int ("footBottomDL", 0) .
338            RelyxTable::write_int ("lastFootTopDL", 0) .
339            RelyxTable::write_int ("lastFootBottomDL", 0) .
340            RelyxTable::write_bool("lastFootEmpty", 0) .
341            ">\n";
342         # column info
343         my $col;
344         foreach $col (@{$thistable->{"columns"}}) {
345             $to_print .= $col->print_info_221;
346         }
347         # row info
348         my $row;
349         my $cell;
350         foreach $row (@{$thistable->{"rows"}}) {
351             $to_print .= $row->print_info_221;
352             my $count = 0;
353             foreach $col (@{$thistable->{"columns"}}) {
354                 $cell = $row->{"cells"}[$count];
355                 $count++;
356                 $to_print .= $cell->print_info_221;
357             }
358             $to_print .= "</row>\n";
359         }
360         $to_print .= "</lyxtabular>\n";
361         return $to_print;
362     } # end sub print_info_221
363
364     sub print_info_215 {
365     # Subroutine to print out the table in \lyxformat 215
366         # print the header information for this table
367         my $thistable = shift;
368         my $to_print = "";
369         $to_print .= "\n\\LyXTable\nmulticol5\n";
370         my @arr = ($thistable->numrows,
371                     $thistable->numcols,
372                     $thistable->{"is_long_table"},
373                     $thistable->{"rotate"},
374                     $thistable->{"endhead"},
375                     $thistable->{"end_first_head"},
376                     $thistable->{"endfoot"},
377                     $thistable->{"end_last_foot"}
378                   );
379         $to_print .= join(" ",@arr);
380         $to_print .= "\n";
381
382         # Print row info
383         my $row;
384         foreach $row (@{$thistable->{"rows"}}) {
385             $to_print .= $row->print_info;
386         }
387
388         # Print column info
389         my $col;
390         foreach $col (@{$thistable->{"columns"}}) {
391             $to_print .= $col->print_info;
392         }
393                    
394         # Print cell info
395         my $cell;
396         foreach $row (@{$thistable->{"rows"}}) {
397             my $count = 0;
398             foreach $col (@{$thistable->{"columns"}}) {
399                 $cell = $row->{"cells"}[$count];
400                 $count++;
401                 $to_print .= $cell->print_info;
402             }
403         }
404
405         $to_print .= "\n";
406
407         return $to_print;
408     } # end sub print_info_215
409
410 # Convenient subroutines
411     sub numrows {
412         my $thistable = shift;
413         return $#{$thistable->{"rows"}} + 1;
414     } # end sub numrows
415
416     sub numcols {
417         my $thistable = shift;
418         return $#{$thistable->{"columns"}} + 1;
419     } # end sub numrows
420
421     sub current_row {
422     # Return the current row blessed as an RT::Row
423         my $thistable = shift;
424         my $row = $thistable->{"rows"}[-1];
425         bless $row, "RelyxTable::Row"; #... and return it
426     } # end sub current_row
427
428 } # end package RelyxTable::Table
429
430 ################################################################################
431
432 {
433 # Column class
434 package RelyxTable::Column;
435
436 # Fields:
437 #    alignment - left, right, or center (l, r, or c)
438 #    right_line- How many lines this column has to its right
439 #    left_line - How many lines this column has to its left
440 #                (only first column can have left lines!)
441 #    pwidth    - width argument to a 'p' alignment command -- e.g., 10cm
442 #    special   - special column description that lyx can't handle
443
444     sub new {
445         my $class = shift;
446         my $description = shift;
447         my $col;
448
449         # Initially zero everything, since we set different 
450         # fields for @ and non-@ columns
451         $col->{"alignment"} = "c";  # default
452         $col->{"left_line"} = 0;
453         $col->{"right_line"} = 0;
454         $col->{"pwidth"} = "";
455         $col->{"special"} = "";
456
457         # Any special (@) column should be handled differently
458         if ($description =~ /\@/ || $description =~ /^m/ ) {
459            # Just put the whole description in "special" field --- this
460            # corresponds the the "extra" field in LyX table popup
461            # Note that LyX ignores alignment, r/l lines for a special column
462            $col->{"special"} = $description;
463            print "\n'$description' column won't display WYSIWYG in LyX\n"
464                                                             if $debug_on;
465
466         # It's not a special @ column
467         } else {
468
469             # left line?
470             $description =~ s/^\|*//;
471             $col->{"left_line"} = length($&);
472
473             # main column description
474             $description =~ s/^[clrp]//;
475             if ($& eq "p") {
476                 $description =~ s/^\{(.+)\}//; # eat the width
477                 $col->{"pwidth"} = $1; # width without braces
478                 # note: alignment is not applicable for 'p' columns
479             } else {
480                 $col->{"alignment"} = $&;
481             }
482
483             # right line?
484             $description =~ s/^\|*//;
485             $col->{"right_line"} = length($&);
486         }
487
488         bless $col, $class; #... and return it
489     } # end sub new
490
491     sub print_info {
492     # print out header information for this column
493     # Note that we need to put "" around pwidth and special for multicol5 format
494         my $col = shift;
495         my $to_print = "";
496         my @arr = ($TableAlignments{$col->{"alignment"}},
497                       $col->{"left_line"},
498                       $col->{"right_line"},
499                       '"' . $col->{"pwidth"} . '"',
500                       '"' . $col->{"special"} . '"'
501                     );
502         $to_print .= join(" ",@arr);
503         $to_print .= "\n";
504                    
505         return $to_print;
506     }
507
508     sub print_info_221 {
509     # print out header information for this column
510         my $col = shift;
511         my $to_print = '';
512
513         $to_print = "<column" .
514 #           RelyxTable::write_attribute("alignment", $TableAlignments{$col->{"alignment"}) .
515 #           RelyxTable::write_attribute("valignment", 0) .
516 #           RelyxTable::write_attribute("leftline",  $col->{"left_line"}) .
517 #           RelyxTable::write_attribute("rightline", $col->{"right_line"} .
518 #           RelyxTable::write_length("width", $col->{"pwidth"}) .
519             RelyxTable::write_string("special", $col->{"special"}) .
520 #           ">\n";
521         return $to_print;
522     }
523
524 } # end package RelyxTable::Column
525
526 ################################################################################
527
528 {
529 package RelyxTable::Row;
530 # Fields:
531 #    top_line    - does this row have a top line?
532 #    bottom_line - does this row have a bottom line?
533 #    curr_col    - which column we're currently dealing with
534 #    cells       - array containing references to this row's cells
535
536     sub new {
537         my $class = shift;
538         my $row;
539         $row->{"top_line"} = 0;
540         $row->{"bottom_line"} = 0;
541         $row->{"is_cont_row"} = 0;
542         $row->{"newpage"} = 0;
543         $row->{"curr_col"} = 0;
544
545         bless $row, $class;
546     } # end sub new
547
548     sub nextcol {
549     # Go to next column on the current row
550         my $row = shift;
551         my $i = $row->{"curr_col"};
552         $i++;
553
554         # What if it was a multicolumn?
555         # $rcells holds a reference to the array of cells
556         my $rcells = \@{$row->{"cells"}};
557         # Paranoia check that we're not attempting to access beyond the
558         # end of the array in case reLyX failed to parse the number of
559         # columns correctly.
560         $i++ while ($i < @{$rcells} &&
561                     ${$rcells}[$i]->{"multicolumn"} eq "part");
562
563         $row->{"curr_col"} = $i;
564     } # end of sub nextcol
565
566     sub multicolumn {
567     # interpret a \multicolumn command
568     # Arg0 is the row that the multicolumn is in
569     # Arg 1 is the first argument to \multicolumn, simply a number (no braces)
570     # Arg 2 is the second argument, which is a TT::Group column specification
571         my $row = shift;
572         my ($num_cols, $coldes) = (shift, shift);
573
574         # parse_cols warns about @{} expressions, which aren't WYSIWYG
575         # and turns the description into a simple string
576         my @dum = &RelyxTable::parse_cols($coldes);
577         # LaTeX multicolumn description can only describe one column...
578         warn "Strange multicolumn description $coldes" if $#dum;
579         my $description = $dum[0];
580
581         # Set the first cell
582         my $firstcell = $row->{"curr_col"};
583         my $cell = $row->{"cells"}[$firstcell];
584         $cell->{"multicolumn"} = "begin";
585         # Simple descriptions use alignment field, others use special
586         #    Special isn't WYSIWYG in LyX -- currently, LyX can't display
587         #    '|' or @{} stuff in multicolumns
588         if ($description =~ /^[clr]$/) {
589             $cell->{"alignment"} = $description;
590         } else {
591             $cell->{"special"} = $description;
592             print "\n'$description' multicolumn won't display WYSIWYG in LyX\n"
593                                                          if $debug_on;
594         }
595
596         # Set other cells
597         my $i;
598         foreach $i (1 .. $num_cols-1) {
599             $cell = $row->{"cells"}[$firstcell + $i];
600             $cell->{"multicolumn"} = "part";
601         }
602
603     } # end sub multicolumn
604
605     sub print_info {
606     # print information for this column
607         my $row = shift;
608         my $to_print = "";
609         my @arr = ($row->{"top_line"},
610                         $row->{"bottom_line"},
611                         $row->{"is_cont_row"},
612                         $row->{"newpage"}
613                     );
614         $to_print .= join(" ",@arr);
615         $to_print .= "\n";
616                    
617         return $to_print;
618     } # end sub print_info
619
620     sub print_info_221 {
621     # print out header information for this column
622         my $row = shift;
623         my $to_print = '';
624
625         $to_print = "<row" .
626 #           RelyxTable::write_attribute("topline", $row->{"top_line"}) .
627 #           RelyxTable::write_attribute("bottomline", $row->{"bottom_line"}) .
628 #           RelyxTable::write_attribute("endhead", $row->{"endhead"}) .
629 #           RelyxTable::write_attribute("endfirsthead", $row->{"endfirsthead"}) .
630 #           RelyxTable::write_attribute("endfoot", $row->{"endfoot"}) .
631 #           RelyxTable::write_attribute("endlastfoot", $row->{"endlastfoot"}) .
632 #           RelyxTable::write_attribute("newpage", $row->{"newpage"}) .
633             ">\n";
634         return $to_print;
635     }
636 } # end package RelyxTable::Row
637
638 ################################################################################
639
640 {
641 package RelyxTable::Cell;
642 # Fields:
643 #    multicolumn - 0 (regular cell), 1 (beg. of multicol), 2 (part of multicol)
644 #    alignment   - alignment of this cell
645 #    top_line    - does the cell have a line on the top?
646 #    bottom_line - does the cell have a line on the bottom?
647 #    has_cont_row- 
648 #    rotate      - rotate cell?
649 #    line_breaks - cell has line breaks in it (???)
650 #    special     - does this multicol have a special description (@ commands?)
651 #    pwidth      - pwidth of this cell for a parbox command (for linebreaks)
652
653     sub new {
654     # args 1 and 2 are the parent row and column of this cell
655         my $class = shift;
656         my ($parent_row, $parent_col) = (shift, shift);
657         my $cell;
658         $cell->{"multicolumn"} = "normal"; # by default, it isn't a multicol
659         $cell->{"alignment"} = "l"; # doesn't really matter: will be reset soon
660         $cell->{"top_line"} = 0;
661         $cell->{"bottom_line"} = 0;
662         $cell->{"has_cont_row"} = 0;
663         $cell->{"rotate"} = 0;
664         $cell->{"line_breaks"} = 0;
665         $cell->{"special"} = "";
666         $cell->{"pwidth"} = "";
667
668         # Have to bless $cell here, so that we can call methods on it
669         bless $cell, $class;
670
671         # The cell should inherit characteristics from its parent row & col
672         $cell->row_inherit($parent_row);
673         $cell->col_inherit($parent_col);
674
675         return $cell;
676     } # end sub new
677
678     sub row_inherit {
679     # Inherit fields from parent row
680         my ($cell, $row) = (shift, shift);
681         $cell->{"top_line"} = $row->{"top_line"};
682         $cell->{"bottom_line"} = $row->{"bottom_line"};
683     } # end sub row_inherit
684
685     sub col_inherit {
686     # Inherit field(s) from parent column
687         my ($cell, $col) = (shift, shift);
688         $cell->{"alignment"} = $col->{"alignment"};
689     }
690
691     sub print_info {
692     # print information for this cell
693     # Note that we need to put "" around pwidth and special for multicol5 format
694         my $cell = shift;
695         my $to_print = "";
696         my @arr = ($MulticolumnTypes{$cell->{"multicolumn"}},
697                         $TableAlignments{$cell->{"alignment"}},
698                         $cell->{"top_line"},
699                         $cell->{"bottom_line"},
700                         $cell->{"has_cont_row"},
701                         $cell->{"rotate"},
702                         $cell->{"line_breaks"},
703                       '"' . $cell->{"special"} . '"',
704                       '"' . $cell->{"pwidth"} . '"',
705                     );
706         $to_print .= join(" ",@arr);
707         $to_print .= "\n";
708                    
709         return $to_print;
710     }
711     sub print_info_221 {
712     # print out header information for this column
713         my $cell = shift;
714         my $to_print = '';
715
716         $to_print = "<cell" .
717 #           RelyxTable::write_attribute("topline", $row->{"top_line"}) .
718 #           RelyxTable::write_attribute("bottomline", $row->{"bottom_line"}) .
719 #           RelyxTable::write_attribute("endhead", $row->{"endhead"}) .
720 #           RelyxTable::write_attribute("endfirsthead", $row->{"endfirsthead"}) .
721 #           RelyxTable::write_attribute("endfoot", $row->{"endfoot"}) .
722 #           RelyxTable::write_attribute("endlastfoot", $row->{"endlastfoot"}) .
723 #           RelyxTable::write_attribute("newpage", $row->{"newpage"}) .
724             ">\n" .
725             "\\begin_inset " .
726             "\n\\end_inset \n" .
727             "</cell>\n";
728         return $to_print;
729     }
730 } # end package RelyxTable::Cell
731
732 1; # return "true" to calling routine