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