]> git.lyx.org Git - lyx.git/blobdiff - src/tex2lyx/text.cpp
Fix bug #7895: Do not output lines with only a single space.
[lyx.git] / src / tex2lyx / text.cpp
index 15d5df2c2e65d6ea6b6726ef702597aa79f817a7..bf3ea8c2bd4c0e032473cca8ff84d4a42ba62833 100644 (file)
@@ -19,6 +19,7 @@
 #include "Context.h"
 #include "Encoding.h"
 #include "FloatList.h"
+#include "LaTeXPackages.h"
 #include "Layout.h"
 #include "Length.h"
 #include "Preamble.h"
@@ -28,6 +29,7 @@
 #include "support/FileName.h"
 #include "support/filetools.h"
 #include "support/lstrings.h"
+#include "support/lyxtime.h"
 
 #include <algorithm>
 #include <iostream>
@@ -672,10 +674,14 @@ void parse_arguments(string const & command,
        for (size_t i = 0; i < no_arguments; ++i) {
                switch (template_arguments[i]) {
                case required:
+               case req_group:
                        // This argument contains regular LaTeX
                        handle_ert(os, ert + '{', context);
                        eat_whitespace(p, os, context, false);
-                       parse_text(p, os, FLAG_ITEM, outer, context);
+                       if (template_arguments[i] == required)
+                               parse_text(p, os, FLAG_ITEM, outer, context);
+                       else
+                               parse_text_snippet(p, os, FLAG_ITEM, outer, context);
                        ert = "}";
                        break;
                case item:
@@ -687,11 +693,13 @@ void parse_arguments(string const & command,
                        else
                                ert += p.verbatim_item();
                        break;
+               case displaymath:
                case verbatim:
                        // This argument may contain special characters
                        ert += '{' + p.verbatim_item() + '}';
                        break;
                case optional:
+               case opt_group:
                        // true because we must not eat whitespace
                        // if an optional arg follows we must not strip the
                        // brackets from this one
@@ -739,16 +747,29 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
        string height_unit = "in";
        string height_special = "totalheight";
        string latex_height;
+       string width_value;
+       string width_unit;
+       string latex_width;
+       string width_special = "none";
        if (!inner_type.empty() && p.hasOpt()) {
-               position = p.getArg('[', ']');
+               if (inner_type != "makebox")
+                       position = p.getArg('[', ']');
+               else {
+                       latex_width = p.getArg('[', ']');
+                       translate_box_len(latex_width, width_value, width_unit, width_special);
+                       position = "t";
+               }
                if (position != "t" && position != "c" && position != "b") {
                        cerr << "invalid position " << position << " for "
                             << inner_type << endl;
                        position = "c";
                }
                if (p.hasOpt()) {
-                       latex_height = p.getArg('[', ']');
-                       translate_box_len(latex_height, height_value, height_unit, height_special);
+                       if (inner_type != "makebox") {
+                               latex_height = p.getArg('[', ']');
+                               translate_box_len(latex_height, height_value, height_unit, height_special);
+                       } else
+                               hor_pos = p.getArg('[', ']');
 
                        if (p.hasOpt()) {
                                inner_pos = p.getArg('[', ']');
@@ -762,12 +783,9 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                        }
                }
        }
-       string width_value;
-       string width_unit;
-       string latex_width;
        if (inner_type.empty()) {
-               if (special.empty())
-                       latex_width = "\\columnwidth";
+               if (special.empty() && outer_type != "framebox")
+                       latex_width = "1\\columnwidth";
                else {
                        Parser p2(special);
                        latex_width = p2.getArg('[', ']');
@@ -782,9 +800,34 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                                }
                        }
                }
-       } else
+       } else if (inner_type != "makebox")
                latex_width = p.verbatim_item();
+       // if e.g. only \ovalbox{content} was used, set the width to 1\columnwidth
+       // as this is LyX's standard for such cases (except for makebox)
+       // \framebox is more special and handled below
+       if (latex_width.empty() && inner_type != "makebox"
+               && outer_type != "framebox")
+               latex_width = "1\\columnwidth";
+
        translate_len(latex_width, width_value, width_unit);
+
+       bool shadedparbox = false;
+       if (inner_type == "shaded") {
+               eat_whitespace(p, os, parent_context, false);
+               if (outer_type == "parbox") {
+                       // Eat '{'
+                       if (p.next_token().cat() == catBegin)
+                               p.get_token();
+                       eat_whitespace(p, os, parent_context, false);
+                       shadedparbox = true;
+               }
+               p.get_token();
+               p.getArg('{', '}');
+       }
+       // If we already read the inner box we have to push the inner env
+       if (!outer_type.empty() && !inner_type.empty() &&
+           (inner_flags & FLAG_END))
+               active_environments.push_back(inner_type);
        // LyX can't handle length variables
        bool use_ert = contains(width_unit, '\\') || contains(height_unit, '\\');
        if (!use_ert && !outer_type.empty() && !inner_type.empty()) {
@@ -797,14 +840,25 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                else
                        p.verbatim_item();
                p.skip_spaces(true);
-               if ((outer_type == "framed" && p.next_token().asInput() != "\\end") ||
-                   (outer_type != "framed" && p.next_token().cat() != catEnd)) {
+               bool const outer_env(outer_type == "framed" || outer_type == "minipage");
+               if ((outer_env && p.next_token().asInput() != "\\end") ||
+                   (!outer_env && p.next_token().cat() != catEnd)) {
                        // something is between the end of the inner box and
                        // the end of the outer box, so we need to use ERT.
                        use_ert = true;
                }
                p.popPosition();
        }
+       // if only \makebox{content} was used we can set its width to 1\width
+       // because this identic and also identic to \mbox
+       // this doesn't work for \framebox{content}, thus we have to use ERT for this
+       if (latex_width.empty() && inner_type == "makebox") {
+               width_value = "1";
+               width_unit = "in";
+               width_special = "width";
+       } else if (latex_width.empty() && outer_type == "framebox") {
+               use_ert = true;
+       }
        if (use_ert) {
                ostringstream ss;
                if (!outer_type.empty()) {
@@ -817,10 +871,12 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                        }
                }
                if (!inner_type.empty()) {
-                       if (inner_flags & FLAG_END)
-                               ss << "\\begin{" << inner_type << '}';
-                       else
-                               ss << '\\' << inner_type;
+                       if (inner_type != "shaded") {
+                               if (inner_flags & FLAG_END)
+                                       ss << "\\begin{" << inner_type << '}';
+                               else
+                                       ss << '\\' << inner_type;
+                       }
                        if (!position.empty())
                                ss << '[' << position << ']';
                        if (!latex_height.empty())
@@ -831,6 +887,8 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                        if (!(inner_flags & FLAG_END))
                                ss << '{';
                }
+               if (inner_type == "shaded")
+                       ss << "\\begin{shaded}";
                handle_ert(os, ss.str(), parent_context);
                if (!inner_type.empty()) {
                        parse_text(p, os, inner_flags, outer, parent_context);
@@ -841,10 +899,24 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                                handle_ert(os, "}", parent_context);
                }
                if (!outer_type.empty()) {
+                       // If we already read the inner box we have to pop
+                       // the inner env
+                       if (!inner_type.empty() && (inner_flags & FLAG_END))
+                               active_environments.pop_back();
+       
+                       // Ensure that the end of the outer box is parsed correctly:
+                       // The opening brace has been eaten by parse_outer_box()
+                       if (!outer_type.empty() && (outer_flags & FLAG_ITEM)) {
+                               outer_flags &= ~FLAG_ITEM;
+                               outer_flags |= FLAG_BRACE_LAST;
+                       }
                        parse_text(p, os, outer_flags, outer, parent_context);
                        if (outer_flags & FLAG_END)
                                handle_ert(os, "\\end{" + outer_type + '}',
                                           parent_context);
+                       else if (inner_type.empty() && outer_type == "framebox")
+                               // in this case it is already closed later
+                               ;
                        else
                                handle_ert(os, "}", parent_context);
                }
@@ -863,7 +935,9 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                        os << "Boxed\n";
                else if (outer_type == "shadowbox")
                        os << "Shadowbox\n";
-               else if (outer_type == "shaded") {
+               else if ((outer_type == "shaded" && inner_type.empty()) ||
+                            (outer_type == "minipage" && inner_type == "shaded") ||
+                            (outer_type == "parbox" && inner_type == "shaded")) {
                        os << "Shaded\n";
                        preamble.registerAutomaticallyLoadedPackage("color");
                } else if (outer_type == "doublebox")
@@ -876,19 +950,29 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                os << "hor_pos \"" << hor_pos << "\"\n";
                os << "has_inner_box " << !inner_type.empty() << "\n";
                os << "inner_pos \"" << inner_pos << "\"\n";
-               os << "use_parbox " << (inner_type == "parbox") << '\n';
-               os << "use_makebox 0\n";
+               os << "use_parbox " << (inner_type == "parbox" || shadedparbox)
+                  << '\n';
+               os << "use_makebox " << (inner_type == "makebox") << '\n';
                os << "width \"" << width_value << width_unit << "\"\n";
-               os << "special \"none\"\n";
+               os << "special \"" << width_special << "\"\n";
                os << "height \"" << height_value << height_unit << "\"\n";
                os << "height_special \"" << height_special << "\"\n";
                os << "status open\n\n";
-               Context context(true, parent_context.textclass);
-               context.font = parent_context.font;
 
-               // FIXME, the inset layout should be plain, not standard, see bug #7846
+               // Unfortunately we can't use parse_text_in_inset:
+               // InsetBox::forcePlainLayout() is hard coded and does not
+               // use the inset layout. Apart from that do we call parse_text
+               // up to two times, but need only one check_end_layout.
+               bool const forcePlainLayout =
+                       (!inner_type.empty() || inner_type == "makebox") &&
+                       outer_type != "shaded" && outer_type != "framed";
+               Context context(true, parent_context.textclass);
+               if (forcePlainLayout)
+                       context.layout = &context.textclass.plainLayout();
+               else
+                       context.font = parent_context.font;
 
-               // If we have no inner box the contens will be read with the outer box
+               // If we have no inner box the contents will be read with the outer box
                if (!inner_type.empty())
                        parse_text(p, os, inner_flags, outer, context);
 
@@ -902,6 +986,10 @@ void parse_box(Parser & p, ostream & os, unsigned outer_flags,
                // Find end of outer box, output contents if inner_type is
                // empty and output possible comments
                if (!outer_type.empty()) {
+                       // If we already read the inner box we have to pop
+                       // the inner env
+                       if (!inner_type.empty() && (inner_flags & FLAG_END))
+                               active_environments.pop_back();
                        // This does not output anything but comments if
                        // inner_type is not empty (see use_ert)
                        parse_text(p, os, outer_flags, outer, context);
@@ -949,6 +1037,22 @@ void parse_outer_box(Parser & p, ostream & os, unsigned flags, bool outer,
        }
        string inner;
        unsigned int inner_flags = 0;
+       p.pushPosition();
+       if (outer_type == "minipage" || outer_type == "parbox") {
+               p.skip_spaces(true);
+               while (p.hasOpt()) {
+                       p.getArg('[', ']');
+                       p.skip_spaces(true);
+               }
+               p.getArg('{', '}');
+               p.skip_spaces(true);
+               if (outer_type == "parbox") {
+                       // Eat '{'
+                       if (p.next_token().cat() == catBegin)
+                               p.get_token();
+                       p.skip_spaces(true);
+               }
+       }
        if (outer_type == "shaded") {
                // These boxes never have an inner box
                ;
@@ -956,25 +1060,31 @@ void parse_outer_box(Parser & p, ostream & os, unsigned flags, bool outer,
                inner = p.get_token().cs();
                inner_flags = FLAG_ITEM;
        } else if (p.next_token().asInput() == "\\begin") {
-               // Is this a minipage?
+               // Is this a minipage or shaded box?
                p.pushPosition();
                p.get_token();
                inner = p.getArg('{', '}');
                p.popPosition();
-               if (inner == "minipage") {
-                       p.get_token();
-                       p.getArg('{', '}');
-                       eat_whitespace(p, os, parent_context, false);
+               if (inner == "minipage" || inner == "shaded")
                        inner_flags = FLAG_END;
-               else
+               else
                        inner = "";
        }
+       p.popPosition();
        if (inner_flags == FLAG_END) {
-               active_environments.push_back(inner);
+               if (inner != "shaded")
+               {
+                       p.get_token();
+                       p.getArg('{', '}');
+                       eat_whitespace(p, os, parent_context, false);
+               }
                parse_box(p, os, flags, FLAG_END, outer, parent_context,
                          outer_type, special, inner);
-               active_environments.pop_back();
        } else {
+               if (inner_flags == FLAG_ITEM) {
+                       p.get_token();
+                       eat_whitespace(p, os, parent_context, false);
+               }
                parse_box(p, os, flags, inner_flags, outer, parent_context,
                          outer_type, special, inner);
        }
@@ -1053,18 +1163,31 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                parse_math(p, os, FLAG_END, MATH_MODE);
                os << "\\end{" << name << "}";
                end_inset(os);
+               if (is_display_math_env(name)) {
+                       // Prevent the conversion of a line break to a space
+                       // (bug 7668). This does not change the output, but
+                       // looks ugly in LyX.
+                       eat_whitespace(p, os, parent_context, false);
+               }
        }
 
-       else if (name == "tabular" || name == "longtable") {
+       else if (unstarred_name == "tabular" || name == "longtable") {
                eat_whitespace(p, os, parent_context, false);
+               string width = "0pt";
+               if (name == "tabular*") {
+                       width = lyx::translate_len(p.getArg('{', '}'));
+                       eat_whitespace(p, os, parent_context, false);
+               }
                parent_context.check_layout(os);
                begin_inset(os, "Tabular ");
-               handle_tabular(p, os, name == "longtable", parent_context);
+               handle_tabular(p, os, name, width, parent_context);
                end_inset(os);
                p.skip_spaces();
        }
 
        else if (parent_context.textclass.floats().typeExist(unstarred_name)) {
+               eat_whitespace(p, os, parent_context, false);
+               string const opt = p.hasOpt() ? p.getArg('[', ']') : string();
                eat_whitespace(p, os, parent_context, false);
                parent_context.check_layout(os);
                begin_inset(os, "Float " + unstarred_name + "\n");
@@ -1076,8 +1199,8 @@ void parse_environment(Parser & p, ostream & os, bool outer,
                        float_type = unstarred_name;
                else
                        float_type = "";
-               if (p.hasOpt())
-                       os << "placement " << p.getArg('[', ']') << '\n';
+               if (!opt.empty())
+                       os << "placement " << opt << '\n';
                os << "wide " << convert<string>(is_starred)
                   << "\nsideways false"
                   << "\nstatus open\n\n";
@@ -1144,7 +1267,29 @@ void parse_environment(Parser & p, ostream & os, bool outer,
 
        else if (name == "minipage") {
                eat_whitespace(p, os, parent_context, false);
-               parse_box(p, os, 0, FLAG_END, outer, parent_context, "", "", name);
+               // Test whether this is an outer box of a shaded box
+               p.pushPosition();
+               // swallow arguments
+               while (p.hasOpt()) {
+                       p.getArg('[', ']');
+                       p.skip_spaces(true);
+               }
+               p.getArg('{', '}');
+               p.skip_spaces(true);
+               Token t = p.get_token();
+               bool shaded = false;
+               if (t.asInput() == "\\begin") {
+                       p.skip_spaces(true);
+                       if (p.getArg('{', '}') == "shaded")
+                               shaded = true;
+               }
+               p.popPosition();
+               if (shaded)
+                       parse_outer_box(p, os, FLAG_END, outer,
+                                       parent_context, name, "shaded");
+               else
+                       parse_box(p, os, 0, FLAG_END, outer, parent_context,
+                                 "", "", name);
                p.skip_spaces();
        }
 
@@ -1769,7 +1914,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        context.check_layout(os);
                        begin_inset(os, "Formula ");
                        Token const & n = p.get_token();
-                       if (n.cat() == catMath && outer) {
+                       bool const display(n.cat() == catMath && outer);
+                       if (display) {
                                // TeX's $$...$$ syntax for displayed math
                                os << "\\[";
                                parse_math(p, os, FLAG_SIMPLE, MATH_MODE);
@@ -1783,6 +1929,12 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                os << '$';
                        }
                        end_inset(os);
+                       if (display) {
+                               // Prevent the conversion of a line break to a
+                               // space (bug 7668). This does not change the
+                               // output, but looks ugly in LyX.
+                               eat_whitespace(p, os, context, false);
+                       }
                }
 
                else if (t.cat() == catSuper || t.cat() == catSub)
@@ -2029,6 +2181,10 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        parse_math(p, os, FLAG_EQUATION, MATH_MODE);
                        os << "\\]";
                        end_inset(os);
+                       // Prevent the conversion of a line break to a space
+                       // (bug 7668). This does not change the output, but
+                       // looks ugly in LyX.
+                       eat_whitespace(p, os, context, false);
                }
 
                else if (t.cs() == "begin")
@@ -2048,16 +2204,15 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                }
 
                else if (t.cs() == "item") {
-                       p.skip_spaces();
                        string s;
-                       bool optarg = false;
-                       if (p.next_token().cat() != catEscape &&
-                           p.next_token().character() == '[') {
-                               p.get_token(); // eat '['
-                               s = parse_text_snippet(p, FLAG_BRACK_LAST,
-                                                      outer, context);
-                               optarg = true;
-                       }
+                       bool const optarg = p.hasOpt();
+                       if (optarg) {
+                               // FIXME: This swallows comments, but we cannot use
+                               //        eat_whitespace() since we must not output
+                               //        anything before the item.
+                               s = p.getArg('[', ']');
+                       } else
+                               p.skip_spaces(false);
                        context.set_item();
                        context.check_layout(os);
                        if (context.has_item) {
@@ -2073,13 +2228,30 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                                if (context.layout->labeltype != LABEL_MANUAL) {
                                        // LyX does not support \item[\mybullet]
                                        // in itemize environments
-                                       handle_ert(os, "[", context);
-                                       os << s;
-                                       handle_ert(os, "]", context);
+                                       Parser p2(s + ']');
+                                       os << parse_text_snippet(p2,
+                                               FLAG_BRACK_LAST, outer, context);
                                } else if (!s.empty()) {
+                                       // LyX adds braces around the argument,
+                                       // so we need to remove them here.
+                                       if (s.size() > 2 && s[0] == '{' &&
+                                           s[s.size()-1] == '}')
+                                               s = s.substr(1, s.size()-2);
+                                       // If the argument contains a space we
+                                       // must put it into ERT: Otherwise LyX
+                                       // would misinterpret the space as
+                                       // item delimiter (bug 7663)
+                                       if (contains(s, ' ')) {
+                                               handle_ert(os, s, context);
+                                       } else {
+                                               Parser p2(s + ']');
+                                               os << parse_text_snippet(p2,
+                                                       FLAG_BRACK_LAST,
+                                                       outer, context);
+                                       }
                                        // The space is needed to separate the
                                        // item from the rest of the sentence.
-                                       os << s << ' ';
+                                       os << ' ';
                                        eat_whitespace(p, os, context, false);
                                }
                        }
@@ -2591,6 +2763,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        parse_text_snippet(p, os, FLAG_ITEM, outer, context);
                        context.check_layout(os);
                        os << "\n\\bar default\n";
+                       preamble.registerAutomaticallyLoadedPackage("ulem");
                }
 
                else if (t.cs() == "sout") {
@@ -2599,6 +2772,7 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        parse_text_snippet(p, os, FLAG_ITEM, outer, context);
                        context.check_layout(os);
                        os << "\n\\strikeout default\n";
+                       preamble.registerAutomaticallyLoadedPackage("ulem");
                }
 
                else if (t.cs() == "uuline" || t.cs() == "uwave" ||
@@ -2608,9 +2782,54 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        parse_text_snippet(p, os, FLAG_ITEM, outer, context);
                        context.check_layout(os);
                        os << "\n\\" << t.cs() << " default\n";
+                       if (t.cs() == "uuline" || t.cs() == "uwave")
+                               preamble.registerAutomaticallyLoadedPackage("ulem");
+               }
+
+               else if (t.cs() == "lyxadded" || t.cs() == "lyxdeleted") {
+                       context.check_layout(os);
+                       string name = p.getArg('{', '}');
+                       string localtime = p.getArg('{', '}');
+                       preamble.registerAuthor(name);
+                       Author const & author = preamble.getAuthor(name);
+                       // from_ctime() will fail if LyX decides to output the
+                       // time in the text language. It might also use a wrong
+                       // time zone (if the original LyX document was exported
+                       // with a different time zone).
+                       time_t ptime = from_ctime(localtime);
+                       if (ptime == static_cast<time_t>(-1)) {
+                               cerr << "Warning: Could not parse time `" << localtime
+                                    << "ยด for change tracking, using current time instead.\n";
+                               ptime = current_time();
+                       }
+                       if (t.cs() == "lyxadded")
+                               os << "\n\\change_inserted ";
+                       else
+                               os << "\n\\change_deleted ";
+                       os << author.bufferId() << ' ' << ptime << '\n';
+                       parse_text_snippet(p, os, FLAG_ITEM, outer, context);
+                       bool dvipost    = LaTeXPackages::isAvailable("dvipost");
+                       bool xcolorulem = LaTeXPackages::isAvailable("ulem") &&
+                                         LaTeXPackages::isAvailable("xcolor");
+                       // No need to test for luatex, since luatex comes in
+                       // two flavours (dvi and pdf), like latex, and those
+                       // are detected by pdflatex.
+                       if (pdflatex || xetex) {
+                               if (xcolorulem) {
+                                       preamble.registerAutomaticallyLoadedPackage("ulem");
+                                       preamble.registerAutomaticallyLoadedPackage("xcolor");
+                                       preamble.registerAutomaticallyLoadedPackage("pdfcolmk");
+                               }
+                       } else {
+                               if (dvipost) {
+                                       preamble.registerAutomaticallyLoadedPackage("dvipost");
+                               } else if (xcolorulem) {
+                                       preamble.registerAutomaticallyLoadedPackage("ulem");
+                                       preamble.registerAutomaticallyLoadedPackage("xcolor");
+                               }
+                       }
                }
 
-               // FIXME, the inset layout should be plain, not standard, see bug #7846
                else if (t.cs() == "phantom" || t.cs() == "hphantom" ||
                             t.cs() == "vphantom") {
                        context.check_layout(os);
@@ -2621,7 +2840,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        if (t.cs() == "vphantom")
                                begin_inset(os, "Phantom VPhantom\n");
                        os << "status open\n";
-                       parse_text_in_inset(p, os, FLAG_ITEM, outer, context);
+                       parse_text_in_inset(p, os, FLAG_ITEM, outer, context,
+                                           "Phantom");
                        end_inset(os);
                }
 
@@ -2918,6 +3138,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        os << t.cs().substr(4) << '\n';
                        parse_text_in_inset(p, os, FLAG_ITEM, false, context);
                        end_inset(os);
+                       if (t.cs() == "textsubscript")
+                               preamble.registerAutomaticallyLoadedPackage("subscript");
                }
 
                else if (is_known(t.cs(), known_quotes)) {
@@ -3343,8 +3565,35 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                        end_inset(os);
                }
 
-               else if (t.cs() == "parbox")
-                       parse_box(p, os, 0, FLAG_ITEM, outer, context, "", "", t.cs());
+               else if (t.cs() == "parbox") {
+                       // Test whether this is an outer box of a shaded box
+                       p.pushPosition();
+                       // swallow arguments
+                       while (p.hasOpt()) {
+                               p.getArg('[', ']');
+                               p.skip_spaces(true);
+                       }
+                       p.getArg('{', '}');
+                       p.skip_spaces(true);
+                       // eat the '{'
+                       if (p.next_token().cat() == catBegin)
+                               p.get_token();
+                       p.skip_spaces(true);
+                       Token to = p.get_token();
+                       bool shaded = false;
+                       if (to.asInput() == "\\begin") {
+                               p.skip_spaces(true);
+                               if (p.getArg('{', '}') == "shaded")
+                                       shaded = true;
+                       }
+                       p.popPosition();
+                       if (shaded) {
+                               parse_outer_box(p, os, FLAG_ITEM, outer,
+                                               context, "parbox", "shaded");
+                       } else
+                               parse_box(p, os, 0, FLAG_ITEM, outer, context,
+                                         "", "", t.cs());
+               }
 
                else if (t.cs() == "ovalbox" || t.cs() == "Ovalbox" ||
                         t.cs() == "shadowbox" || t.cs() == "doublebox")
@@ -3357,16 +3606,22 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
                }
 
                //\makebox() is part of the picture environment and different from \makebox{}
-               //\makebox{} will be parsed by parse_box when bug 2956 is fixed
+               //\makebox{} will be parsed by parse_box
                else if (t.cs() == "makebox") {
                        string arg = t.asInput();
-                       if (p.next_token().character() == '(')
+                       if (p.next_token().character() == '(') {
                                //the syntax is: \makebox(x,y)[position]{content}
                                arg += p.getFullParentheseArg();
-                       else
-                               //the syntax is: \makebox[width][position]{content}
                                arg += p.getFullOpt();
-                       handle_ert(os, arg + p.getFullOpt(), context);
+                               eat_whitespace(p, os, context, false);
+                               handle_ert(os, arg + '{', context);
+                               eat_whitespace(p, os, context, false);
+                               parse_text(p, os, FLAG_ITEM, outer, context);
+                               handle_ert(os, "}", context);
+                       } else
+                               //the syntax is: \makebox[width][position]{content}
+                               parse_box(p, os, 0, FLAG_ITEM, outer, context,
+                                         "", "", t.cs());
                }
 
                else if (t.cs() == "smallskip" ||