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