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.at(row) = dummy.at(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";
502 Parser p1(dummy.at(i));
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 c = 0; c < colinfo.size(); ++c)
510 cellinfos.at(c).topline = true;
512 rowinfo[row].bottomline = true;
513 for (size_t c = 0; c < colinfo.size(); ++c)
514 cellinfos.at(c).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.at(0));
523 size_t to = string2int(t.at(1));
524 for (size_t col = from; col < to; ++col) {
526 cellinfos.at(col).topline = true;
528 cellinfos.at(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.at(cell) << "\n";
541 Parser p(cells.at(cell));
543 //cerr << "handling cell: " << p.nextToken().cs() << " '" <<
544 //cells.at(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.at(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.at(col).multi = 2;
564 cellinfos.at(col).align = "center";
567 cellinfos.at(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.at(row).empty() && row + 1 == rowinfo.size()) {
576 //cerr << "remove empty last line\n";
577 if (rowinfo.at(row).topline);
578 rowinfo.at(row - 1).bottomline = true;
579 for (size_t c = 0; c < colinfo.size(); ++c)
580 if (cellinfo.at(row).at(c).topline)
581 cellinfo.at(row - 1).at(c).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.at(col).align << "\"";
596 if (colinfo.at(col).rightline)
597 os << " rightline=\"true\"";
598 if (col == 0 && leftline)
599 os << " leftline=\"true\"";
600 os << " valignment=\"top\"";
601 os << " width=\"" << colinfo.at(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
880 if (flags & FLAG_BRACED) {
881 if (t.cat() == catSpace)
884 if (t.cat() != catBegin) {
885 p.error("opening brace expected");
889 // skip the brace and collect everything to the next matching
891 flags = FLAG_BRACE_LAST;
895 if (flags & FLAG_OPTION) {
896 if (t.cat() == catOther && t.character() == '[') {
897 parse(p, os, FLAG_BRACK_LAST, mode, outer);
899 // no option found, put back token and we are done
908 if (t.cat() == catMath) {
909 if (mode == TEXT_MODE || mode == MATHTEXT_MODE) {
910 // we are inside some text mode thingy, so opening new math is allowed
911 if (mode == TEXT_MODE)
912 begin_inset(os, "Formula ");
913 Token const & n = p.getToken();
914 if (n.cat() == catMath && outer) {
915 // TeX's $$...$$ syntax for displayed math
917 parse(p, os, FLAG_SIMPLE, MATH_MODE, outer);
919 p.getToken(); // skip the second '$' token
921 // simple $...$ stuff
924 parse(p, os, FLAG_SIMPLE, MATH_MODE, outer);
927 if (mode == TEXT_MODE)
931 else if (mode == TABLE_MODE) {
935 else if (flags & FLAG_SIMPLE) {
936 // this is the end of the formula
941 cerr << "\nmode: " << mode << endl;
942 p.error("something strange in the parser\n");
947 else if (t.cat() == catLetter ||
948 t.cat() == catSpace ||
949 t.cat() == catSuper ||
951 t.cat() == catOther ||
952 t.cat() == catParameter)
955 else if (t.cat() == catNewline) {
956 if (p.nextToken().cat() == catNewline) {
960 os << "\n "; // note the space
964 else if (t.cat() == catActive) {
965 if (t.character() == '~') {
966 if (curr_env() == "lyxcode")
968 else if (mode == TEXT_MODE)
969 os << "\\SpecialChar ~\n";
976 else if (t.cat() == catBegin) {
977 if (mode == TEXT_MODE) {
979 parse(p, os, FLAG_BRACE_LAST, mode, outer);
986 else if (t.cat() == catEnd) {
987 if (flags & FLAG_BRACE_LAST)
989 if (mode == TEXT_MODE)
995 else if (t.cat() == catAlign) {
996 if (mode == TABLE_MODE)
1002 else if (t.cs() == "tabularnewline") {
1003 if (mode == TABLE_MODE) {
1004 // stuff before the line break
1005 // and look ahead for stuff after the line break
1006 os << HLINE << hlines << HLINE << LINE << read_hlines(p) << HLINE;
1013 else if (t.cs() == "\\" && mode == MATH_MODE)
1016 else if (t.cs() == "\\" && mode == TEXT_MODE && curr_env() == "tabular")
1019 else if (t.character() == ']' && (flags & FLAG_BRACK_LAST)) {
1020 //cerr << "finished reading option\n";
1024 else if (t.cat() == catOther)
1025 os << string(1, t.character());
1027 else if (t.cat() == catComment) {
1030 Token const & t = p.getToken();
1031 if (t.cat() == catNewline)
1035 //cerr << "comment\n";
1040 // control sequences
1043 else if (t.cs() == "ldots" && mode == MATH_MODE)
1044 os << "\n\\SpecialChar \\ldots{}\n";
1046 else if (t.cs() == "lyxlock")
1049 else if (t.cs() == "makeatletter") {
1050 p.setCatCode('@', catLetter);
1051 handle_ert(os, "\\makeatletter\n");
1054 else if (t.cs() == "makeatother") {
1055 p.setCatCode('@', catOther);
1056 handle_ert(os, "\\makeatother\n");
1059 else if (t.cs() == "newcommand" || t.cs() == "renewcommand"
1060 || t.cs() == "providecommand") {
1061 string const name = p.verbatimItem();
1062 string const opts = p.getOpt();
1063 string const body = p.verbatimItem();
1064 // only non-lyxspecific stuff
1065 if (name != "\\noun " && name != "\\tabularnewline ") {
1067 ss << '\\' << t.cs() << '{' << name << '}'
1068 << opts << '{' << body << "}\n";
1069 handle_ert(os, ss.str());
1071 ostream & out = in_preamble ? h_preamble : os;
1073 begin_inset(os, "FormulaMacro\n");
1074 out << "\\" << t.cs() << "{" << name << "}"
1075 << opts << "{" << body << "}\n";
1082 else if (t.cs() == "newtheorem") {
1084 ss << "\\newtheorem";
1085 ss << '{' << p.verbatimItem() << '}';
1087 ss << '{' << p.verbatimItem() << '}';
1090 handle_ert(os, ss.str());
1093 else if (t.cs() == "(") {
1094 begin_inset(os, "Formula");
1096 parse(p, os, FLAG_SIMPLE2, MATH_MODE, outer);
1101 else if (t.cs() == "[" && mode == TEXT_MODE) {
1102 begin_inset(os, "Formula");
1104 parse(p, os, FLAG_EQUATION, MATH_MODE, outer);
1109 else if (t.cs() == "protect")
1110 // ignore \\protect, will hopefully be re-added during output
1113 else if (t.cs() == "begin") {
1114 string const name = p.getArg('{', '}');
1115 active_environments.push(name);
1116 if (name == "abstract") {
1118 parse(p, os, FLAG_END, mode, outer);
1119 } else if (is_math_env(name)) {
1120 begin_inset(os, "Formula ");
1121 os << "\\begin{" << name << "}";
1122 parse(p, os, FLAG_END, MATH_MODE, outer);
1123 os << "\\end{" << name << "}";
1125 } else if (name == "tabular") {
1126 if (mode == TEXT_MODE)
1127 handle_tabular(p, os, mode);
1129 os << "\\begin{" << name << "}";
1130 parse(p, os, FLAG_END, MATHTEXT_MODE, outer);
1131 os << "\\end{" << name << "}";
1133 } else if (name == "table" || name == "figure") {
1134 string opts = p.getOpt();
1135 begin_inset(os, "Float " + name + "\n");
1137 os << "placement " << opts << '\n';
1138 os << "wide false\n"
1139 << "collapsed false\n"
1141 << "\\layout Standard\n";
1142 parse(p, os, FLAG_END, mode, outer);
1144 } else if (name == "lyxlist") {
1145 p.verbatimItem(); // swallow next arg
1146 parse(p, os, FLAG_END, mode, outer);
1147 os << "\n\\layout Bibliography\n\n";
1148 } else if (name == "thebibliography") {
1149 p.verbatimItem(); // swallow next arg
1150 parse(p, os, FLAG_END, mode, outer);
1151 os << "\n\\layout Bibliography\n\n";
1152 } else if (mode == MATH_MODE || mode == MATHTEXT_MODE) {
1153 os << "\\begin{" << name << "}";
1154 parse(p, os, FLAG_END, mode, outer);
1155 os << "\\end{" << name << "}";
1157 parse(p, os, FLAG_END, mode, outer);
1161 else if (t.cs() == "end") {
1162 if (flags & FLAG_END) {
1163 // eat environment name
1164 string const name = p.getArg('{', '}');
1165 if (name != curr_env())
1166 p.error("\\end{" + name + "} does not match \\begin{"
1167 + curr_env() + "}");
1168 active_environments.pop();
1171 p.error("found 'end' unexpectedly");
1174 else if (t.cs() == "item")
1177 else if (t.cs() == ")") {
1178 if (flags & FLAG_SIMPLE2)
1180 p.error("found '\\)' unexpectedly");
1183 else if (t.cs() == "]") {
1184 if (flags & FLAG_EQUATION)
1186 p.error("found '\\]' unexpectedly");
1189 else if (t.cs() == "documentclass") {
1190 vector<string> opts;
1191 split(p.getArg('[', ']'), opts, ',');
1192 handle_opt(opts, known_languages, h_language);
1193 handle_opt(opts, known_fontsizes, h_paperfontsize);
1194 h_quotes_language = h_language;
1195 h_options = join(opts, ",");
1196 h_textclass = p.getArg('{', '}');
1199 else if (t.cs() == "usepackage") {
1200 string const options = p.getArg('[', ']');
1201 string const name = p.getArg('{', '}');
1202 if (options.empty() && name.find(',')) {
1203 vector<string> vecnames;
1204 split(name, vecnames, ',');
1205 vector<string>::const_iterator it = vecnames.begin();
1206 vector<string>::const_iterator end = vecnames.end();
1207 for (; it != end; ++it)
1208 handle_package(trim(*it), string());
1210 handle_package(name, options);
1214 else if (t.cs() == "def") {
1215 string name = p.getToken().cs();
1216 while (p.nextToken().cat() != catBegin)
1217 name += p.getToken().asString();
1218 handle_ert(os, "\\def\\" + name + '{' + p.verbatimItem() + '}');
1221 else if (t.cs() == "par")
1224 else if (is_known(t.cs(), known_headings)) {
1225 string name = t.cs();
1226 if (p.nextToken().asInput() == "*") {
1230 os << "\n\n\\layout " << cap(name) << "\n\n";
1231 string opt = p.getOpt();
1233 begin_inset(os, "OptArg\n");
1234 os << "collapsed true\n\n\\layout Standard\n\n" << opt;
1237 parse(p, os, FLAG_ITEM, mode, outer);
1238 os << "\n\n\\layout Standard\n\n";
1241 else if (t.cs() == "includegraphics") {
1242 if (mode == TEXT_MODE) {
1243 map<string, string> opts = split_map(p.getArg('[', ']'));
1244 string name = p.verbatimItem();
1245 begin_inset(os, "Graphics ");
1246 os << "\n\tfilename " << name << '\n';
1247 if (opts.find("width") != opts.end())
1248 os << "\twidth " << opts["width"] << '\n';
1249 if (opts.find("height") != opts.end())
1250 os << "\theight " << opts["height"] << '\n';
1253 os << "\\includegraphics ";
1257 else if (t.cs() == "footnote") {
1258 begin_inset(os, "Foot\n");
1259 os << "collapsed true\n\n\\layout Standard\n\n";
1260 parse(p, os, FLAG_ITEM, mode, false);
1264 else if (t.cs() == "makeindex" || t.cs() == "maketitle")
1267 else if (t.cs() == "tableofcontents")
1268 p.verbatimItem(); // swallow this
1270 else if (t.cs() == "hline" && mode == TABLE_MODE)
1271 hlines += "\\hline";
1273 else if (t.cs() == "cline" && mode == TABLE_MODE)
1274 hlines += "\\cline{" + p.verbatimItem() + '}';
1276 else if (t.cs() == "tiny" && mode == TEXT_MODE)
1277 os << "\n\\size tiny\n";
1279 else if (t.cs() == "scriptsize" && mode == TEXT_MODE)
1280 os << "\n\\size scriptsize\n";
1282 else if (t.cs() == "Large" && mode == TEXT_MODE)
1283 os << "\n\\size larger\n";
1285 else if (t.cs() == "textrm") {
1286 if (mode == TEXT_MODE) {
1287 os << "\n\\family roman\n";
1288 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1289 os << "\n\\family default\n";
1291 os << '\\' << t.cs() << '{';
1292 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1297 else if (t.cs() == "textsf") {
1298 if (mode == TEXT_MODE) {
1299 os << "\n\\family sans\n";
1300 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1301 os << "\n\\family default\n";
1303 os << '\\' << t.cs() << '{';
1304 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1309 else if (t.cs() == "texttt") {
1310 if (mode == TEXT_MODE) {
1311 os << "\n\\family typewriter\n";
1312 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1313 os << "\n\\family default\n";
1315 os << '\\' << t.cs() << '{';
1316 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1321 else if (t.cs() == "textsc") {
1322 if (mode == TEXT_MODE) {
1323 os << "\n\\noun on\n";
1324 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1325 os << "\n\\noun default\n";
1327 os << '\\' << t.cs() << '{';
1328 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1333 else if (t.cs() == "textbf") {
1334 if (mode == TEXT_MODE) {
1335 os << "\n\\series bold\n";
1336 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1337 os << "\n\\series default\n";
1339 os << '\\' << t.cs() << '{';
1340 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1345 else if (t.cs() == "underbar") {
1346 if (mode == TEXT_MODE) {
1347 os << "\n\\bar under\n";
1348 parse(p, os, FLAG_ITEM, TEXT_MODE, outer);
1349 os << "\n\\bar default\n";
1351 os << '\\' << t.cs() << '{';
1352 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1357 else if ((t.cs() == "emph" || t.cs() == "noun") && mode == TEXT_MODE) {
1358 os << "\n\\" << t.cs() << " on\n";
1359 parse(p, os, FLAG_ITEM, mode, outer);
1360 os << "\n\\" << t.cs() << " default\n";
1363 else if (t.cs() == "mbox" && mode != TEXT_MODE) {
1365 parse(p, os, FLAG_ITEM, MATHTEXT_MODE, outer);
1369 else if (is_known(t.cs(), known_latex_commands) && mode == TEXT_MODE) {
1370 begin_inset(os, "LatexCommand ");
1371 os << '\\' << t.cs();
1374 os << '{' << p.verbatimItem() << '}';
1378 else if (t.cs() == "bibitem") {
1379 os << "\n\\layout Bibliography\n\\bibitem ";
1381 os << '{' << p.verbatimItem() << '}' << "\n\n";
1384 else if (mode == TEXT_MODE && is_known(t.cs(), known_quotes)) {
1385 char const ** where = is_known(t.cs(), known_quotes);
1386 begin_inset(os, "Quotes ");
1387 os << known_coded_quotes[where - known_quotes];
1391 else if (t.cs() == "LyX" && mode == TEXT_MODE) {
1392 p.verbatimItem(); // eat {}
1396 else if (t.cs() == "TeX" && mode == TEXT_MODE) {
1397 p.verbatimItem(); // eat {}
1401 else if (t.cs() == "LaTeX" && mode == TEXT_MODE) {
1402 p.verbatimItem(); // eat {}
1406 else if (t.cs() == "LaTeXe" && mode == TEXT_MODE) {
1407 p.verbatimItem(); // eat {}
1411 else if (t.cs() == "lyxarrow" && mode == TEXT_MODE) {
1413 os << "\\SpecialChar \\menuseparator\n";
1416 else if (t.cs() == "ldots" && mode == TEXT_MODE) {
1418 os << "\\SpecialChar \\ldots{}\n";
1421 else if (t.cs() == "@" && mode == TEXT_MODE)
1422 os << "\\SpecialChar \\@";
1424 else if (t.cs() == "textasciitilde" && mode == TEXT_MODE)
1427 else if (t.cs() == "_" && mode == TEXT_MODE)
1430 else if (t.cs() == "&" && mode == TEXT_MODE)
1433 else if (t.cs() == "#" && mode == TEXT_MODE)
1436 else if (t.cs() == "\"") {
1437 string const name = p.verbatimItem();
1438 if (name == "a") os << 'ä';
1439 else if (name == "o") os << 'ö';
1440 else if (name == "u") os << 'ü';
1441 else if (name == "A") os << 'Ä';
1442 else if (name == "O") os << 'Ö';
1443 else if (name == "U") os << 'Ü';
1444 else handle_ert(os, "\"{" + name + "}");
1447 else if (t.cs() == "ss")
1450 else if (t.cs() == "input")
1451 handle_ert(os, "\\input{" + p.verbatimItem() + "}\n");
1453 else if (t.cs() == "fancyhead") {
1455 ss << "\\fancyhead";
1457 ss << '{' << p.verbatimItem() << "}\n";
1458 handle_ert(os, ss.str());
1462 //cerr << "#: " << t << " mode: " << mode << endl;
1463 if (mode == TEXT_MODE) {
1464 // heuristic: read up to next non-nested space
1466 string s = t.asInput();
1467 string z = p.verbatimItem();
1468 while (p.good() && z != " " && z.size()) {
1469 //cerr << "read: " << z << endl;
1471 z = p.verbatimItem();
1473 cerr << "found ERT: " << s << endl;
1474 handle_ert(os, s + ' ');
1476 handle_ert(os, t.asInput() + ' ');
1479 //cerr << "#: writing: '" << t.asInput() << "'\n";
1483 if (flags & FLAG_LEAVE) {
1484 flags &= ~FLAG_LEAVE;
1492 } // anonymous namespace
1495 int main(int argc, char * argv[])
1498 cerr << "Usage: " << argv[0] << " <infile.tex>" << endl;
1502 ifstream is(argv[1]);
1504 parse_preamble(p, cout);
1505 active_environments.push("document");
1506 parse(p, cout, FLAG_END, TEXT_MODE, true);
1507 cout << "\n\\the_end";