Exemple #1
0
    def append_local_layout(self, new_layout):
        " Append `new_layout` to the local layouts."
        # new_layout may be a string or a list of strings (lines)
        try:
            new_layout = new_layout.splitlines()
        except AttributeError:
            pass
        i = find_token(self.header, "\\begin_local_layout", 0)
        if i == -1:
            k = find_token(self.header, "\\language", 0)
            if k == -1:
                # this should not happen
                self.warning(
                    "Malformed LyX document! No \\language header found!")
                return
            self.header[k:k] = ["\\begin_local_layout", "\\end_local_layout"]
            i = k

        j = find_end_of(self.header, i, "\\begin_local_layout",
                        "\\end_local_layout")
        if j == -1:
            # this should not happen
            self.warning(
                "Malformed LyX document: Can't find end of local layout!")
            return

        self.header[i + 1:i + 1] = new_layout
Exemple #2
0
def convert_beamer_article_styles(document):
    " Remove included (scr)article styles in beamer article "

    beamer_articles = ["article-beamer", "scrarticle-beamer"]
    if document.textclass not in beamer_articles:
        return

    while True:
        i = find_token(document.header, "\\begin_local_layout", 0)
        if i == -1:
            return

        j = find_end_of(document.header, i, "\\begin_local_layout",
                        "\\end_local_layout")
        if j == -1:
            # this should not happen
            break

        k = find_token(
            document.header,
            "### Inserted by lyx2lyx (more [scr]article styles) ###", i, j)
        if k != -1:
            l = find_token(
                document.header,
                "### End of insertion by lyx2lyx (more [scr]article styles) ###",
                i, j)
            if l == -1:
                # this should not happen
                document.warning(
                    "End of lyx2lyx local layout insertion not found!")
                break

            document.header[k:l + 1] = []

        return
Exemple #3
0
    def get_toc(self, depth = 4):
        " Returns the TOC of this LyX document."
        paragraphs_filter = {'Title' : 0,'Chapter' : 1, 'Section' : 2,
                             'Subsection' : 3, 'Subsubsection': 4}
        allowed_insets = ['Quotes']
        allowed_parameters = ('\\paragraph_spacing', '\\noindent',
                              '\\align', '\\labelwidthstring',
                              "\\start_of_appendix", "\\leftindent")
        sections = []
        for section in paragraphs_filter.keys():
            sections.append('\\begin_layout %s' % section)

        toc_par = []
        i = 0
        while 1:
            i = find_tokens(self.body, sections, i)
            if i == -1:
                break

            j = find_end_of(self.body,  i + 1, '\\begin_layout', '\\end_layout')
            if j == -1:
                self.warning('Incomplete file.', 0)
                break

            section = self.body[i].split()[1]
            if section[-1] == '*':
                section = section[:-1]

            par = []

            k = i + 1
            # skip paragraph parameters
            while not self.body[k].strip() or self.body[k].split()[0] \
                      in allowed_parameters:
                k += 1

            while k < j:
                if check_token(self.body[k], '\\begin_inset'):
                    inset = self.body[k].split()[1]
                    end = find_end_of_inset(self.body, k)
                    if end == -1 or end > j:
                        self.warning('Malformed file.', 0)

                    if inset in allowed_insets:
                        par.extend(self.body[k: end+1])
                    k = end + 1
                else:
                    par.append(self.body[k])
                    k += 1

            # trim empty lines in the end.
            while par and par[-1].strip() == '':
                par.pop()

            toc_par.append(Paragraph(section, par))

            i = j + 1

        return toc_par
Exemple #4
0
def revert_beamer_article_styles(document):
    " Include (scr)article styles in beamer article "

    beamer_articles = ["article-beamer", "scrarticle-beamer"]
    if document.textclass not in beamer_articles:
        return

    inclusion = "article.layout"
    if document.textclass == "scrarticle-beamer":
        inclusion = "scrartcl.layout"

    while True:
        i = find_token(document.header, "\\begin_local_layout", 0)
        if i == -1:
            k = find_token(document.header, "\\language", 0)
            if k == -1:
                # this should not happen
                document.warning(
                    "Malformed LyX document! No \\language header found!")
                break
            document.header[k - 1:k - 1] = [
                "\\begin_local_layout", "\\end_local_layout"
            ]
            i = find_token(document.header, "\\begin_local_layout", 0)
        if i != -1:
            j = find_end_of(document.header, i, "\\begin_local_layout",
                            "\\end_local_layout")
            if j == -1:
                # this should not happen
                break

            document.header[i + 1:i + 1] = [
                "### Inserted by lyx2lyx (more [scr]article styles) ###",
                "Input " + inclusion, "Input beamer.layout",
                "Provides geometry 0", "Provides hyperref 0", "DefaultFont",
                "	Family                Roman",
                "	Series                Medium", "	Shape                 Up",
                "	Size                  Normal", "	Color                 None",
                "EndFont", "Preamble", "	\\usepackage{beamerarticle,pgf}",
                "	% this default might be overridden by plain title style",
                "	\\newcommand\makebeamertitle{\\frame{\\maketitle}}%",
                "	\\AtBeginDocument{",
                "		\\let\\origtableofcontents=\\tableofcontents",
                "		\\def\\tableofcontents{\\@ifnextchar[{\\origtableofcontents}{\\gobbletableofcontents}}",
                "		\\def\\gobbletableofcontents#1{\\origtableofcontents}",
                "	}", "EndPreamble",
                "### End of insertion by lyx2lyx (more [scr]article styles) ###"
            ]
        return
Exemple #5
0
 def _lyxml2xml(self, start, end):
     lines = self.lines
     xout = self.xout
     i = start
     while i < end:
         self.dbg(3, 'Parsing line %d: %s' % (i, lines[i]))
         if lines[i].startswith('</'):
             xout.end_elt(lines[i][2:-2])
             i += 1
         elif lines[i].startswith('<'):
             (tag, attrs, start_tok, end_tok) = _parse_xml_tag(lines[i])
             # There don't seem to be tags with no child nodes in .lyx,
             # but let's handle them just in case.
             if lines[i][-2] != '/':
                 e = find_end_of(lines, i, start_tok, end_tok)
                 self.dbg(
                     3, 'find_end_of(%d, %s, %s) = %d' %
                     (i, start_tok, end_tok, e))
                 # lyxtabular's <column> and <features> don't get closed!
                 # What a mess.
                 if e == -1:
                     e = None
             else:
                 e = None
             xout.start_elt(tag)
             xout.attr('embedded_xml', 'true')
             for a in attrs:
                 xout.attr(a[0], a[1])
             if e:
                 i = self._lyxml2xml(i + 1, e)
                 self.dbg(3, '_lyx2xml(...) = %d, looking for %d' % (i, e))
                 if i + 1 == e:
                     i += 1
                 else:
                     self.dbg(3, '_lyx2xml() returned %d, e = %d' % (i, e))
             else:
                 xout.end_elt(tag)
                 i += 1
         else:
             i = self._lyx2xml(i, end)
     return i
Exemple #6
0
 def _lyxml2xml(self, start, end):
     lines = self.lines
     xout = self.xout
     i = start
     while i < end:
         self.dbg(3, "Parsing line {0:d}: {1!s}".format(i, lines[i]))
         if lines[i].startswith("</"):
             xout.end_elt(lines[i][2:-2])
             i += 1
         elif lines[i].startswith("<"):
             (tag, attrs, start_tok, end_tok) = _parse_xml_tag(lines[i])
             # There don't seem to be tags with no child nodes in .lyx,
             # but let's handle them just in case.
             if lines[i][-2] != "/":
                 e = find_end_of(lines, i, start_tok, end_tok)
                 self.dbg(3, "find_end_of({0:d}, {1!s}, {2!s}) = {3:d}".format(i, start_tok, end_tok, e))
                 # lyxtabular's <column> and <features> don't get closed!
                 # What a mess.
                 if e == -1:
                     e = None
             else:
                 e = None
             xout.start_elt(tag)
             xout.attr("embedded_xml", "true")
             for a in attrs:
                 xout.attr(a[0], a[1])
             if e:
                 i = self._lyxml2xml(i + 1, e)
                 self.dbg(3, "_lyx2xml(...) = {0:d}, looking for {1:d}".format(i, e))
                 if i + 1 == e:
                     i += 1
                 else:
                     self.dbg(3, "_lyx2xml() returned {0:d}, e = {1:d}".format(i, e))
             else:
                 xout.end_elt(tag)
                 i += 1
         else:
             i = self._lyx2xml(i, end)
     return i
Exemple #7
0
 def _lyxml2xml(self, start, end):
     lines = self.lines
     xout = self.xout
     i = start
     while i < end:
         self.dbg(3, 'Parsing line %d: %s' % (i, lines[i]))
         if lines[i].startswith('</'):
             xout.end_elt(lines[i][2:-2])
             i += 1
         elif lines[i].startswith('<'):
             (tag, attrs, start_tok, end_tok) = _parse_xml_tag(lines[i])
             # There don't seem to be tags with no child nodes in .lyx,
             # but let's handle them just in case.
             if lines[i][-2] != '/':
                 e = find_end_of(lines, i, start_tok, end_tok)
                 self.dbg(3, 'find_end_of(%d, %s, %s) = %d' % (i, start_tok, end_tok, e))
                 # lyxtabular's <column> and <features> don't get closed!
                 # What a mess.
                 if e == -1:
                     e = None
             else:
                 e = None
             xout.start_elt(tag)
             xout.attr('embedded_xml', 'true')
             for a in attrs:
                 xout.attr(a[0], a[1])
             if e:
                 i = self._lyxml2xml(i + 1, e)
                 self.dbg(3, '_lyx2xml(...) = %d, looking for %d' % (i, e))
                 if i + 1 == e:
                     i += 1
                 else:
                     self.dbg(3, '_lyx2xml() returned %d, e = %d' % (i, e))
             else:
                 xout.end_elt(tag)
                 i += 1
         else:
             i = self._lyx2xml(i, end)
     return i
Exemple #8
0
    def append_local_layout(self, new_layout):
        " Append `new_layout` to the local layouts."
        # new_layout may be a string or a list of strings (lines)
        try:
            new_layout = new_layout.splitlines()
        except AttributeError:
            pass
        i = find_token(self.header, "\\begin_local_layout", 0)
        if i == -1:
            k = find_token(self.header, "\\language", 0)
            if k == -1:
                # this should not happen
                self.warning("Malformed LyX document! No \\language header found!")
                return
            self.header[k-1 : k-1] = ["\\begin_local_layout", "\\end_local_layout"]
            i = k - 1

        j = find_end_of(self.header, i, "\\begin_local_layout", "\\end_local_layout")
        if j == -1:
            # this should not happen
            self.warning("Malformed LyX document: Can't find end of local layout!")
            return

        self.header[i+1 : i+1] = new_layout
Exemple #9
0
 def _lyx2xml(self, start=0, end=-1, cmd_type=None):
     lines = self.lines
     xout = self.xout
     i = start
     if end < 0:
         end = len(lines)
     prev_i = -1
     while i < end:
         self.dbg(3, 'Parsing line %d: %s' % (i, lines[i]))
         assert i > prev_i
         prev_i = i
         if len(lines[i]) == 0 or lines[i] == ' ':
             # Ignore empty lines
             i += 1
             cmd_type = None
         elif lines[i][0] == '#':
             # LyX source comment
             #xout.comment(lines[i][1:])
             i += 1
             cmd_type = None
         elif _beginner(lines[i]):
             (el, start_tok, end_tok, cmd_type,
              rest) = _parse_begin(lines[i])
             xout.start_elt(el)
             if el == 'document':
                 # Default XML namespace
                 xout.attr('xmlns', 'urn:cryptonector.com:lyx-other')
                 # We put layouts, insets, and custome insets into
                 # separate namespaces
                 xout.attr('xmlns:layout',
                           'urn:cryptonector.com:lyx-layout')
                 xout.attr('xmlns:inset', 'urn:cryptonector.com:lyx-inset')
                 xout.attr('xmlns:flex', 'urn:cryptonector.com:lyx-flex')
                 # XXX MathML not yet implemented; we get to turn LyX
                 # formulas into MathML, joy!  But it looks like
                 # LaTeX math, and there's tools for converting that
                 # to MathML, so, hey, it might not be too much work.
                 xout.attr('xmlns:math',
                           'http://www.w3.org/1998/Math/MathML')
                 # Suck in \lyxformat, which precedes \begin_document,
                 # and anything else that might be there (though
                 # nothing will be).
                 self._handle_interspersed_attrs(0, i)
             if (el.startswith('inset:') or el.startswith('flex:')) and \
                not cmd_type and (i + 1) < end and \
                lines[i + 1].startswith('status '):
                 i += 1  # skip status open|collapsed line
                 status = _chomp(lines[i][lines[i].find(' ') + 1:])
             else:
                 status = None
             self.dbg(4, 'lines[%d] = %s' % (i, lines[i]))
             e = find_end_of(lines, i, start_tok, end_tok)
             assert e != -1
             self.dbg(
                 4, 'find_end_of(%d, %s, %s) = %d' %
                 (i, start_tok, end_tok, e))
             # XXX Here we need to find any attributes that might be
             # interspersed with child nodes so we can suck them in
             # first.  What a PITA.
             self._handle_interspersed_attrs(i + 1, e - 1)
             if status:
                 xout.attr('status', status)
             if len(rest) == 2 and el != 'inset:Formula':
                 xout.attr(rest[0], escape(rest[1]))
             i = self._lyx2xml(i + 1, e, cmd_type)
             self.dbg(
                 4, '_lyx2xml(...) = %d, looking for %s at %d; end = %d' %
                 (i, end_tok, e, end))
             if i + 1 == e:
                 i += 1
             else:
                 self.dbg(
                     4, '_lyx2xml() returned %d, e = %d; end = %d' %
                     (i, e, end))
             assert lines[i].startswith('\\end_')
             if len(rest) == 2 and el == 'inset:Formula':
                 xout.text(' ')
                 xout.text(escape(rest[1]))
             xout.end_elt(el)
             cmd_type = None
             i += 1
         elif cmd_type == 'inset':
             # Parse "\begin_inset CommandInset ..." attributes
             self.dbg(4, 'lines[%d] = %s' % (i, lines[i]))
             while i < end and lines[i] != '' and lines[i] != ' ':
                 (a, v) = _parse_attr(lines[i])
                 if not a or not v:
                     break
                 xout.attr(a, v)
                 i += 1
             # then suck in content
             cmd_type = None
         elif cmd_type == 'XML':
             # Parse embedded XML contents
             i = self._lyxml2xml(i, end)
             cmd_type = None
         elif lines[i][0] == '\\' and \
            not lines[i].startswith('\\begin_') and \
            not lines[i].startswith('\\end_') and \
            not _key(lines[i]) in mixed_tags:
             # An attribute, which we've handled above with the call to
             # _handle_interspersed_attrs().
             i += 1
         else:
             line = lines[i]
             if xout.stack[-1] == 'layout':
                 line = _chomp(line)
             key = _key(line)
             if key in mixed_tags:
                 val = _chomp(lines[i][lines[i].find(' ') + 1:])
                 if val == mixed_tags[key]:
                     xout.end_elt(key)
                 else:
                     xout.start_elt(key)
                     xout.attr('type', val)
             else:
                 xout.text(escape(line))
             cmd_type = None
             i += 1
     return i
Exemple #10
0
def find_end_of_inset(lines, i):
    " Find beginning of inset, where lines[i] is included."
    return find_end_of(lines, i, "\\begin_inset", "\\end_inset")
Exemple #11
0
def find_end_of_inset(lines, i):
    "Finds the matching \end_inset"
    return find_end_of(lines, i, "\\begin_inset", "\\end_inset")
Exemple #12
0
def find_end_of_inset(lines, i):
    " Find beginning of inset, where lines[i] is included."
    return find_end_of(lines, i, "\\begin_inset", "\\end_inset")
Exemple #13
0
def find_end_of_tabular(lines, i):
    " Finds the matching end of tabular."
    return find_end_of(lines, i, "<lyxtabular", "</lyxtabular")
Exemple #14
0
def find_end_of_inset(lines, i):
    " Finds the matching \end_inset"
    return find_end_of(lines, i, "\\begin_inset", "\\end_inset")
Exemple #15
0
def find_end_of_tabular(lines, i):
    " Finds the matching end of tabular."
    return find_end_of(lines, i, "<lyxtabular", "</lyxtabular")
Exemple #16
0
 def _lyx2xml(self, start=0, end=-1, cmd_type=None):
     lines = self.lines
     xout = self.xout
     i = start
     if end < 0:
         end = len(lines)
     prev_i = -1
     while i < end:
         self.dbg(3, 'Parsing line %d: %s' % (i, lines[i]))
         assert i > prev_i
         prev_i = i
         if len(lines[i]) == 0 or lines[i] == ' ':
             # Ignore empty lines
             i += 1
             cmd_type = None
         elif lines[i][0] == '#':
             # LyX source comment
             #xout.comment(lines[i][1:])
             i += 1
             cmd_type = None
         elif _beginner(lines[i]):
             (el, start_tok, end_tok, cmd_type, rest) = _parse_begin(lines[i])
             xout.start_elt(el)
             if el == 'document':
                 # Default XML namespace
                 xout.attr('xmlns', 'urn:cryptonector.com:lyx-other')
                 # We put layouts, insets, and custome insets into
                 # separate namespaces
                 xout.attr('xmlns:layout', 'urn:cryptonector.com:lyx-layout')
                 xout.attr('xmlns:inset', 'urn:cryptonector.com:lyx-inset')
                 xout.attr('xmlns:flex', 'urn:cryptonector.com:lyx-flex')
                 # XXX MathML not yet implemented; we get to turn LyX
                 # formulas into MathML, joy!  But it looks like
                 # LaTeX math, and there's tools for converting that
                 # to MathML, so, hey, it might not be too much work.
                 xout.attr('xmlns:math', 'http://www.w3.org/1998/Math/MathML')
                 # Suck in \lyxformat, which precedes \begin_document,
                 # and anything else that might be there (though
                 # nothing will be).
                 self._handle_interspersed_attrs(0, i)
             if (el.startswith('inset:') or el.startswith('flex:')) and \
                not cmd_type and (i + 1) < end and \
                lines[i + 1].startswith('status '):
                 i += 1 # skip status open|collapsed line
                 status = _chomp(lines[i][lines[i].find(' ') + 1:])
             else:
                 status = None
             self.dbg(4, 'lines[%d] = %s' % (i, lines[i]))
             e = find_end_of(lines, i, start_tok, end_tok)
             assert e != -1
             self.dbg(4, 'find_end_of(%d, %s, %s) = %d' % (i, start_tok, end_tok, e))
             # XXX Here we need to find any attributes that might be
             # interspersed with child nodes so we can suck them in
             # first.  What a PITA.
             self._handle_interspersed_attrs(i + 1, e - 1)
             if status:
                 xout.attr('status', status)
             if len(rest) == 2 and el != 'inset:Formula':
                 xout.attr(rest[0], escape(rest[1]))
             i = self._lyx2xml(i + 1, e, cmd_type)
             self.dbg(4, '_lyx2xml(...) = %d, looking for %s at %d; end = %d' % (i, end_tok, e, end))
             if i + 1 == e:
                 i += 1
             else:
                 self.dbg(4, '_lyx2xml() returned %d, e = %d; end = %d' % (i, e, end))
             assert lines[i].startswith('\\end_')
             if len(rest) == 2 and el == 'inset:Formula':
                 xout.text(' ')
                 xout.text(escape(rest[1]))
             xout.end_elt(el)
             cmd_type = None
             i += 1
         elif cmd_type == 'inset':
             # Parse "\begin_inset CommandInset ..." attributes
             self.dbg(4, 'lines[%d] = %s' % (i, lines[i]))
             while i < end and lines[i] != '' and lines[i] != ' ':
                 (a, v) = _parse_attr(lines[i])
                 if not a or not v:
                     break
                 xout.attr(a, v)
                 i += 1
             # then suck in content
             cmd_type = None
         elif cmd_type == 'XML':
             # Parse embedded XML contents
             i = self._lyxml2xml(i, end)
             cmd_type = None
         elif lines[i][0] == '\\' and \
            not lines[i].startswith('\\begin_') and \
            not lines[i].startswith('\\end_') and \
            not _key(lines[i]) in mixed_tags:
             # An attribute, which we've handled above with the call to
             # _handle_interspersed_attrs().
             i += 1
         else:
             line = lines[i]
             if xout.stack[-1] == 'layout':
                 line = _chomp(line)
             key = _key(line)
             if key in mixed_tags:
                 val = _chomp(lines[i][lines[i].find(' ') + 1:])
                 if val == mixed_tags[key]:
                     xout.end_elt(key)
                 else:
                     xout.start_elt(key)
                     xout.attr('type', val)
             else:
                 xout.text(escape(line))
             cmd_type = None
             i += 1
     return i
Exemple #17
0
 def _lyx2xml(self, start=0, end=-1, cmd_type=None):
     lines = self.lines
     xout = self.xout
     i = start
     if end < 0:
         end = len(lines)
     prev_i = -1
     while i < end:
         self.dbg(3, "Parsing line {0:d}: {1!s}".format(i, lines[i]))
         assert i > prev_i
         prev_i = i
         if len(lines[i]) == 0 or lines[i] == " ":
             # Ignore empty lines
             i += 1
             cmd_type = None
         elif lines[i][0] == "#":
             # LyX source comment
             # xout.comment(lines[i][1:])
             i += 1
             cmd_type = None
         elif _beginner(lines[i]):
             (el, start_tok, end_tok, cmd_type, rest) = _parse_begin(lines[i])
             xout.start_elt(el)
             if el == "document":
                 # Default XML namespace
                 xout.attr("xmlns", "urn:cryptonector.com:lyx-other")
                 # We put layouts, insets, and custome insets into
                 # separate namespaces
                 xout.attr("xmlns:layout", "urn:cryptonector.com:lyx-layout")
                 xout.attr("xmlns:inset", "urn:cryptonector.com:lyx-inset")
                 xout.attr("xmlns:flex", "urn:cryptonector.com:lyx-flex")
                 # XXX MathML not yet implemented; we get to turn LyX
                 # formulas into MathML, joy!  But it looks like
                 # LaTeX math, and there's tools for converting that
                 # to MathML, so, hey, it might not be too much work.
                 xout.attr("xmlns:math", "http://www.w3.org/1998/Math/MathML")
                 # Suck in \lyxformat, which precedes \begin_document,
                 # and anything else that might be there (though
                 # nothing will be).
                 self._handle_interspersed_attrs(0, i)
             if (
                 (el.startswith("inset:") or el.startswith("flex:"))
                 and not cmd_type
                 and (i + 1) < end
                 and lines[i + 1].startswith("status ")
             ):
                 i += 1  # skip status open|collapsed line
                 status = _chomp(lines[i][lines[i].find(" ") + 1 :])
             else:
                 status = None
             self.dbg(4, "lines[{0:d}] = {1!s}".format(i, lines[i]))
             e = find_end_of(lines, i, start_tok, end_tok)
             assert e != -1
             self.dbg(4, "find_end_of({0:d}, {1!s}, {2!s}) = {3:d}".format(i, start_tok, end_tok, e))
             # XXX Here we need to find any attributes that might be
             # interspersed with child nodes so we can suck them in
             # first.  What a PITA.
             self._handle_interspersed_attrs(i + 1, e - 1)
             if status:
                 xout.attr("status", status)
             if len(rest) == 2 and el != "inset:Formula":
                 xout.attr(rest[0], escape(rest[1]))
             i = self._lyx2xml(i + 1, e, cmd_type)
             self.dbg(4, "_lyx2xml(...) = {0:d}, looking for {1!s} at {2:d}; end = {3:d}".format(i, end_tok, e, end))
             if i + 1 == e:
                 i += 1
             else:
                 self.dbg(4, "_lyx2xml() returned {0:d}, e = {1:d}; end = {2:d}".format(i, e, end))
             assert lines[i].startswith("\\end_")
             if len(rest) == 2 and el == "inset:Formula":
                 xout.text(" ")
                 xout.text(escape(rest[1]))
             xout.end_elt(el)
             cmd_type = None
             i += 1
         elif cmd_type == "inset":
             # Parse "\begin_inset CommandInset ..." attributes
             self.dbg(4, "lines[{0:d}] = {1!s}".format(i, lines[i]))
             while i < end and lines[i] != "" and lines[i] != " ":
                 (a, v) = _parse_attr(lines[i])
                 if not a or not v:
                     break
                 xout.attr(a, v)
                 i += 1
             # then suck in content
             cmd_type = None
         elif cmd_type == "XML":
             # Parse embedded XML contents
             i = self._lyxml2xml(i, end)
             cmd_type = None
         elif (
             lines[i][0] == "\\"
             and not lines[i].startswith("\\begin_")
             and not lines[i].startswith("\\end_")
             and not _key(lines[i]) in mixed_tags
         ):
             # An attribute, which we've handled above with the call to
             # _handle_interspersed_attrs().
             i += 1
         else:
             line = lines[i]
             if xout.stack[-1] == "layout":
                 line = _chomp(line)
             key = _key(line)
             if key in mixed_tags:
                 val = _chomp(lines[i][lines[i].find(" ") + 1 :])
                 if val == mixed_tags[key]:
                     xout.end_elt(key)
                 else:
                     xout.start_elt(key)
                     xout.attr("type", val)
             else:
                 xout.text(escape(line))
             cmd_type = None
             i += 1
     return i