]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/table.C
what's the difference between \\ and \tabularnewline ?
[lyx.git] / src / tex2lyx / table.C
1 /** The .tex to .lyx converter
2     \author André Pönitz (2003)
3  */
4
5 // {[(
6
7 #include <config.h>
8
9 #include "Lsstream.h"
10 #include "tex2lyx.h"
11
12 #include <cctype>
13 #include <fstream>
14 #include <iostream>
15 #include <string>
16 #include <vector>
17
18 using std::cerr;
19 using std::endl;
20 using std::istringstream;
21 using std::ostream;
22 using std::ostringstream;
23 using std::string;
24 using std::vector;
25
26
27 namespace {
28
29 struct ColInfo
30 {
31         ColInfo() : rightline(0), leftline(false) {}
32         string align;      // column alignment
33         string width;      // column width
34         int    rightline;  // a line on the right?
35         bool   leftline;
36 };
37
38
39 struct RowInfo
40 {
41         RowInfo() : topline(false), bottomline(false) {} 
42         bool topline;     // horizontal line above
43         int  bottomline;  // horizontal line below
44 };
45
46
47 struct CellInfo
48 {
49         CellInfo()
50                 : multi(0), leftline(false), rightline(false),
51            topline(false), bottomline(false)
52         {}
53
54         string content;    // cell content
55         int multi;         // multicolumn flag
56         string align;      // cell alignment
57         bool leftline;     // do we have a line on the left?
58         bool rightline;    // do we have a line on the right?
59         bool topline;        // do we have a line above?
60         bool bottomline;   // do we have a line below?
61 };
62
63
64 int string2int(string const & s, int deflt = 0)
65 {
66         istringstream is(s);
67         int i = deflt;
68         is >> i;
69         return i;
70 }
71
72
73 string read_hlines(Parser & p)
74 {
75         ostringstream os;
76         p.skipSpaces();
77         while (p.good()) {
78                 if (p.next_token().cs() == "hline") {
79                         p.get_token();
80                         os << "\\hline";
81                 } else if (p.next_token().cs() == "cline") {
82                         p.get_token();
83                         os << "\\cline{" << p.verbatim_item() << "}";
84                 } else
85                         break;
86                 p.skipSpaces();
87         };
88         //cerr << "read_hlines(), read: '" << os.str() << "'\n";
89         //cerr << "read_hlines(), next token: " << p.next_token() << "\n";
90         return os.str();
91 }
92
93
94
95 /* rather brutish way to code table structure in a string:
96
97   \begin{tabular}{ccc}
98     1 & 2 & 3\\ \hline
99     \multicolumn{2}{c}{4} & 5 //
100     6 & 7 \\
101   \end{tabular}
102
103  gets "translated" to:
104
105   1 TAB 2 TAB 3 LINE
106   \hline HLINE  TAB 5 LINE
107   5 TAB 7 LINE
108 */
109
110 char const TAB   = '\001';
111 char const LINE  = '\002';
112 char const HLINE = '\004';
113
114 string get_align(char c)
115 {
116         switch (c) {
117                 case 'c': return "center";
118                 case 'l': return "left";
119                 case 'r': return "right";
120                 case 'b': return "block";
121         }
122         return "center";
123 }
124
125
126 void handle_colalign(Parser & p, vector<ColInfo> & colinfo)
127 {
128         if (p.get_token().cat() != catBegin)
129                 cerr << "wrong syntax for table column alignment. '{' expected\n";
130
131         string nextalign = "block";
132         bool leftline = false;
133         for (Token t=p.get_token(); p.good() && t.cat() != catEnd; t = p.get_token()){
134 #ifdef FILEDEBUG
135                 cerr << "t: " << t << "  c: '" << t.character() << "'\n";
136 #endif
137
138                 switch (t.character()) {
139                         case 'c':
140                         case 'l':
141                         case 'r': {
142                                 ColInfo ci;
143                                 ci.align = get_align(t.character());
144                                 if (colinfo.size() && colinfo.back().rightline > 1) {
145                                         ci.leftline = true;
146                                         --colinfo.back().rightline;
147                                 }
148                                 colinfo.push_back(ci);
149                                 break;
150                         }
151                         case 'p':
152                                 colinfo.push_back(ColInfo());
153                                 colinfo.back().align = nextalign;
154                                 colinfo.back().width = p.verbatim_item();
155                                 nextalign = "block";
156                                 break;
157                         case '|':
158                                 if (colinfo.empty())
159                                         leftline = true;
160                                 else
161                                         ++colinfo.back().rightline;
162                                 break;
163                         case '>': {
164                                 string s = p.verbatim_item();
165                                 if (s == "\\raggedleft ")
166                                         nextalign = "left";
167                                 else if (s == "\\raggedright ")
168                                         nextalign = "right";
169                                 else
170                                         cerr << "unknown '>' column '" << s << "'\n";
171                                 break;
172                         }
173                         default:
174                                 cerr << "ignoring special separator '" << t << "'\n";
175                                 break;
176                         }
177         }
178         if (colinfo.size() && leftline)
179                 colinfo[0].leftline = true;
180 }
181
182
183 } // anonymous namespace
184
185
186 void parse_table(Parser & p, ostream & os, unsigned flags)
187 {
188         string hlines;
189
190         while (p.good()) {
191                 Token const & t = p.get_token();
192
193 #ifdef FILEDEBUG
194                 cerr << "t: " << t << " flags: " << flags << "\n";
195 #endif
196
197                 //
198                 // cat codes
199                 //
200                 if (t.cat() == catMath) {
201                                 // we are inside some text mode thingy, so opening new math is allowed
202                         Token const & n = p.get_token();
203                         if (n.cat() == catMath) {
204                                 // TeX's $$...$$ syntax for displayed math
205                                 os << "\\[";
206                                 parse_math(p, os, FLAG_SIMPLE, MATH_MODE);
207                                 os << "\\]";
208                                 p.get_token(); // skip the second '$' token
209                         } else {
210                                 // simple $...$  stuff
211                                 p.putback();
212                                 os << '$';
213                                 parse_math(p, os, FLAG_SIMPLE, MATH_MODE);
214                                 os << '$';
215                         }
216                 }
217
218                 else if (t.cat() == catLetter ||
219                                t.cat() == catSpace ||
220                                t.cat() == catSuper ||
221                                t.cat() == catSub ||
222                                t.cat() == catOther ||
223                                t.cat() == catActive ||
224                                t.cat() == catNewline ||
225                                t.cat() == catParameter)
226                         os << t.character();
227
228                 else if (t.cat() == catBegin) {
229                         os << '{';
230                         parse_table(p, os, FLAG_BRACE_LAST);
231                         os << '}';
232                 }
233
234                 else if (t.cat() == catEnd) {
235                         if (flags & FLAG_BRACE_LAST)
236                                 return;
237                         cerr << "unexpected '}'\n";
238                 }
239
240                 else if (t.cat() == catAlign) {
241                         os << TAB;
242                 }
243
244                 else if (t.cs() == "tabularnewline" || t.cs() == "\\") {
245                 //else if (t.cs() == "tabularnewline") {
246                         // stuff before the line break
247                         // and look ahead for stuff after the line break
248                         os << HLINE << hlines << HLINE << LINE << read_hlines(p) << HLINE;
249                         hlines.erase();
250                 }
251
252                 else if (t.cs() == "hline")
253                         hlines += "\\hline";
254
255                 else if (t.cs() == "cline")
256                         hlines += "\\cline{" + p.verbatim_item() + '}';
257
258                 else if (t.cat() == catComment)
259                         handle_comment(p);
260
261                 else if (t.cs() == "(") {
262                         os << "\\(";
263                         parse_math(p, os, FLAG_SIMPLE2, MATH_MODE);
264                         os << "\\)";
265                 }
266
267                 else if (t.cs() == "[") {
268                         os << "\\[";
269                         parse_math(p, os, FLAG_EQUATION, MATH_MODE);
270                         os << "\\]";
271                 }
272
273                 else if (t.cs() == "begin") {
274                         string const name = p.getArg('{', '}');
275                         active_environments.push_back(name);
276                         parse_table(p, os, FLAG_END);
277                 }
278
279                 else if (t.cs() == "end") {
280                         if (flags & FLAG_END) {
281                                 // eat environment name
282                                 string const name = p.getArg('{', '}');
283                                 if (name != active_environment())
284                                         p.error("\\end{" + name + "} does not match \\begin{"
285                                                 + active_environment() + "}");
286                                 active_environments.pop_back();
287                                 return;
288                         }
289                         p.error("found 'end' unexpectedly");
290                 }
291
292                 else 
293                         os << t.asInput();
294         }
295 }
296
297
298 void handle_hline_above(RowInfo & ri, vector<CellInfo> & ci)
299 {
300         ri.topline = true;
301         for (size_t col = 0; col < ci.size(); ++col)
302                 ci[col].topline = true;
303 }
304
305
306 void handle_hline_below(RowInfo & ri, vector<CellInfo> & ci)
307 {
308         ri.bottomline = true;
309         for (size_t col = 0; col < ci.size(); ++col)
310                 ci[col].bottomline = true;
311 }
312
313
314 void handle_tabular(Parser & p, ostream & os)
315 {
316         string posopts = p.getOpt();
317         if (posopts.size())
318                 cerr << "vertical tabular positioning '" << posopts << "' ignored\n";
319
320         vector<ColInfo>            colinfo;
321
322         // handle column formatting
323         handle_colalign(p, colinfo);
324
325         // handle initial hlines
326
327         // first scan of cells
328         // use table mode to keep it minimal-invasive
329         // not exactly what's TeX doing...
330         vector<string> lines;
331         ostringstream ss;
332         ss << read_hlines(p) << HLINE; // handle initial hlines
333         parse_table(p, ss, FLAG_END);
334         split(ss.str(), lines, LINE);
335
336         vector< vector<CellInfo> > cellinfo(lines.size());
337         vector<RowInfo> rowinfo(lines.size());
338         
339         // split into rows
340         //cerr << "// split into rows\n";
341         for (size_t row = 0; row < rowinfo.size(); ++row) {
342
343                 // init row
344                 cellinfo[row].resize(colinfo.size());
345
346                 // split row    
347                 vector<string> dummy;
348                 //cerr << "\n########### LINE: " << lines[row] << "########\n";
349                 split(lines[row], dummy, HLINE);
350
351                 // handle horizontal line fragments
352                 if (dummy.size() != 3) {
353                         if (dummy.size() != 1)
354                                 cerr << "unexpected dummy size: " << dummy.size()
355                                         << " content: " << lines[row] << "\n";
356                         dummy.resize(3);
357                 }
358                 lines[row] = dummy[1];
359
360                 //cerr << "line: " << row << " above 0: " << dummy[0] << "\n";
361                 //cerr << "line: " << row << " below 2: " << dummy[2] <<  "\n";
362                 //cerr << "line: " << row << " cells 1: " << dummy[1] <<  "\n";
363
364                 for (int i = 0; i <= 2; i += 2) {       
365                         //cerr << "   reading from line string '" << dummy[i] << "'\n";
366                         Parser p1(dummy[i]);
367                         while (p1.good()) {
368                                 Token t = p1.get_token();
369                                 //cerr << "read token: " << t << "\n";
370                                 if (t.cs() == "hline") {
371                                         if (i == 0) {
372                                                 if (rowinfo[row].topline) {
373                                                         if (row > 0) // extra bottomline above
374                                                                 handle_hline_below(rowinfo[row - 1], cellinfo[row - 1]);
375                                                         else
376                                                                 cerr << "dropping extra hline\n";
377                                                         //cerr << "below row: " << row-1 << endl;
378                                                 } else {
379                                                         handle_hline_above(rowinfo[row], cellinfo[row]);
380                                                         //cerr << "above row: " << row << endl;
381                                                 }
382                                         } else {        
383                                                 //cerr << "below row: " << row << endl;
384                                                 handle_hline_below(rowinfo[row], cellinfo[row]);
385                                         }
386                                 } else if (t.cs() == "cline") {
387                                         string arg = p1.verbatim_item();
388                                         //cerr << "read cline arg: '" << arg << "'\n";
389                                         vector<string> t;
390                                         split(arg, t, '-');
391                                         t.resize(2);
392                                         size_t from = string2int(t[0]) - 1;
393                                         size_t to = string2int(t[1]);
394                                         for (size_t col = from; col < to; ++col) {
395                                                 //cerr << "row: " << row << " col: " << col << " i: " << i << endl;
396                                                 if (i == 0) {
397                                                         rowinfo[row].topline = true;
398                                                         cellinfo[row][col].topline = true;
399                                                 } else {
400                                                         rowinfo[row].bottomline = true;
401                                                         cellinfo[row][col].bottomline = true;
402                                                 }
403                                         }
404                                 } else {
405                                         cerr << "unexpected line token: " << t << endl;
406                                 }
407                         }
408                 }
409
410                 // split into cells
411                 vector<string> cells;
412                 split(lines[row], cells, TAB);
413                 for (size_t col = 0, cell = 0;
414                                 cell < cells.size() && col < colinfo.size(); ++col, ++cell) {
415                         //cerr << "cell content: '" << cells[cell] << "'\n";
416                         Parser p(cells[cell]);
417                         p.skipSpaces(); 
418                         //cells[cell] << "'\n";
419                         if (p.next_token().cs() == "multicolumn") {
420                                 // how many cells?
421                                 p.get_token();
422                                 size_t const ncells = string2int(p.verbatim_item());
423
424                                 // special cell properties alignment    
425                                 vector<ColInfo> t;
426                                 handle_colalign(p, t);
427                                 cellinfo[row][col].multi     = 1;
428                                 cellinfo[row][col].align     = t.front().align;
429                                 cellinfo[row][col].content   = parse_text(p, FLAG_ITEM, false);
430                                 cellinfo[row][col].leftline  |= t.front().leftline;
431                                 cellinfo[row][col].rightline |= t.front().rightline;
432
433                                 // add dummy cells for multicol
434                                 for (size_t i = 0; i < ncells - 1 && col < colinfo.size(); ++i) {
435                                         ++col;
436                                         cellinfo[row][col].multi = 2;
437                                         cellinfo[row][col].align = "center";
438                                 }
439
440                                 // more than one line on the right?
441                                 if (t.front().rightline > 1)
442                                         cellinfo[row][col + 1].leftline = true;
443
444                         } else {        
445                                 // FLAG_END is a hack, we need to read all of it
446                                 cellinfo[row][col].content = parse_text(p, FLAG_END, false);
447                         }
448                 }
449
450                 //cerr << "//  handle almost empty last row what we have\n";
451                 // handle almost empty last row
452                 if (row && lines[row].empty() && row + 1 == rowinfo.size()) {
453                         //cerr << "remove empty last line\n";
454                         if (rowinfo[row].topline)
455                                 rowinfo[row - 1].bottomline = true;
456                         for (size_t col = 0; col < colinfo.size(); ++col)
457                                 if (cellinfo[row][col].topline)
458                                         cellinfo[row - 1][col].bottomline = true;
459                         rowinfo.pop_back();
460                 }
461
462         }
463
464         //cerr << "// output what we have\n";
465         // output what we have
466         os << "<lyxtabular version=\"3\" rows=\"" << rowinfo.size()
467                  << "\" columns=\"" << colinfo.size() << "\">\n"
468                  << "<features>\n";
469
470         //cerr << "// after header\n";
471         for (size_t col = 0; col < colinfo.size(); ++col) {
472                 os << "<column alignment=\"" << colinfo[col].align << "\"";
473                 if (colinfo[col].rightline)
474                         os << " rightline=\"true\"";
475                 if (colinfo[col].leftline)
476                         os << " leftline=\"true\"";
477                 os << " valignment=\"top\"";
478                 os << " width=\"" << colinfo[col].width << "\"";
479                 os << ">\n";
480         }
481         //cerr << "// after cols\n";
482
483         for (size_t row = 0; row < rowinfo.size(); ++row) {
484                 os << "<row";
485                 if (rowinfo[row].topline)
486                         os << " topline=\"true\"";
487                 if (rowinfo[row].bottomline)
488                         os << " bottomline=\"true\"";
489                 os << ">\n";
490                 for (size_t col = 0; col < colinfo.size(); ++col) {
491                         CellInfo const & cell = cellinfo[row][col];
492                         os << "<cell";
493                         if (cell.multi)
494                                 os << " multicolumn=\"" << cell.multi << "\"";
495                         if (cell.leftline)
496                                 os << " leftline=\"true\"";
497                         if (cell.rightline)
498                                 os << " rightline=\"true\"";
499                         if (cell.topline)
500                                 os << " topline=\"true\"";
501                         if (cell.bottomline)
502                                 os << " bottomline=\"true\"";
503                         //cerr << "\nrow: " << row << " col: " << col;
504                         //if (cell.topline)
505                         //      cerr << " topline=\"true\"";
506                         //if (cell.bottomline)
507                         //      cerr << " bottomline=\"true\"";
508                         os << " alignment=\"" << cell.align << "\""
509                                  << " valignment=\"top\""
510                                  << " usebox=\"none\""
511                                  << ">"
512                            << "\n\\begin_inset Text"
513                            << "\n\n\\layout Standard\n\n"
514                            << cell.content
515                            << "\n\\end_inset\n\n"
516                            << "</cell>\n";
517                 }
518                 os << "</row>\n";
519         }
520                         
521         os << "</lyxtabular>\n";
522 }
523
524
525
526
527 // }])