1 /** The .tex to .lyx converter
2 \author André Pönitz (2003)
20 #include "texparser.h"
31 using std::istringstream;
35 using std::ostringstream;
45 ColInfo() : rightline(false) {}
46 string align; // column alignment
47 string width; // column width
48 bool rightline; // a line on the right?
54 RowInfo() : topline(false), bottomline(false) {}
55 bool topline; // horizontal line above
56 bool bottomline; // horizontal line below
63 : multi(0), leftline(false), rightline(false),
64 topline(false), bottomline(false)
67 string content; // cell content
68 int multi; // multicolumn flag
69 string align; // cell alignment
70 bool leftline; // do we have a line on the left?
71 bool rightline; // do we have a line on the right?
72 bool topline; // do we have a line above?
73 bool bottomline; // do we have a line below?
77 void parse_preamble(Parser & p, ostream & os);
79 void parse(Parser & p, ostream & os, unsigned flags, const mode_type mode,
82 string parse(Parser & p, unsigned flags, const mode_type mode,
86 parse(p, os, flags, mode, outer);
90 int string2int(string const & s, int deflt = 0)
98 string read_hlines(Parser & p)
103 if (p.nextToken().cs() == "hline") {
106 } else if (p.nextToken().cs() == "cline") {
108 os << "\\cline{" << p.verbatimItem() << "}";
113 //cerr << "read_hlines(), read: '" << os.str() << "'\n";
114 //cerr << "read_hlines(), next token: " << p.nextToken() << "\n";
120 /* rather brutish way to code table structure in a string:
124 \multicolumn{2}{c}{4} & 5 //
128 gets "translated" to:
131 \hline HLINE TAB 5 LINE
135 char const TAB = '\001';
136 char const LINE = '\002';
137 char const HLINE = '\004';
139 const char * known_languages[] = { "austrian", "babel", "bahasa", "basque",
140 "breton", "british", "bulgarian", "catalan", "croatian", "czech", "danish",
141 "dutch", "english", "esperanto", "estonian", "finnish", "francais",
142 "frenchb", "galician", "german", "germanb", "greek", "hebcal", "hebfont",
143 "hebrew", "hebrew_newcode", "hebrew_oldcode", "hebrew_p", "hyphen",
144 "icelandic", "irish", "italian", "latin", "lgrcmr", "lgrcmro", "lgrcmss",
145 "lgrcmtt", "lgrenc", "lgrlcmss", "lgrlcmtt", "lheclas", "lhecmr",
146 "lhecmss", "lhecmtt", "lhecrml", "lheenc", "lhefr", "lheredis", "lheshold",
147 "lheshscr", "lheshstk", "lsorbian", "magyar", "naustrian", "ngermanb",
148 "ngerman", "norsk", "polish", "portuges", "rlbabel", "romanian",
149 "russianb", "samin", "scottish", "serbian", "slovak", "slovene", "spanish",
150 "swedish", "turkish", "ukraineb", "usorbian", "welsh", 0};
152 char const * known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
154 char const * known_headings[] = { "caption", "title", "author", "date",
155 "paragraph", "chapter", "section", "subsection", "subsubsection", 0 };
157 char const * known_math_envs[] = { "equation", "equation*",
158 "eqnarray", "eqnarray*", "align", "align*", 0};
160 char const * known_latex_commands[] = { "ref", "cite", "label", "index",
161 "printindex", "pageref", "url", 0 };
163 // LaTeX names for quotes
164 char const * known_quotes[] = { "glqq", "grqq", "quotedblbase",
165 "textquotedblleft", 0};
167 // the same as known_quotes with .lyx names
168 char const * known_coded_quotes[] = { "gld", "grd", "gld", "grd", 0};
173 ostringstream h_preamble;
174 string h_textclass = "article";
175 string h_options = "";
176 string h_language = "english";
177 string h_inputencoding = "latin1";
178 string h_fontscheme = "default";
179 string h_graphics = "default";
180 string h_paperfontsize = "default";
181 string h_spacing = "single";
182 string h_papersize = "default";
183 string h_paperpackage = "default";
184 string h_use_geometry = "0";
185 string h_use_amsmath = "0";
186 string h_use_natbib = "0";
187 string h_use_numerical_citations = "0";
188 string h_paperorientation = "portrait";
189 string h_secnumdepth = "3";
190 string h_tocdepth = "3";
191 string h_paragraph_separation = "indent";
192 string h_defskip = "medskip";
193 string h_quotes_language = "english";
194 string h_quotes_times = "2";
195 string h_papercolumns = "1";
196 string h_papersides = "1";
197 string h_paperpagestyle = "default";
198 string h_tracking_changes = "0";
200 // current stack of nested environments
201 stack<string> active_environments;
207 s[0] = toupper(s[0]);
212 string const trim(string const & a, char const * p = " \t\n\r")
216 if (a.empty() || !*p)
219 string::size_type r = a.find_last_not_of(p);
220 string::size_type l = a.find_first_not_of(p);
222 // Is this the minimal test? (lgb)
223 if (r == string::npos && l == string::npos)
226 return a.substr(l, r - l + 1);
230 void split(string const & s, vector<string> & result, char delim = ',')
232 //cerr << "split 1: '" << s << "'\n";
235 while (getline(is, t, delim))
237 //cerr << "split 2\n";
241 // splits "x=z, y=b" into a map
242 map<string, string> split_map(string const & s)
244 map<string, string> res;
247 for (size_t i = 0; i < v.size(); ++i) {
248 size_t const pos = v[i].find('=');
249 string const index = v[i].substr(0, pos);
250 string const value = v[i].substr(pos + 1, string::npos);
251 res[trim(index)] = trim(value);
257 string join(vector<string> const & input, char const * delim)
260 for (size_t i = 0; i < input.size(); ++i) {
269 char const ** is_known(string const & str, char const ** what)
271 for ( ; *what; ++what)
278 void handle_opt(vector<string> & opts, char const ** what, string & target)
283 for ( ; *what; ++what) {
284 vector<string>::iterator it = find(opts.begin(), opts.end(), *what);
285 if (it != opts.end()) {
286 //cerr << "### found option '" << *what << "'\n";
295 bool is_math_env(string const & name)
297 for (char const ** what = known_math_envs; *what; ++what)
304 void begin_inset(ostream & os, string const & name)
306 os << "\n\\begin_inset " << name;
310 void end_inset(ostream & os)
312 os << "\n\\end_inset\n\n";
318 return active_environments.empty() ? string() : active_environments.top();
322 void handle_ert(ostream & os, string const & s)
324 begin_inset(os, "ERT");
325 os << "\nstatus Collapsed\n\n\\layout Standard\n\n";
326 for (string::const_iterator it = s.begin(), et = s.end(); it != et; ++it) {
328 os << "\n\\backslash\n";
336 void handle_par(ostream & os)
338 if (active_environments.empty())
341 string s = curr_env();
342 if (s == "document" || s == "table")
343 os << "Standard\n\n";
344 else if (s == "lyxcode")
345 os << "LyX-Code\n\n";
346 else if (s == "lyxlist")
348 else if (s == "thebibliography")
349 os << "Bibliography\n\n";
351 os << cap(s) << "\n\n";
355 void handle_package(string const & name, string const & options)
357 //cerr << "handle_package: '" << name << "'\n";
358 if (name == "a4wide") {
359 h_papersize = "a4paper";
360 h_paperpackage = "widemarginsa4";
361 } else if (name == "ae")
363 else if (name == "aecompl")
365 else if (name == "amsmath")
367 else if (name == "amssymb")
369 else if (name == "babel")
371 else if (name == "fontenc")
373 else if (name == "inputenc")
374 h_inputencoding = options;
375 else if (name == "makeidx")
377 else if (name == "verbatim")
379 else if (is_known(name, known_languages)) {
381 h_quotes_language = name;
384 h_preamble << "\\usepackage[" << options << "]{" << name << "}\n";
386 h_preamble << "\\usepackage{" << name << "}\n";
391 bool handle_colalign(Parser & p, vector<ColInfo> & colinfo)
393 if (p.getToken().cat() != catBegin)
394 cerr << "wrong syntax for table column alignment. '{' expected\n";
396 string nextalign = "block";
397 bool leftline = false;
398 for (Token t = p.getToken(); p.good() && t.cat() != catEnd; t = p.getToken()){
400 cerr << "t: " << t << " c: '" << t.character() << "'\n";
403 switch (t.character()) {
405 colinfo.push_back(ColInfo());
406 colinfo.back().align = "center";
409 colinfo.push_back(ColInfo());
410 colinfo.back().align = "left";
413 colinfo.push_back(ColInfo());
414 colinfo.back().align = "right";
417 colinfo.push_back(ColInfo());
418 colinfo.back().align = nextalign;
419 colinfo.back().width = p.verbatimItem();
426 colinfo.back().rightline = true;
429 string s = p.verbatimItem();
430 if (s == "\\raggedleft ")
432 else if (s == "\\raggedright ")
435 cerr << "unknown '>' column '" << s << "'\n";
439 cerr << "ignoring special separator '" << t << "'\n";
448 void handle_tabular(Parser & p, ostream & os, mode_type mode)
450 begin_inset(os, "Tabular \n");
451 string posopts = p.getOpt();
453 cerr << "vertical tabular positioning '" << posopts << "' ignored\n";
455 vector<ColInfo> colinfo;
457 // handle column formatting
458 bool leftline = handle_colalign(p, colinfo);
460 // handle initial hlines
462 // first scan of cells
463 // use table mode to keep it minimal-invasive
464 // not exactly what's TeX doing...
465 vector<string> lines;
467 ss << read_hlines(p) << HLINE; // handle initial hlines
468 parse(p, ss, FLAG_END, TABLE_MODE, false);
469 split(ss.str(), lines, LINE);
471 vector< vector<CellInfo> > cellinfo(lines.size());
472 vector<RowInfo> rowinfo(lines.size());
475 //cerr << "// split into rows\n";
476 for (size_t row = 0; row < rowinfo.size(); ++row) {
479 vector<CellInfo> & cellinfos = cellinfo[row];
480 cellinfos.resize(colinfo.size());
483 vector<string> dummy;
484 //cerr << "\n########### LINE: " << lines[row] << "########\n";
485 split(lines[row], dummy, HLINE);
487 // handle horizontal line fragments
488 if (dummy.size() != 3) {
489 if (dummy.size() != 1)
490 cerr << "unexpected dummy size: " << dummy.size()
491 << " content: " << lines[row] << "\n";
494 lines[row] = dummy[1];
496 //cerr << "line: " << row << " above 0: " << dummy[0] << "\n";
497 //cerr << "line: " << row << " below 2: " << dummy[2] << "\n";
498 //cerr << "line: " << row << " cells 1: " << dummy[1] << "\n";
500 for (int i = 0; i <= 2; i += 2) {
501 //cerr << " reading from line string '" << dummy[i] << "'\n";
504 Token t = p1.getToken();
505 //cerr << "read token: " << t << "\n";
506 if (t.cs() == "hline") {
508 rowinfo[row].topline = true;
509 for (size_t col = 0; col < colinfo.size(); ++col)
510 cellinfos[col].topline = true;
512 rowinfo[row].bottomline = true;
513 for (size_t col = 0; col < colinfo.size(); ++col)
514 cellinfos[col].bottomline = true;
516 } else if (t.cs() == "cline") {
517 string arg = p1.verbatimItem();
518 //cerr << "read cline arg: '" << arg << "'\n";
522 size_t from = string2int(t[0]);
523 size_t to = string2int(t[1]);
524 for (size_t col = from; col < to; ++col) {
526 cellinfos[col].topline = true;
528 cellinfos[col].bottomline = true;
531 cerr << "unexpected line token: " << t << endl;
537 vector<string> cells;
538 split(lines[row], cells, TAB);
539 for (size_t col = 0, cell = 0; cell < cells.size() && col < colinfo.size(); ++col, ++cell) {
540 //cerr << "cell content: " << cells[cell] << "\n";
541 Parser p(cells[cell]);
543 //cerr << "handling cell: " << p.nextToken().cs() << " '" <<
544 //cells[cell] << "'\n";
545 if (p.nextToken().cs() == "multicolumn") {
548 size_t ncells = string2int(p.verbatimItem());
550 // special cell properties alignment
552 bool leftline = handle_colalign(p, t);
553 CellInfo & ci = cellinfos[col];
555 ci.align = t.front().align;
556 ci.content = parse(p, FLAG_ITEM, mode, false);
557 ci.leftline = leftline;
558 ci.rightline = t.front().rightline;
560 // add dummy cells for multicol
561 for (size_t i = 0; i < ncells - 1 && col < colinfo.size(); ++i) {
563 cellinfos[col].multi = 2;
564 cellinfos[col].align = "center";
567 cellinfos[col].content = parse(p, FLAG_ITEM, mode, false);
571 cellinfo.push_back(cellinfos);
573 //cerr << "// handle almost empty last row what we have\n";
574 // handle almost empty last row
575 if (row && lines[row].empty() && row + 1 == rowinfo.size()) {
576 //cerr << "remove empty last line\n";
577 if (rowinfo[row].topline);
578 rowinfo[row - 1].bottomline = true;
579 for (size_t col = 0; col < colinfo.size(); ++col)
580 if (cellinfo[row][col].topline)
581 cellinfo[row - 1][col].bottomline = true;
587 //cerr << "// output what we have\n";
588 // output what we have
589 os << "<lyxtabular version=\"3\" rows=\"" << rowinfo.size()
590 << "\" columns=\"" << colinfo.size() << "\">\n"
593 //cerr << "// after header\n";
594 for (size_t col = 0; col < colinfo.size(); ++col) {
595 os << "<column alignment=\"" << colinfo[col].align << "\"";
596 if (colinfo[col].rightline)
597 os << " rightline=\"true\"";
598 if (col == 0 && leftline)
599 os << " leftline=\"true\"";
600 os << " valignment=\"top\"";
601 os << " width=\"" << colinfo[col].width << "\"";
604 //cerr << "// after cols\n";
606 for (size_t row = 0; row < rowinfo.size(); ++row) {
608 if (rowinfo[row].topline)
609 os << " topline=\"true\"";
610 if (rowinfo[row].bottomline)
611 os << " bottomline=\"true\"";
613 for (size_t col = 0; col < colinfo.size(); ++col) {
614 CellInfo const & cell = cellinfo[row][col];
617 os << " multicolumn=\"" << cell.multi << "\"";
619 os << " leftline=\"true\"";
621 os << " rightline=\"true\"";
623 os << " topline=\"true\"";
625 os << " bottomline=\"true\"";
626 os << " alignment=\"" << cell.align << "\""
627 << " valignment=\"top\""
628 << " usebox=\"none\""
630 begin_inset(os, "Text");
631 os << "\n\n\\layout Standard\n\n";
639 os << "</lyxtabular>\n";
644 void end_preamble(ostream & os)
646 os << "# tex2lyx 0.0.2 created this file\n"
647 << "\\lyxformat 222\n"
648 << "\\textclass " << h_textclass << "\n"
649 << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n"
650 << "\\options " << h_options << "\n"
651 << "\\language " << h_language << "\n"
652 << "\\inputencoding " << h_inputencoding << "\n"
653 << "\\fontscheme " << h_fontscheme << "\n"
654 << "\\graphics " << h_graphics << "\n"
655 << "\\paperfontsize " << h_paperfontsize << "\n"
656 << "\\spacing " << h_spacing << "\n"
657 << "\\papersize " << h_papersize << "\n"
658 << "\\paperpackage " << h_paperpackage << "\n"
659 << "\\use_geometry " << h_use_geometry << "\n"
660 << "\\use_amsmath " << h_use_amsmath << "\n"
661 << "\\use_natbib " << h_use_natbib << "\n"
662 << "\\use_numerical_citations " << h_use_numerical_citations << "\n"
663 << "\\paperorientation " << h_paperorientation << "\n"
664 << "\\secnumdepth " << h_secnumdepth << "\n"
665 << "\\tocdepth " << h_tocdepth << "\n"
666 << "\\paragraph_separation " << h_paragraph_separation << "\n"
667 << "\\defskip " << h_defskip << "\n"
668 << "\\quotes_language " << h_quotes_language << "\n"
669 << "\\quotes_times " << h_quotes_times << "\n"
670 << "\\papercolumns " << h_papercolumns << "\n"
671 << "\\papersides " << h_papersides << "\n"
672 << "\\paperpagestyle " << h_paperpagestyle << "\n"
673 << "\\tracking_changes " << h_tracking_changes << "\n";
677 void parse_preamble(Parser & p, ostream & os)
680 Token const & t = p.getToken();
683 cerr << "t: " << t << " flags: " << flags << "\n";
690 if (t.cat() == catLetter ||
691 t.cat() == catSpace ||
692 t.cat() == catSuper ||
694 t.cat() == catOther ||
695 t.cat() == catMath ||
696 t.cat() == catActive ||
697 t.cat() == catBegin ||
699 t.cat() == catAlign ||
700 t.cat() == catNewline ||
701 t.cat() == catParameter)
702 h_preamble << t.character();
704 else if (t.cat() == catComment) {
707 Token const & t = p.getToken();
708 if (t.cat() == catNewline)
712 //cerr << "comment\n";
716 else if (t.cs() == "pagestyle")
717 h_paperpagestyle == p.verbatimItem();
719 else if (t.cs() == "makeatletter") {
720 p.setCatCode('@', catLetter);
721 h_preamble << "\\makeatletter\n";
724 else if (t.cs() == "makeatother") {
725 p.setCatCode('@', catOther);
726 h_preamble << "\\makeatother\n";
729 else if (t.cs() == "newcommand" || t.cs() == "renewcommand"
730 || t.cs() == "providecommand") {
732 if (p.nextToken().character() == '*') {
736 string const name = p.verbatimItem();
737 string const opts = p.getOpt();
738 string const body = p.verbatimItem();
739 // only non-lyxspecific stuff
740 if (name != "\\noun "
741 && name != "\\tabularnewline "
743 && name != "\\lyxline "
744 && name != "\\lyxaddress "
745 && name != "\\lyxrightaddress "
746 && name != "\\boldsymbol "
747 && name != "\\lyxarrow ") {
749 ss << '\\' << t.cs();
752 ss << '{' << name << '}' << opts << '{' << body << "}\n";
753 h_preamble << ss.str();
755 ostream & out = in_preamble ? h_preamble : os;
756 out << "\\" << t.cs() << "{" << name << "}"
757 << opts << "{" << body << "}\n";
764 else if (t.cs() == "documentclass") {
766 split(p.getArg('[', ']'), opts, ',');
767 handle_opt(opts, known_languages, h_language);
768 handle_opt(opts, known_fontsizes, h_paperfontsize);
769 h_quotes_language = h_language;
770 h_options = join(opts, ",");
771 h_textclass = p.getArg('{', '}');
774 else if (t.cs() == "usepackage") {
775 string const options = p.getArg('[', ']');
776 string const name = p.getArg('{', '}');
777 if (options.empty() && name.find(',')) {
778 vector<string> vecnames;
779 split(name, vecnames, ',');
780 vector<string>::const_iterator it = vecnames.begin();
781 vector<string>::const_iterator end = vecnames.end();
782 for (; it != end; ++it)
783 handle_package(trim(*it), string());
785 handle_package(name, options);
789 else if (t.cs() == "newenvironment") {
790 string const name = p.getArg('{', '}');
792 ss << "\\newenvironment{" << name << "}";
795 ss << '{' << p.verbatimItem() << '}';
796 ss << '{' << p.verbatimItem() << '}';
798 if (name != "lyxcode" && name != "lyxlist"
799 && name != "lyxrightadress" && name != "lyxaddress")
800 h_preamble << ss.str();
803 else if (t.cs() == "def") {
804 string name = p.getToken().cs();
805 while (p.nextToken().cat() != catBegin)
806 name += p.getToken().asString();
807 h_preamble << "\\def\\" << name << '{' << p.verbatimItem() << "}\n";
810 else if (t.cs() == "setcounter") {
811 string const name = p.getArg('{', '}');
812 string const content = p.getArg('{', '}');
813 if (name == "secnumdepth")
814 h_secnumdepth = content;
815 else if (name == "tocdepth")
816 h_tocdepth = content;
818 h_preamble << "\\setcounter{" << name << "}{" << content << "}\n";
821 else if (t.cs() == "setlength") {
822 string const name = p.verbatimItem();
823 string const content = p.verbatimItem();
824 if (name == "parskip")
825 h_paragraph_separation = "skip";
826 else if (name == "parindent")
827 h_paragraph_separation = "skip";
829 h_preamble << "\\setlength{" + name + "}{" + content + "}\n";
832 else if (t.cs() == "par")
835 else if (t.cs() == "begin") {
836 string const name = p.getArg('{', '}');
837 if (name == "document") {
839 os << "\n\n\\layout Standard\n\n";
842 h_preamble << "\\begin{" << name << "}";
845 else if (t.cs().size())
846 h_preamble << '\\' << t.cs() << ' ';
851 void parse(Parser & p, ostream & os, unsigned flags, const mode_type mode,
857 Token const & t = p.getToken();
860 cerr << "t: " << t << " flags: " << flags << "\n";
863 if (flags & FLAG_ITEM) {
864 if (t.cat() == catSpace)
868 if (t.cat() == catBegin) {
869 // skip the brace and collect everything to the next matching
871 flags |= FLAG_BRACE_LAST;
875 // handle only this single token, leave the loop if done
883 if (t.cat() == catMath) {
884 if (mode == TEXT_MODE || mode == MATHTEXT_MODE) {
885 // we are inside some text mode thingy, so opening new math is allowed
886 if (mode == TEXT_MODE)
887 begin_inset(os, "Formula ");
888 Token const & n = p.getToken();
889 if (n.cat() == catMath && outer) {
890 // TeX's $$...$$ syntax for displayed math
892 parse(p, os, FLAG_SIMPLE, MATH_MODE, outer);
894 p.getToken(); // skip the second '$' token
896 // simple $...$ stuff
899 parse(p, os, FLAG_SIMPLE, MATH_MODE, outer);
902 if (mode == TEXT_MODE)
906 else if (mode == TABLE_MODE) {
910 else if (flags & FLAG_SIMPLE) {
911 // this is the end of the formula
916 cerr << "\nmode: " << mode << endl;
917 p.error("something strange in the parser\n");
922 else if (t.cat() == catLetter ||
923 t.cat() == catSpace ||
924 t.cat() == catSuper ||
926 t.cat() == catOther ||
927 t.cat() == catParameter)
930 else if (t.cat() == catNewline) {
931 if (p.nextToken().cat() == catNewline) {
935 os << "\n "; // note the space
939 else if (t.cat() == catActive) {
940 if (t.character() == '~') {
941 if (curr_env() == "lyxcode")
943 else if (mode == TEXT_MODE)
944 os << "\\SpecialChar ~\n";
951 else if (t.cat() == catBegin) {
952 if (mode == TEXT_MODE) {
954 parse(p, os, FLAG_BRACE_LAST, mode, outer);
961 else if (t.cat() == catEnd) {
962 if (flags & FLAG_BRACE_LAST)
964 if (mode == TEXT_MODE)
970 else if (t.cat() == catAlign) {
971 if (mode == TABLE_MODE)
977 else if (t.cs() == "tabularnewline") {
978 if (mode == TABLE_MODE) {
979 // stuff before the line break
980 // and look ahead for stuff after the line break
981 os << HLINE << hlines << HLINE << LINE << read_hlines(p) << HLINE;
988 else if (t.cs() == "\\" && mode == MATH_MODE)
991 else if (t.cs() == "\\" && mode == TEXT_MODE && curr_env() == "tabular")
994 else if (t.cat() == catOther)
995 os << string(1, t.character());
997 else if (t.cat() == catComment) {
1000 Token const & t = p.getToken();
1001 if (t.cat() == catNewline)
1005 //cerr << "comment\n";
1010 // control sequences
1013 else if (t.cs() == "ldots" && mode == MATH_MODE)
1014 os << "\n\\SpecialChar \\ldots{}\n";
1016 else if (t.cs() == "lyxlock")
1019 else if (t.cs() == "makeatletter") {
1020 p.setCatCode('@', catLetter);
1021 handle_ert(os, "\\makeatletter\n");
1024 else if (t.cs() == "makeatother") {
1025 p.setCatCode('@', catOther);
1026 handle_ert(os, "\\makeatother\n");
1029 else if (t.cs() == "newcommand" || t.cs() == "renewcommand"
1030 || t.cs() == "providecommand") {
1031 string const name = p.verbatimItem();
1032 string const opts = p.getOpt();
1033 string const body = p.verbatimItem();
1034 // only non-lyxspecific stuff
1035 if (name != "\\noun " && name != "\\tabularnewline ") {
1037 ss << '\\' << t.cs() << '{' << name << '}'
1038 << opts << '{' << body << "}\n";
1039 handle_ert(os, ss.str());
1041 ostream & out = in_preamble ? h_preamble : os;
1043 begin_inset(os, "FormulaMacro\n");
1044 out << "\\" << t.cs() << "{" << name << "}"
1045 << opts << "{" << body << "}\n";
1052 else if (t.cs() == "newtheorem") {
1054 ss << "\\newtheorem";
1055 ss << '{' << p.verbatimItem() << '}';
1057 ss << '{' << p.verbatimItem() << '}';
1060 handle_ert(os, ss.str());
1063 else if (t.cs() == "(") {
1064 begin_inset(os, "Formula");
1066 parse(p, os, FLAG_SIMPLE2, MATH_MODE, outer);
1071 else if (t.cs() == "[" && mode == TEXT_MODE) {
1072 begin_inset(os, "Formula");
1074 parse(p, os, FLAG_EQUATION, MATH_MODE, outer);
1079 else if (t.cs() == "protect")
1080 // ignore \\protect, will hopefully be re-added during output
1083 else if (t.cs() == "begin") {
1084 string const name = p.getArg('{', '}');
1085 active_environments.push(name);
1086 if (name == "abstract") {
1088 parse(p, os, FLAG_END, mode, outer);
1089 } else if (is_math_env(name)) {
1090 begin_inset(os, "Formula ");
1091 os << "\\begin{" << name << "}";
1092 parse(p, os, FLAG_END, MATH_MODE, outer);
1093 os << "\\end{" << name << "}";
1095 } else if (name == "tabular") {
1096 if (mode == TEXT_MODE)
1097 handle_tabular(p, os, mode);
1099 os << "\\begin{" << name << "}";
1100 parse(p, os, FLAG_END, MATHTEXT_MODE, outer);
1101 os << "\\end{" << name << "}";
1103 } else if (name == "table" || name == "figure") {
1104 string opts = p.getOpt();
1105 begin_inset(os, "Float " + name + "\n");
1107 os << "placement " << opts << '\n';
1108 os << "wide false\n"
1109 << "collapsed false\n"
1111 << "\\layout Standard\n";
1112 parse(p, os, FLAG_END, mode, outer);
1114 } else if (name == "lyxlist") {
1115 p.verbatimItem(); // swallow next arg
1116 parse(p, os, FLAG_END, mode, outer);
1117 os << "\n\\layout Bibliography\n\n";
1118 } else if (name == "thebibliography") {
1119 p.verbatimItem(); // swallow next arg
1120 parse(p, os, FLAG_END, mode, outer);
1121 os << "\n\\layout Bibliography\n\n";
1122 } else if (mode == MATH_MODE || mode == MATHTEXT_MODE) {
1123 os << "\\begin{" << name << "}";
1124 parse(p, os, FLAG_END, mode, outer);
1125 os << "\\end{" << name << "}";
1127 parse(p, os, FLAG_END, mode, outer);
1131 else if (t.cs() == "end") {
1132 if (flags & FLAG_END) {
1133 // eat environment name
1134 string const name = p.getArg('{', '}');
1135 if (name != curr_env())
1136 p.error("\\end{" + name + "} does not match \\begin{"
1137 + curr_env() + "}");
1138 active_environments.pop();
1141 p.error("found 'end' unexpectedly");
1144 else if (t.cs() == "item")
1147 else if (t.cs() == ")") {
1148 if (flags & FLAG_SIMPLE2)
1150 p.error("found '\\)' unexpectedly");
1153 else if (t.cs() == "]") {
1154 if (flags & FLAG_EQUATION)
1156 p.error("found '\\]' unexpectedly");
1159 else if (t.cs() == "documentclass") {
1160 vector<string> opts;
1161 split(p.getArg('[', ']'), opts, ',');
1162 handle_opt(opts, known_languages, h_language);
1163 handle_opt(opts, known_fontsizes, h_paperfontsize);
1164 h_quotes_language = h_language;
1165 h_options = join(opts, ",");
1166 h_textclass = p.getArg('{', '}');
1169 else if (t.cs() == "usepackage") {
1170 string const options = p.getArg('[', ']');
1171 string const name = p.getArg('{', '}');
1172 if (options.empty() && name.find(',')) {
1173 vector<string> vecnames;
1174 split(name, vecnames, ',');
1175 vector<string>::const_iterator it = vecnames.begin();
1176 vector<string>::const_iterator end = vecnames.end();
1177 for (; it != end; ++it)
1178 handle_package(trim(*it), string());
1180 handle_package(name, options);
1184 else if (t.cs() == "def") {
1185 string name = p.getToken().cs();
1186 while (p.nextToken().cat() != catBegin)
1187 name += p.getToken().asString();
1188 handle_ert(os, "\\def\\" + name + '{' + p.verbatimItem() + '}');
1191 else if (t.cs() == "par")
1194 else if (is_known(t.cs(), known_headings)) {
1195 string name = t.cs();
1196 if (p.nextToken().asInput() == "*") {
1200 os << "\n\n\\layout " << cap(name) << "\n\n";
1201 string opt = p.getOpt();
1203 begin_inset(os, "OptArg\n");
1204 os << "collapsed true\n\n\\layout Standard\n\n" << opt;
1207 parse(p, os, FLAG_ITEM, mode, outer);
1208 os << "\n\n\\layout Standard\n\n";
1211 else if (t.cs() == "includegraphics") {
1212 if (mode == TEXT_MODE) {
1213 map<string, string> opts = split_map(p.getArg('[', ']'));
1214 string name = p.verbatimItem();
1215 begin_inset(os, "Graphics ");
1216 os << "\n\tfilename " << name << '\n';
1217 if (opts.find("width") != opts.end())
1218 os << "\twidth " << opts["width"] << '\n';
1219 if (opts.find("height") != opts.end())
1220 os << "\theight " << opts["height"] << '\n';
1223 os << "\\includegraphics ";
1227 else if (t.cs() == "footnote") {
1228 begin_inset(os, "Foot\n");
1229 os << "collapsed true\n\n\\layout Standard\n\n";
1230 parse(p, os, FLAG_ITEM, mode, false);
1234 else if (t.cs() == "makeindex" || t.cs() == "maketitle")
1237 else if (t.cs() == "tableofcontents")
1238 p.verbatimItem(); // swallow this
1240 else if (t.cs() == "hline" && mode == TABLE_MODE)
1241 hlines += "\\hline";
1243 else if (t.cs() == "cline" && mode == TABLE_MODE)
1244 hlines += "\\cline{" + p.verbatimItem() + '}';
1246 else if (t.cs() == "tiny" && mode == TEXT_MODE)
1247 os << "\n\\size tiny\n";
1249 else if (t.cs() == "scriptsize" && mode == TEXT_MODE)
1250 os << "\n\\size scriptsize\n";
1252 else if (t.cs() == "Large" && mode == TEXT_MODE)
1253 os << "\n\\size larger\n";
1255 else if (t.cs() == "textrm") {
1256 if (mode == TEXT_MODE) {
1257 os << "\n\\family roman\n";
1258 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1259 os << "\n\\family default\n";
1261 os << '\\' << t.cs() << '{';
1262 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1267 else if (t.cs() == "textsf") {
1268 if (mode == TEXT_MODE) {
1269 os << "\n\\family sans\n";
1270 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1271 os << "\n\\family default\n";
1273 os << '\\' << t.cs() << '{';
1274 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1279 else if (t.cs() == "texttt") {
1280 if (mode == TEXT_MODE) {
1281 os << "\n\\family typewriter\n";
1282 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1283 os << "\n\\family default\n";
1285 os << '\\' << t.cs() << '{';
1286 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1291 else if (t.cs() == "textsc") {
1292 if (mode == TEXT_MODE) {
1293 os << "\n\\noun on\n";
1294 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1295 os << "\n\\noun default\n";
1297 os << '\\' << t.cs() << '{';
1298 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1303 else if (t.cs() == "textbf") {
1304 if (mode == TEXT_MODE) {
1305 os << "\n\\series bold\n";
1306 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1307 os << "\n\\series default\n";
1309 os << '\\' << t.cs() << '{';
1310 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1315 else if (t.cs() == "underbar") {
1316 if (mode == TEXT_MODE) {
1317 os << "\n\\bar under\n";
1318 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1319 os << "\n\\bar default\n";
1321 os << '\\' << t.cs() << '{';
1322 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1327 else if ((t.cs() == "emph" || t.cs() == "noun") && mode == TEXT_MODE) {
1328 os << "\n\\" << t.cs() << " on\n";
1329 parse(p, os, FLAG_ITEM, mode, outer);
1330 os << "\n\\" << t.cs() << " default\n";
1333 else if (t.cs() == "mbox" && mode != TEXT_MODE) {
1335 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1339 else if (is_known(t.cs(), known_latex_commands) && mode == TEXT_MODE) {
1340 begin_inset(os, "LatexCommand ");
1341 os << '\\' << t.cs();
1344 os << '{' << p.verbatimItem() << '}';
1348 else if (t.cs() == "bibitem") {
1349 os << "\n\\layout Bibliography\n\\bibitem ";
1351 os << '{' << p.verbatimItem() << '}' << "\n\n";
1354 else if (mode == TEXT_MODE && is_known(t.cs(), known_quotes)) {
1355 char const ** where = is_known(t.cs(), known_quotes);
1356 begin_inset(os, "Quotes ");
1357 os << known_coded_quotes[where - known_quotes];
1361 else if (t.cs() == "LyX" && mode == TEXT_MODE) {
1362 p.verbatimItem(); // eat {}
1366 else if (t.cs() == "TeX" && mode == TEXT_MODE) {
1367 p.verbatimItem(); // eat {}
1371 else if (t.cs() == "LaTeX" && mode == TEXT_MODE) {
1372 p.verbatimItem(); // eat {}
1376 else if (t.cs() == "LaTeXe" && mode == TEXT_MODE) {
1377 p.verbatimItem(); // eat {}
1381 else if (t.cs() == "lyxarrow" && mode == TEXT_MODE) {
1383 os << "\\SpecialChar \\menuseparator\n";
1386 else if (t.cs() == "ldots" && mode == TEXT_MODE) {
1388 os << "\\SpecialChar \\ldots{}\n";
1391 else if (t.cs() == "@" && mode == TEXT_MODE)
1392 os << "\\SpecialChar \\@";
1394 else if (t.cs() == "textasciitilde" && mode == TEXT_MODE)
1397 else if (t.cs() == "_" && mode == TEXT_MODE)
1400 else if (t.cs() == "&" && mode == TEXT_MODE)
1403 else if (t.cs() == "#" && mode == TEXT_MODE)
1406 else if (t.cs() == "\"") {
1407 string const name = p.verbatimItem();
1408 if (name == "a") os << 'ä';
1409 else if (name == "o") os << 'ö';
1410 else if (name == "u") os << 'ü';
1411 else if (name == "A") os << 'Ä';
1412 else if (name == "O") os << 'Ö';
1413 else if (name == "U") os << 'Ü';
1414 else handle_ert(os, "\"{" + name + "}");
1417 else if (t.cs() == "ss")
1420 else if (t.cs() == "input")
1421 handle_ert(os, "\\input{" + p.verbatimItem() + "}\n");
1423 else if (t.cs() == "fancyhead") {
1425 ss << "\\fancyhead";
1427 ss << '{' << p.verbatimItem() << "}\n";
1428 handle_ert(os, ss.str());
1432 //cerr << "#: " << t << " mode: " << mode << endl;
1433 if (mode == TEXT_MODE) {
1434 // heuristic: read up to next non-nested space
1436 string s = t.asInput();
1437 string z = p.verbatimItem();
1438 while (p.good() && z != " " && z.size()) {
1439 //cerr << "read: " << z << endl;
1441 z = p.verbatimItem();
1443 cerr << "found ERT: " << s << endl;
1444 handle_ert(os, s + ' ');
1446 handle_ert(os, t.asInput() + ' ');
1449 //cerr << "#: writing: '" << t.asInput() << "'\n";
1453 if (flags & FLAG_LEAVE) {
1454 flags &= ~FLAG_LEAVE;
1462 } // anonymous namespace
1465 int main(int argc, char * argv[])
1468 cerr << "Usage: " << argv[0] << " <infile.tex>" << endl;
1472 ifstream is(argv[1]);
1474 parse_preamble(p, cout);
1475 active_environments.push("document");
1476 parse(p, cout, FLAG_END, TEXT_MODE, true);
1477 cout << "\n\\the_end";