]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/table.C
fix bug 1750
[lyx.git] / src / tex2lyx / table.C
1 /**
2  * \file table.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  * \author Jean-Marc Lasgouttes
8  * \author Georg Baum
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 // {[(
14
15 #include <config.h>
16
17 #include "tex2lyx.h"
18
19 #include <cctype>
20 #include <fstream>
21 #include <iostream>
22 #include <sstream>
23 #include <vector>
24 #include <map>
25
26 using std::cerr;
27 using std::endl;
28 using std::istringstream;
29 using std::ostream;
30 using std::ostringstream;
31 using std::string;
32 using std::vector;
33
34
35 // filled in preamble.C
36 std::map<char, int> special_columns;
37
38
39 namespace {
40
41 struct ColInfo
42 {
43         ColInfo() : align('c'), rightline(false), leftline(false) {}
44         /// column alignment
45         char align;
46         /// column width
47         string width;
48         /// special column alignment
49         string special;
50         /// how many lines on the right?
51         int rightline;
52         /// a line on the left?
53         bool leftline;
54 };
55
56
57 /// row type for longtables
58 enum LTRowType
59 {
60         /// normal row
61         LT_NORMAL,
62         /// part of head
63         LT_HEAD,
64         /// part of head on first page
65         LT_FIRSTHEAD,
66         /// part of foot
67         LT_FOOT,
68         /// part of foot on last page
69         LT_LASTFOOT
70 };
71
72
73 struct RowInfo
74 {
75         RowInfo() : topline(false), bottomline(false), type(LT_NORMAL),
76                     newpage(false) {}
77         /// horizontal line above
78         bool topline;
79         /// horizontal line below
80         bool bottomline;
81         /// These are for longtabulars only
82         /// row type (head, foot, firsthead etc.)
83         LTRowType type;
84         /// row for a pagebreak
85         bool newpage;
86 };
87
88
89 struct CellInfo
90 {
91         CellInfo() : multi(0), align('n'), leftline(false), rightline(false),
92                      topline(false), bottomline(false) {}
93         /// cell content
94         string content;
95         /// multicolumn flag
96         int multi;
97         /// cell alignment
98         char align;
99         /// do we have a line on the left?
100         bool leftline;
101         /// do we have a line on the right?
102         bool rightline;
103         /// do we have a line above?
104         bool topline;
105         /// do we have a line below?
106         bool bottomline;
107 };
108
109
110 /// translate a horizontal alignment (as stored in ColInfo and CellInfo) to LyX
111 inline char const * verbose_align(char c)
112 {
113         return c == 'c' ? "center" : c == 'r' ? "right" : c == 'l' ? "left" : "none";
114 }
115
116
117 // stripped down from tabluar.C. We use it currently only for bools and
118 // strings
119 string const write_attribute(string const & name, bool const & b)
120 {
121         // we write only true attribute values so we remove a bit of the
122         // file format bloat for tabulars.
123         return b ? ' ' + name + "=\"true\"" : string();
124 }
125
126
127 string const write_attribute(string const & name, string const & s)
128 {
129         return s.empty() ? string() : ' ' + name + "=\"" + s + '"';
130 }
131
132
133 int string2int(string const & s, int deflt = 0)
134 {
135         istringstream is(s);
136         int i = deflt;
137         is >> i;
138         return i;
139 }
140
141
142 /* rather brutish way to code table structure in a string:
143
144   \begin{tabular}{ccc}
145     1 & 2 & 3\\ \hline
146     \multicolumn{2}{c}{4} & 5 //
147     6 & 7 \\
148   \end{tabular}
149
150  gets "translated" to:
151
152          HLINE 1 TAB 2               TAB 3 HLINE HLINE LINE
153   \hline HLINE \multicolumn{2}{c}{4} TAB 5 HLINE HLINE LINE
154          HLINE 6 TAB 7                     HLINE HLINE LINE
155 */
156
157 char const TAB   = '\001';
158 char const LINE  = '\002';
159 char const HLINE = '\004';
160
161
162 /// handle column specifications for tabulars and multicolumns
163 void handle_colalign(Parser & p, vector<ColInfo> & colinfo)
164 {
165         if (p.get_token().cat() != catBegin)
166                 cerr << "wrong syntax for table column alignment. '{' expected\n";
167
168         char nextalign = 'b';
169         bool leftline = false;
170         for (Token t=p.get_token(); p.good() && t.cat() != catEnd; t = p.get_token()){
171 #ifdef FILEDEBUG
172                 cerr << "t: " << t << "  c: '" << t.character() << "'\n";
173 #endif
174
175                 // We cannot handle comments here
176                 if (t.cat() == catComment) {
177                         if (t.cs().empty()) {
178                                 // "%\n" combination
179                                 p.skip_spaces();
180                         } else
181                                 cerr << "Ignoring comment: " << t.asInput();
182                         continue;
183                 }
184
185                 switch (t.character()) {
186                         case 'c':
187                         case 'l':
188                         case 'r': {
189                                 ColInfo ci;
190                                 ci.align = t.character();
191                                 if (colinfo.size() && colinfo.back().rightline > 1) {
192                                         ci.leftline = true;
193                                         --colinfo.back().rightline;
194                                 }
195                                 colinfo.push_back(ci);
196                                 break;
197                         }
198                         case 'p':
199                                 colinfo.push_back(ColInfo());
200                                 colinfo.back().align = nextalign;
201                                 colinfo.back().width = p.verbatim_item();
202                                 nextalign = 'b';
203                                 break;
204                         case '|':
205                                 if (colinfo.empty())
206                                         leftline = true;
207                                 else
208                                         ++colinfo.back().rightline;
209                                 break;
210                         case '>': {
211                                 string s = p.verbatim_item();
212                                 if (s == "\\raggedleft ")
213                                         nextalign = 'l';
214                                 else if (s == "\\raggedright ")
215                                         nextalign = 'r';
216                                 else
217                                         cerr << "unknown '>' column '" << s << "'\n";
218                                 break;
219                         }
220                         default:
221                                 if (special_columns.find(t.character()) != special_columns.end()) {
222                                         ColInfo ci;
223                                         ci.align = 'c';
224                                         ci.special += t.character();
225                                         int const nargs = special_columns[t.character()];
226                                         for (int i = 0; i < nargs; ++i)
227                                                 ci.special += "{" + p.verbatim_item() + "}";
228                                         //cerr << "handling special column '" << t << "' " << nargs
229                                         //      << "  '" << ci.special << "'\n";
230                                         colinfo.push_back(ci);
231                                 } else {
232                                         cerr << "ignoring special separator '" << t << "'\n";
233                                 }
234                                 break;
235                         }
236         }
237         if (colinfo.size() && leftline)
238                 colinfo[0].leftline = true;
239 }
240
241
242 /*!
243  * Parse hlines and similar stuff.
244  * \returns wether the token \p t was parsed
245  */
246 bool parse_hlines(Parser & p, Token const & t, string & hlines,
247                   bool is_long_tabular)
248 {
249         BOOST_ASSERT(t.cat() == catEscape);
250
251         if (t.cs() == "hline")
252                 hlines += "\\hline";
253
254         else if (t.cs() == "cline")
255                 hlines += "\\cline{" + p.verbatim_item() + '}';
256
257         else if (is_long_tabular && t.cs() == "newpage")
258                 hlines += "\\newpage";
259
260         else
261                 return false;
262
263         return true;
264 }
265
266
267 /// Position in a row
268 enum RowPosition {
269         /// At the very beginning, before the first token
270         ROW_START,
271         /// After the first token and before any column token
272         IN_HLINES_START,
273         /// After the first column token. Comments and whitespace are only
274         /// treated as tokens in this position
275         IN_COLUMNS,
276         /// After the first non-column token at the end
277         IN_HLINES_END
278 };
279
280
281 /*!
282  * Parse table structure.
283  * We parse tables in a two-pass process: This function extracts the table
284  * structure (rows, columns, hlines etc.), but does not change the cell
285  * content. The cell content is parsed in a second step in handle_tabular().
286  */
287 void parse_table(Parser & p, ostream & os, bool is_long_tabular,
288                  RowPosition & pos, unsigned flags)
289 {
290         // table structure commands such as \hline
291         string hlines;
292
293         // comments that occur at places where we can't handle them
294         string comments;
295
296         while (p.good()) {
297                 Token const & t = p.get_token();
298
299 #ifdef FILEDEBUG
300                 cerr << "t: " << t << " flags: " << flags << "\n";
301 #endif
302
303                 // comments and whitespace in hlines
304                 switch (pos) {
305                 case ROW_START:
306                 case IN_HLINES_START:
307                 case IN_HLINES_END:
308                         if (t.cat() == catComment) {
309                                 if (t.cs().empty())
310                                         // line continuation
311                                         p.skip_spaces();
312                                 else
313                                         // We can't handle comments here,
314                                         // store them for later use
315                                         comments += t.asInput();
316                                 continue;
317                         } else if (t.cat() == catSpace ||
318                                    t.cat() == catNewline) {
319                                 // whitespace is irrelevant here, we
320                                 // need to recognize hline stuff
321                                 p.skip_spaces();
322                                 continue;
323                         }
324                         break;
325                 case IN_COLUMNS:
326                         break;
327                 }
328
329                 // We need to handle structure stuff first in order to
330                 // determine wether we need to output a HLINE separator
331                 // before the row or not.
332                 if (t.cat() == catEscape) {
333                         if (parse_hlines(p, t, hlines, is_long_tabular)) {
334                                 switch (pos) {
335                                 case ROW_START:
336                                         pos = IN_HLINES_START;
337                                         break;
338                                 case IN_COLUMNS:
339                                         pos = IN_HLINES_END;
340                                         break;
341                                 case IN_HLINES_START:
342                                 case IN_HLINES_END:
343                                         break;
344                                 }
345                                 continue;
346                         }
347
348                         else if (t.cs() == "tabularnewline" ||
349                                  t.cs() == "\\" ||
350                                  t.cs() == "cr") {
351                                 if (t.cs() == "cr")
352                                         cerr << "Warning: Converting TeX "
353                                                 "'\\cr' to LaTeX '\\\\'."
354                                              << endl;
355                                 // stuff before the line break
356                                 os << comments << HLINE << hlines << HLINE
357                                    << LINE;
358                                 //cerr << "hlines: " << hlines << endl;
359                                 hlines.erase();
360                                 comments.erase();
361                                 pos = ROW_START;
362                                 continue;
363                         }
364
365                         else if (is_long_tabular &&
366                                  (t.cs() == "endhead" ||
367                                   t.cs() == "endfirsthead" ||
368                                   t.cs() == "endfoot" ||
369                                   t.cs() == "endlastfoot")) {
370                                 hlines += t.asInput();
371                                 switch (pos) {
372                                 case IN_COLUMNS:
373                                 case IN_HLINES_END:
374                                         // these commands are implicit line
375                                         // breaks
376                                         os << comments << HLINE << hlines
377                                            << HLINE << LINE;
378                                         hlines.erase();
379                                         comments.erase();
380                                         pos = ROW_START;
381                                         break;
382                                 case ROW_START:
383                                         pos = IN_HLINES_START;
384                                         break;
385                                 case IN_HLINES_START:
386                                         break;
387                                 }
388                                 continue;
389                         }
390
391                 }
392
393                 // We need a HLINE separator if we either have no hline
394                 // stuff at all and are just starting a row or if we just
395                 // got the first non-hline token.
396                 switch (pos) {
397                 case ROW_START:
398                         // no hline tokens exist, first token at row start
399                 case IN_HLINES_START:
400                         // hline tokens exist, first non-hline token at row
401                         // start
402                         os << hlines << HLINE << comments;
403                         hlines.erase();
404                         comments.erase();
405                         pos = IN_COLUMNS;
406                         break;
407                 case IN_HLINES_END:
408                         // Oops, there is still cell content after hline
409                         // stuff. This does not work in LaTeX, so we ignore
410                         // the hlines.
411                         cerr << "Ignoring '" << hlines << "' in a cell"
412                              << endl;
413                         os << comments;
414                         hlines.erase();
415                         comments.erase();
416                         pos = IN_COLUMNS;
417                         break;
418                 case IN_COLUMNS:
419                         break;
420                 }
421
422                 // If we come here we have normal cell content
423                 //
424                 // cat codes
425                 //
426                 if (t.cat() == catMath) {
427                         // we are inside some text mode thingy, so opening new math is allowed
428                         Token const & n = p.get_token();
429                         if (n.cat() == catMath) {
430                                 // TeX's $$...$$ syntax for displayed math
431                                 os << "\\[";
432                                 parse_math(p, os, FLAG_SIMPLE, MATH_MODE);
433                                 os << "\\]";
434                                 p.get_token(); // skip the second '$' token
435                         } else {
436                                 // simple $...$  stuff
437                                 p.putback();
438                                 os << '$';
439                                 parse_math(p, os, FLAG_SIMPLE, MATH_MODE);
440                                 os << '$';
441                         }
442                 }
443
444                 else if (t.cat() == catSpace || t.cat() == catNewline)
445                                 os << t.cs();
446
447                 else if (t.cat() == catLetter ||
448                                t.cat() == catSuper ||
449                                t.cat() == catSub ||
450                                t.cat() == catOther ||
451                                t.cat() == catActive ||
452                                t.cat() == catParameter)
453                         os << t.character();
454
455                 else if (t.cat() == catBegin) {
456                         os << '{';
457                         parse_table(p, os, is_long_tabular, pos,
458                                     FLAG_BRACE_LAST);
459                         os << '}';
460                 }
461
462                 else if (t.cat() == catEnd) {
463                         if (flags & FLAG_BRACE_LAST)
464                                 return;
465                         cerr << "unexpected '}'\n";
466                 }
467
468                 else if (t.cat() == catAlign) {
469                         os << TAB;
470                         p.skip_spaces();
471                 }
472
473                 else if (t.cat() == catComment)
474                         os << t.asInput();
475
476                 else if (t.cs() == "(") {
477                         os << "\\(";
478                         parse_math(p, os, FLAG_SIMPLE2, MATH_MODE);
479                         os << "\\)";
480                 }
481
482                 else if (t.cs() == "[") {
483                         os << "\\[";
484                         parse_math(p, os, FLAG_EQUATION, MATH_MODE);
485                         os << "\\]";
486                 }
487
488                 else if (t.cs() == "begin") {
489                         string const name = p.getArg('{', '}');
490                         active_environments.push_back(name);
491                         os << "\\begin{" << name << '}';
492                         if (is_math_env(name)) {
493                                 parse_math(p, os, FLAG_END, MATH_MODE);
494                         } else {
495                                 parse_table(p, os, is_long_tabular, pos,
496                                             FLAG_END);
497                         }
498                         os << "\\end{" << name << '}';
499                         active_environments.pop_back();
500                 }
501
502                 else if (t.cs() == "end") {
503                         if (flags & FLAG_END) {
504                                 // eat environment name
505                                 string const name = p.getArg('{', '}');
506                                 if (name != active_environment())
507                                         p.error("\\end{" + name + "} does not match \\begin{"
508                                                 + active_environment() + "}");
509                                 return;
510                         }
511                         p.error("found 'end' unexpectedly");
512                 }
513
514                 else
515                         os << t.asInput();
516         }
517
518         // We can have comments if the last line is incomplete
519         os << comments;
520
521         // We can have hline stuff if the last line is incomplete
522         if (!hlines.empty()) {
523                 // this does not work in LaTeX, so we ignore it
524                 cerr << "Ignoring '" << hlines << "' at end of tabular"
525                      << endl;
526         }
527 }
528
529
530 void handle_hline_above(RowInfo & ri, vector<CellInfo> & ci)
531 {
532         ri.topline = true;
533         for (size_t col = 0; col < ci.size(); ++col)
534                 ci[col].topline = true;
535 }
536
537
538 void handle_hline_below(RowInfo & ri, vector<CellInfo> & ci)
539 {
540         ri.bottomline = true;
541         for (size_t col = 0; col < ci.size(); ++col)
542                 ci[col].bottomline = true;
543 }
544
545
546 } // anonymous namespace
547
548
549 void handle_tabular(Parser & p, ostream & os, bool is_long_tabular,
550                     Context & context)
551 {
552         string posopts = p.getOpt();
553         if (!posopts.empty()) {
554                 if (is_long_tabular)
555                         cerr << "horizontal longtable";
556                 else
557                         cerr << "vertical tabular";
558                 cerr << " positioning '" << posopts << "' ignored\n";
559         }
560
561         vector<ColInfo>            colinfo;
562
563         // handle column formatting
564         handle_colalign(p, colinfo);
565
566         // first scan of cells
567         // use table mode to keep it minimal-invasive
568         // not exactly what's TeX doing...
569         vector<string> lines;
570         ostringstream ss;
571         RowPosition rowpos = ROW_START;
572         parse_table(p, ss, is_long_tabular, rowpos, FLAG_END);
573         split(ss.str(), lines, LINE);
574
575         vector< vector<CellInfo> > cellinfo(lines.size());
576         vector<RowInfo> rowinfo(lines.size());
577
578         // split into rows
579         //cerr << "// split into rows\n";
580         for (size_t row = 0; row < rowinfo.size(); ++row) {
581
582                 // init row
583                 cellinfo[row].resize(colinfo.size());
584
585                 // split row
586                 vector<string> dummy;
587                 //cerr << "\n########### LINE: " << lines[row] << "########\n";
588                 split(lines[row], dummy, HLINE);
589
590                 // handle horizontal line fragments
591                 // we do only expect this for a last line without '\\'
592                 if (dummy.size() != 3) {
593                         if ((dummy.size() != 1 && dummy.size() != 2) ||
594                             row != rowinfo.size() - 1)
595                                 cerr << "unexpected dummy size: " << dummy.size()
596                                         << " content: " << lines[row] << "\n";
597                         dummy.resize(3);
598                 }
599                 lines[row] = dummy[1];
600
601                 //cerr << "line: " << row << " above 0: " << dummy[0] << "\n";
602                 //cerr << "line: " << row << " below 2: " << dummy[2] <<  "\n";
603                 //cerr << "line: " << row << " cells 1: " << dummy[1] <<  "\n";
604
605                 for (int i = 0; i <= 2; i += 2) {
606                         //cerr << "   reading from line string '" << dummy[i] << "'\n";
607                         Parser p1(dummy[i]);
608                         while (p1.good()) {
609                                 Token t = p1.get_token();
610                                 //cerr << "read token: " << t << "\n";
611                                 if (t.cs() == "hline") {
612                                         if (i == 0) {
613                                                 if (rowinfo[row].topline) {
614                                                         if (row > 0) // extra bottomline above
615                                                                 handle_hline_below(rowinfo[row - 1], cellinfo[row - 1]);
616                                                         else
617                                                                 cerr << "dropping extra hline\n";
618                                                         //cerr << "below row: " << row-1 << endl;
619                                                 } else {
620                                                         handle_hline_above(rowinfo[row], cellinfo[row]);
621                                                         //cerr << "above row: " << row << endl;
622                                                 }
623                                         } else {
624                                                 //cerr << "below row: " << row << endl;
625                                                 handle_hline_below(rowinfo[row], cellinfo[row]);
626                                         }
627                                 } else if (t.cs() == "cline") {
628                                         string arg = p1.verbatim_item();
629                                         //cerr << "read cline arg: '" << arg << "'\n";
630                                         vector<string> t;
631                                         split(arg, t, '-');
632                                         t.resize(2);
633                                         size_t from = string2int(t[0]) - 1;
634                                         if (from >= colinfo.size()) {
635                                                 cerr << "cline starts at non "
636                                                         "existing column "
637                                                      << (from + 1) << endl;
638                                                 from = colinfo.size() - 1;
639                                         }
640                                         size_t to = string2int(t[1]) - 1;
641                                         if (to >= colinfo.size()) {
642                                                 cerr << "cline ends at non "
643                                                         "existing column "
644                                                      << (to + 1) << endl;
645                                                 to = colinfo.size() - 1;
646                                         }
647                                         for (size_t col = from; col <= to; ++col) {
648                                                 //cerr << "row: " << row << " col: " << col << " i: " << i << endl;
649                                                 if (i == 0) {
650                                                         rowinfo[row].topline = true;
651                                                         cellinfo[row][col].topline = true;
652                                                 } else {
653                                                         rowinfo[row].bottomline = true;
654                                                         cellinfo[row][col].bottomline = true;
655                                                 }
656                                         }
657                                 } else if (t.cs() == "endhead") {
658                                         if (i > 0)
659                                                 rowinfo[row].type = LT_HEAD;
660                                         for (int r = row - 1; r >= 0; --r) {
661                                                 if (rowinfo[r].type != LT_NORMAL)
662                                                         break;
663                                                 rowinfo[r].type = LT_HEAD;
664                                         }
665                                 } else if (t.cs() == "endfirsthead") {
666                                         if (i > 0)
667                                                 rowinfo[row].type = LT_FIRSTHEAD;
668                                         for (int r = row - 1; r >= 0; --r) {
669                                                 if (rowinfo[r].type != LT_NORMAL)
670                                                         break;
671                                                 rowinfo[r].type = LT_FIRSTHEAD;
672                                         }
673                                 } else if (t.cs() == "endfoot") {
674                                         if (i > 0)
675                                                 rowinfo[row].type = LT_FOOT;
676                                         for (int r = row - 1; r >= 0; --r) {
677                                                 if (rowinfo[r].type != LT_NORMAL)
678                                                         break;
679                                                 rowinfo[r].type = LT_FOOT;
680                                         }
681                                 } else if (t.cs() == "endlastfoot") {
682                                         if (i > 0)
683                                                 rowinfo[row].type = LT_LASTFOOT;
684                                         for (int r = row - 1; r >= 0; --r) {
685                                                 if (rowinfo[r].type != LT_NORMAL)
686                                                         break;
687                                                 rowinfo[r].type = LT_LASTFOOT;
688                                         }
689                                 } else if (t.cs() == "newpage") {
690                                         if (i == 0) {
691                                                 if (row > 0)
692                                                         rowinfo[row - 1].newpage = true;
693                                                 else
694                                                         cerr << "Ignoring "
695                                                                 "'\\newpage' "
696                                                                 "before rows."
697                                                              << endl;
698                                         } else
699                                                 rowinfo[row].newpage = true;
700                                 } else {
701                                         cerr << "unexpected line token: " << t << endl;
702                                 }
703                         }
704                 }
705
706                 // split into cells
707                 vector<string> cells;
708                 split(lines[row], cells, TAB);
709                 // Has the last multicolumn cell a rightline?
710                 bool last_rightline = false;
711                 for (size_t col = 0, cell = 0;
712                                 cell < cells.size() && col < colinfo.size(); ++col, ++cell) {
713                         //cerr << "cell content: '" << cells[cell] << "'\n";
714                         Parser p(cells[cell]);
715                         p.skip_spaces();
716                         //cells[cell] << "'\n";
717                         if (p.next_token().cs() == "multicolumn") {
718                                 // how many cells?
719                                 p.get_token();
720                                 size_t const ncells = string2int(p.verbatim_item());
721
722                                 // special cell properties alignment
723                                 vector<ColInfo> t;
724                                 handle_colalign(p, t);
725                                 cellinfo[row][col].multi     = 1;
726                                 cellinfo[row][col].align     = t.front().align;
727                                 ostringstream os;
728                                 parse_text_in_inset(p, os, FLAG_ITEM, false, context);
729                                 cellinfo[row][col].content   = os.str();
730
731                                 // multicolumn cells are tricky: This
732                                 // \multicolumn{2}{|c|}{col1-2}&
733                                 // \multicolumn{2}{|c|}{col3-4} "\\"
734                                 // gives | col1-2 | col3-4 | and not
735                                 //       | col1-2 || col3-4 |
736                                 // So:
737                                 if (last_rightline && t.front().leftline) {
738                                         t.front().leftline = false;
739                                 }
740                                 last_rightline = t.front().rightline;
741
742                                 // multicolumn lines override normal cell lines
743                                 cellinfo[row][col].leftline  = t.front().leftline;
744                                 cellinfo[row][col].rightline = t.front().rightline;
745
746                                 // add dummy cells for multicol
747                                 for (size_t i = 0; i < ncells - 1 && col < colinfo.size(); ++i) {
748                                         ++col;
749                                         cellinfo[row][col].multi = 2;
750                                         cellinfo[row][col].align = 'c';
751                                 }
752
753                                 // more than one line on the right?
754                                 if (t.front().rightline > 1)
755                                         cellinfo[row][col + 1].leftline = true;
756
757                         } else {
758                                 // FLAG_END is a hack, we need to read all of it
759                                 cellinfo[row][col].leftline = colinfo[col].leftline;
760                                 cellinfo[row][col].rightline = colinfo[col].rightline;
761                                 cellinfo[row][col].align = colinfo[col].align;
762                                 ostringstream os;
763                                 parse_text_in_inset(p, os, FLAG_CELL, false, context);
764                                 cellinfo[row][col].content   = os.str();
765                                 last_rightline = false;
766                         }
767                 }
768
769                 //cerr << "//  handle almost empty last row what we have\n";
770                 // handle almost empty last row
771                 if (row && lines[row].empty() && row + 1 == rowinfo.size()) {
772                         //cerr << "remove empty last line\n";
773                         if (rowinfo[row].topline)
774                                 rowinfo[row - 1].bottomline = true;
775                         for (size_t col = 0; col < colinfo.size(); ++col)
776                                 if (cellinfo[row][col].topline)
777                                         cellinfo[row - 1][col].bottomline = true;
778                         rowinfo.pop_back();
779                 }
780
781         }
782
783         //cerr << "// output what we have\n";
784         // output what we have
785         os << "\n<lyxtabular version=\"3\" rows=\"" << rowinfo.size()
786            << "\" columns=\"" << colinfo.size() << "\">\n";
787         os << "<features"
788            << write_attribute("islongtable", is_long_tabular)
789            << ">\n";
790
791         //cerr << "// after header\n";
792         for (size_t col = 0; col < colinfo.size(); ++col) {
793                 os << "<column alignment=\""
794                    << verbose_align(colinfo[col].align) << "\""
795                    << " valignment=\"top\""
796                    << write_attribute("leftline", colinfo[col].leftline)
797                    << write_attribute("rightline", colinfo[col].rightline)
798                    << write_attribute("width", colinfo[col].width)
799                    << write_attribute("special", colinfo[col].special)
800                    << ">\n";
801         }
802         //cerr << "// after cols\n";
803
804         for (size_t row = 0; row < rowinfo.size(); ++row) {
805                 os << "<row"
806                    << write_attribute("topline", rowinfo[row].topline)
807                    << write_attribute("bottomline", rowinfo[row].bottomline)
808                    << write_attribute("endhead",
809                                       rowinfo[row].type == LT_HEAD)
810                    << write_attribute("endfirsthead",
811                                       rowinfo[row].type == LT_FIRSTHEAD)
812                    << write_attribute("endfoot",
813                                       rowinfo[row].type == LT_FOOT)
814                    << write_attribute("endlastfoot",
815                                       rowinfo[row].type == LT_LASTFOOT)
816                    << write_attribute("newpage", rowinfo[row].newpage)
817                    << ">\n";
818                 for (size_t col = 0; col < colinfo.size(); ++col) {
819                         CellInfo const & cell = cellinfo[row][col];
820                         os << "<cell";
821                         if (cell.multi)
822                                 os << " multicolumn=\"" << cell.multi << "\"";
823                         os << " alignment=\"" << verbose_align(cell.align)
824                            << "\""
825                            << " valignment=\"top\""
826                            << write_attribute("topline", cell.topline)
827                            << write_attribute("bottomline", cell.bottomline)
828                            << write_attribute("leftline", cell.leftline)
829                            << write_attribute("rightline", cell.rightline);
830                         //cerr << "\nrow: " << row << " col: " << col;
831                         //if (cell.topline)
832                         //      cerr << " topline=\"true\"";
833                         //if (cell.bottomline)
834                         //      cerr << " bottomline=\"true\"";
835                         os << " usebox=\"none\""
836                            << ">"
837                            << "\n\\begin_inset Text\n"
838                            << cell.content
839                            << "\n\\end_inset\n"
840                            << "</cell>\n";
841                 }
842                 os << "</row>\n";
843         }
844
845         os << "</lyxtabular>\n";
846 }
847
848
849
850
851 // }])