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