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