]> git.lyx.org Git - lyx.git/blobdiff - lib/lyx2lyx/lyx_2_0.py
Don't use widest label for numerical citations.
[lyx.git] / lib / lyx2lyx / lyx_2_0.py
index f5204464061da85052763cd40a9144ec9338f6e3..2756418f88d09afb0671878977800b6a8f525584 100644 (file)
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 # This file is part of lyx2lyx
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
 # This file is part of lyx2lyx
 # -*- coding: utf-8 -*-
-# Copyright (C) 2008 José Matos  <jamatos@lyx.org>
+# Copyright (C) 2011 The LyX team
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
 #
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
 #
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
 """ Convert files to the file format generated by lyx 2.0"""
 
 import re, string
 import unicodedata
 import sys, os
 
 """ Convert files to the file format generated by lyx 2.0"""
 
 import re, string
 import unicodedata
 import sys, os
-import lyx2lyx_version
 
 
-from parser_tools import find_token, find_end_of, find_tokens, get_value, get_value_string
+from parser_tools import find_token, find_end_of, find_tokens, \
+  find_token_exact, find_end_of_inset, find_end_of_layout, \
+  find_token_backwards, is_in_inset, get_value, get_quoted_value, \
+  del_token, check_token, get_option_value
+  
+from lyx2lyx_tools import add_to_preamble, insert_to_preamble, \
+  put_cmd_in_ert, lyx2latex, latex_length, revert_flex_inset, \
+  revert_font_attrs, hex2ratio, str2bool
 
 ####################################################################
 # Private helper functions
 
 
 ####################################################################
 # Private helper functions
 
-def find_end_of_inset(lines, i):
-    " Find end of inset, where lines[i] is included."
-    return find_end_of(lines, i, "\\begin_inset", "\\end_inset")
-
-
-def add_to_preamble(document, text):
-    """ Add text to the preamble if it is not already there.
-    Only the first line is checked!"""
-
-    if find_token(document.preamble, text[0], 0) != -1:
-        return
-
-    document.preamble.extend(text)
-
-
-def insert_to_preamble(index, document, text):
-    """ Insert text to the preamble at a given line"""
-
-    document.preamble.insert(index, text)
-
-
-def read_unicodesymbols():
-    " Read the unicodesymbols list of unicode characters and corresponding commands."
-    pathname = os.path.abspath(os.path.dirname(sys.argv[0]))
-    fp = open(os.path.join(pathname.strip('lyx2lyx'), 'unicodesymbols'))
-    spec_chars = []
-    # Two backslashes, followed by some non-word character, and then a character
-    # in brackets. The idea is to check for constructs like: \"{u}, which is how
-    # they are written in the unicodesymbols file; but they can also be written
-    # as: \"u or even \" u.
-    r = re.compile(r'\\\\(\W)\{(\w)\}')
-    for line in fp.readlines():
-        if line[0] != '#' and line.strip() != "":
-            line=line.replace(' "',' ') # remove all quotation marks with spaces before
-            line=line.replace('" ',' ') # remove all quotation marks with spaces after
-            line=line.replace(r'\"','"') # replace \" by " (for characters with diaeresis)
-            try:
-                [ucs4,command,dead] = line.split(None,2)
-                if command[0:1] != "\\":
-                    continue
-                spec_chars.append([command, unichr(eval(ucs4))])
-            except:
-                continue
-            m = r.match(command)
-            if m != None:
-                command = "\\\\"
-                # If the character is a double-quote, then we need to escape it, too,
-                # since it is done that way in the LyX file.
-                if m.group(1) == "\"":
-                    command += "\\"
-                commandbl = command
-                command += m.group(1) + m.group(2)
-                commandbl += m.group(1) + ' ' + m.group(2)
-                spec_chars.append([command, unichr(eval(ucs4))])
-                spec_chars.append([commandbl, unichr(eval(ucs4))])
-    fp.close()
-    return spec_chars
-
-
-unicode_reps = read_unicodesymbols()
-
-
-# DO NOT USE THIS ROUTINE ANY MORE. Better yet, replace the uses that
-# have been made of it with uses of put_cmd_in_ert.
-def old_put_cmd_in_ert(string):
-    for rep in unicode_reps:
-        string = string.replace(rep[1], rep[0].replace('\\\\', '\\'))
-    string = string.replace('\\', "\\backslash\n")
-    string = "\\begin_inset ERT\nstatus collapsed\n\\begin_layout Plain Layout\n" \
-      + string + "\n\\end_layout\n\\end_inset"
-    return string
-
-
-# This routine wraps some content in an ERT inset. It returns a 
-# LIST of strings. This is how lyx2lyx works: with a list of strings, 
-# each representing a line of a LyX file. Embedded newlines confuse
-# lyx2lyx very much.
-# For this same reason, we expect as input a LIST of strings, not
-# something with embedded newlines. That said, if any of your strings
-# do have embedded newlines, the string will eventually get split on
-# them and you'll get a list back.
-#
-# A call to this routine will often go something like this:
-#   i = find_token('\\begin_inset FunkyInset', ...)
-#   ...
-#   j = find_end_of_inset(document.body, i)
-#   content = ...extract content from insets
-#   ert = put_cmd_in_ert(content)
-#   document.body[i:j] = ert
-# Now, before we continue, we need to reset i appropriately. Normally,
-# this would be: 
-#   i += len(ert)
-# That puts us right after the ERT we just inserted.
-def put_cmd_in_ert(strlist):
-    ret = ["\\begin_inset ERT", "status collapsed", "\\begin_layout Plain Layout\n"]
-    # Despite the warnings just given, it will be faster for us to work
-    # with a single string internally. That way, we only go through the
-    # unicode_reps loop once.
-    s = "\n".join(strlist)
-    for rep in unicode_reps:
-        s = s.replace(rep[1], rep[0].replace('\\\\', '\\'))
-    s = s.replace('\\', "\\backslash\n")
-    ret += s.splitlines()
-    ret += ["\\end_layout", "\\end_inset"]
-    return ret
-
-            
-def lyx2latex(document, lines):
-    'Convert some LyX stuff into corresponding LaTeX stuff, as best we can.'
-    # clean up multiline stuff
-    content = ""
-    ert_end = 0
-
-    for curline in range(len(lines)):
-      line = lines[curline]
-      if line.startswith("\\begin_inset ERT"):
-          # We don't want to replace things inside ERT, so figure out
-          # where the end of the inset is.
-          ert_end = find_end_of_inset(lines, curline + 1)
-          continue
-      elif line.startswith("\\begin_inset Formula"):
-          line = line[20:]
-      elif line.startswith("\\begin_inset Quotes"):
-          # For now, we do a very basic reversion. Someone who understands
-          # quotes is welcome to fix it up.
-          qtype = line[20:].strip()
-          # lang = qtype[0]
-          side = qtype[1]
-          dbls = qtype[2]
-          if side == "l":
-              if dbls == "d":
-                  line = "``"
-              else:
-                  line = "`"
-          else:
-              if dbls == "d":
-                  line = "''"
-              else:
-                  line = "'"
-      elif line.isspace() or \
-            line.startswith("\\begin_layout") or \
-            line.startswith("\\end_layout") or \
-            line.startswith("\\begin_inset") or \
-            line.startswith("\\end_inset") or \
-            line.startswith("\\lang") or \
-            line.strip() == "status collapsed" or \
-            line.strip() == "status open":
-          #skip all that stuff
-          continue
-
-      # this needs to be added to the preamble because of cases like
-      # \textmu, \textbackslash, etc.
-      add_to_preamble(document, ['% added by lyx2lyx for converted index entries',
-                                 '\\@ifundefined{textmu}',
-                                 ' {\\usepackage{textcomp}}{}'])
-      # a lossless reversion is not possible
-      # try at least to handle some common insets and settings
-      if ert_end >= curline:
-          line = line.replace(r'\backslash', r'\\')
-      else:
-          line = line.replace('&', '\\&{}')
-          line = line.replace('#', '\\#{}')
-          line = line.replace('^', '\\^{}')
-          line = line.replace('%', '\\%{}')
-          line = line.replace('_', '\\_{}')
-          line = line.replace('$', '\\${}')
-
-          # Do the LyX text --> LaTeX conversion
-          for rep in unicode_reps:
-            line = line.replace(rep[1], rep[0] + "{}")
-          line = line.replace(r'\backslash', r'\textbackslash{}')
-          line = line.replace(r'\series bold', r'\bfseries{}').replace(r'\series default', r'\mdseries{}')
-          line = line.replace(r'\shape italic', r'\itshape{}').replace(r'\shape smallcaps', r'\scshape{}')
-          line = line.replace(r'\shape slanted', r'\slshape{}').replace(r'\shape default', r'\upshape{}')
-          line = line.replace(r'\emph on', r'\em{}').replace(r'\emph default', r'\em{}')
-          line = line.replace(r'\noun on', r'\scshape{}').replace(r'\noun default', r'\upshape{}')
-          line = line.replace(r'\bar under', r'\underbar{').replace(r'\bar default', r'}')
-          line = line.replace(r'\family sans', r'\sffamily{}').replace(r'\family default', r'\normalfont{}')
-          line = line.replace(r'\family typewriter', r'\ttfamily{}').replace(r'\family roman', r'\rmfamily{}')
-          line = line.replace(r'\InsetSpace ', r'').replace(r'\SpecialChar ', r'')
-      content += line
-    return content
-
-
-def latex_length(string):
-    'Convert lengths to their LaTeX representation.'
-    i = 0
-    percent = False
-    # the string has the form
-    # ValueUnit+ValueUnit-ValueUnit or
-    # ValueUnit+-ValueUnit
-    # the + and - (glue lengths) are optional
-    # the + always precedes the -
-
-    # Convert relative lengths to LaTeX units
-    units = {"text%":"\\textwidth", "col%":"\\columnwidth",
-             "page%":"\\pagewidth", "line%":"\\linewidth",
-             "theight%":"\\textheight", "pheight%":"\\pageheight"}
-    for unit in units.keys():
-        i = string.find(unit)
-        if i != -1:
-            percent = True
-            minus = string.rfind("-", 1, i)
-            plus = string.rfind("+", 0, i)
-            latex_unit = units[unit]
-            if plus == -1 and minus == -1:
-                value = string[:i]
-                value = str(float(value)/100)
-                end = string[i + len(unit):]
-                string = value + latex_unit + end
-            if plus > minus:
-                value = string[plus+1:i]
-                value = str(float(value)/100)
-                begin = string[:plus+1]
-                end = string[i+len(unit):]
-                string = begin + value + latex_unit + end
-            if plus < minus:
-                value = string[minus+1:i]
-                value = str(float(value)/100)
-                begin = string[:minus+1]
-                string = begin + value + latex_unit
-
-    # replace + and -, but only if the - is not the first character
-    string = string[0] + string[1:].replace("+", " plus ").replace("-", " minus ")
-    # handle the case where "+-1mm" was used, because LaTeX only understands
-    # "plus 1mm minus 1mm"
-    if string.find("plus  minus"):
-        lastvaluepos = string.rfind(" ")
-        lastvalue = string[lastvaluepos:]
-        string = string.replace("  ", lastvalue + " ")
-    if percent ==  False:
-        return "False," + string
-    else:
-        return "True," + string
-        
+def remove_option(lines, m, option):
+    ''' removes option from line m. returns whether we did anything '''
+    l = lines[m].find(option)
+    if l == -1:
+        return False
+    val = lines[m][l:].split('"')[1]
+    lines[m] = lines[m][:l - 1] + lines[m][l+len(option + '="' + val + '"'):]
+    return True
 
 
-####################################################################
 
 
+###############################################################################
+###
+### Conversion and reversion routines
+###
+###############################################################################
 
 def revert_swiss(document):
     " Set language german-ch to ngerman "
 
 def revert_swiss(document):
     " Set language german-ch to ngerman "
@@ -289,284 +72,249 @@ def revert_tabularvalign(document):
    " Revert the tabular valign option "
    i = 0
    while True:
    " Revert the tabular valign option "
    i = 0
    while True:
-       i = find_token(document.body, "\\begin_inset Tabular", i)
-       if i == -1:
-           return
-       j = find_token(document.body, "</cell>", i)
-       if j == -1:
-           document.warning("Malformed LyX document: Could not find end of tabular cell.")
-           i = j
-           continue
-       # don't set a box for longtables, only delete tabularvalignment
-       # the alignment is 2 lines below \\begin_inset Tabular
-       p = document.body[i+2].find("islongtable")
-       if p > -1:
-           q = document.body[i+2].find("tabularvalignment")
-           if q > -1:
-               document.body[i+2] = document.body[i+2][:q-1]
-               document.body[i+2] = document.body[i+2] + '>'
-           i = i + 1
-
-       # when no longtable
-       if p == -1:
-         tabularvalignment = 'c'
-         # which valignment is specified?
-         m = document.body[i+2].find('tabularvalignment="top"')
-         if m > -1:
-             tabularvalignment = 't'
-         m = document.body[i+2].find('tabularvalignment="bottom"')
-         if m > -1:
-             tabularvalignment = 'b'
-         # delete tabularvalignment
-         q = document.body[i+2].find("tabularvalignment")
-         if q > -1:
-             document.body[i+2] = document.body[i+2][:q-1]
-             document.body[i+2] = document.body[i+2] + '>'
-
-         # don't add a box when centered
-         if tabularvalignment == 'c':
-             i = j
-             continue
-         subst = ['\\end_layout', '\\end_inset']
-         document.body[j:j] = subst # just inserts those lines
-         subst = ['\\begin_inset Box Frameless',
-             'position "' + tabularvalignment +'"',
-             'hor_pos "c"',
-             'has_inner_box 1',
-             'inner_pos "c"',
-             'use_parbox 0',
-             # we don't know the width, assume 50%
-             'width "50col%"',
-             'special "none"',
-             'height "1in"',
-             'height_special "totalheight"',
-             'status open',
-             '',
-             '\\begin_layout Plain Layout']
-         document.body[i:i] = subst # this just inserts the array at i
-         i += len(subst) + 2 # adjust i to save a few cycles
-
+      i = find_token(document.body, "\\begin_inset Tabular", i)
+      if i == -1:
+          return
+      end = find_end_of_inset(document.body, i)
+      if end == -1:
+          document.warning("Can't find end of inset at line " + str(i))
+          i += 1
+          continue
+      fline = find_token(document.body, "<features", i, end)
+      if fline == -1:
+          document.warning("Can't find features for inset at line " + str(i))
+          i += 1
+          continue
+      p = document.body[fline].find("islongtable")
+      if p != -1:
+          q = document.body[fline].find("tabularvalignment")
+          if q != -1:
+              # FIXME
+              # This seems wrong: It removes everything after 
+              # tabularvalignment, too.
+              document.body[fline] = document.body[fline][:q - 1] + '>'
+          i += 1
+          continue
 
 
-def revert_phantom(document):
+       # no longtable
+      tabularvalignment = 'c'
+      # which valignment is specified?
+      m = document.body[fline].find('tabularvalignment="top"')
+      if m != -1:
+          tabularvalignment = 't'
+      m = document.body[fline].find('tabularvalignment="bottom"')
+      if m != -1:
+          tabularvalignment = 'b'
+      # delete tabularvalignment
+      q = document.body[fline].find("tabularvalignment")
+      if q != -1:
+          # FIXME
+          # This seems wrong: It removes everything after 
+          # tabularvalignment, too.
+          document.body[fline] = document.body[fline][:q - 1] + '>'
+
+      # don't add a box when centered
+      if tabularvalignment == 'c':
+          i = end
+          continue
+      subst = ['\\end_layout', '\\end_inset']
+      document.body[end:end] = subst # just inserts those lines
+      subst = ['\\begin_inset Box Frameless',
+          'position "' + tabularvalignment +'"',
+          'hor_pos "c"',
+          'has_inner_box 1',
+          'inner_pos "c"',
+          'use_parbox 0',
+          # we don't know the width, assume 50%
+          'width "50col%"',
+          'special "none"',
+          'height "1in"',
+          'height_special "totalheight"',
+          'status open',
+          '',
+          '\\begin_layout Plain Layout']
+      document.body[i:i] = subst # this just inserts the array at i
+      # since there could be a tabular inside a tabular, we cannot
+      # jump to end
+      i += len(subst)
+
+
+def revert_phantom_types(document, ptype, cmd):
     " Reverts phantom to ERT "
     i = 0
     " Reverts phantom to ERT "
     i = 0
-    j = 0
     while True:
     while True:
-      i = find_token(document.body, "\\begin_inset Phantom Phantom", i)
+      i = find_token(document.body, "\\begin_inset Phantom " + ptype, i)
       if i == -1:
           return
       if i == -1:
           return
-      substi = document.body[i].replace('\\begin_inset Phantom Phantom', \
-                '\\begin_inset ERT\nstatus collapsed\n\n' \
-                '\\begin_layout Plain Layout\n\n\n\\backslash\n' \
-                'phantom{\n\\end_layout\n\n\\end_inset\n')
-      substi = substi.split('\n')
-      document.body[i : i+4] = substi
-      i += len(substi)
-      j = find_token(document.body, "\\end_layout", i)
-      if j == -1:
-          document.warning("Malformed LyX document: Could not find end of Phantom inset.")
-          return
-      substj = document.body[j].replace('\\end_layout', \
-                '\\size default\n\n\\begin_inset ERT\nstatus collapsed\n\n' \
-                '\\begin_layout Plain Layout\n\n' \
-                '}\n\\end_layout\n\n\\end_inset\n')
-      substj = substj.split('\n')
-      document.body[j : j+4] = substj
-      i += len(substj)
+      end = find_end_of_inset(document.body, i)
+      if end == -1:
+          document.warning("Can't find end of inset at line " + str(i))
+          i += 1
+          continue
+      blay = find_token(document.body, "\\begin_layout Plain Layout", i, end)
+      if blay == -1:
+          document.warning("Can't find layout for inset at line " + str(i))
+          i = end
+          continue
+      bend = find_end_of_layout(document.body, blay)
+      if bend == -1:
+          document.warning("Malformed LyX document: Could not find end of Phantom inset's layout.")
+          i = end
+          continue
+      substi = ["\\begin_inset ERT", "status collapsed", "",
+                "\\begin_layout Plain Layout", "", "", "\\backslash", 
+                cmd + "{", "\\end_layout", "", "\\end_inset"]
+      substj = ["\\size default", "", "\\begin_inset ERT", "status collapsed", "",
+                "\\begin_layout Plain Layout", "", "}", "\\end_layout", "", "\\end_inset"]
+      # do the later one first so as not to mess up the numbering
+      document.body[bend:end + 1] = substj
+      document.body[i:blay + 1] = substi
+      i = end + len(substi) + len(substj) - (end - bend) - (blay - i) - 2
 
 
 
 
+def revert_phantom(document):
+    revert_phantom_types(document, "Phantom", "phantom")
+    
 def revert_hphantom(document):
 def revert_hphantom(document):
-    " Reverts hphantom to ERT "
-    i = 0
-    j = 0
-    while True:
-      i = find_token(document.body, "\\begin_inset Phantom HPhantom", i)
-      if i == -1:
-          return
-      substi = document.body[i].replace('\\begin_inset Phantom HPhantom', \
-                '\\begin_inset ERT\nstatus collapsed\n\n' \
-                '\\begin_layout Plain Layout\n\n\n\\backslash\n' \
-                'hphantom{\n\\end_layout\n\n\\end_inset\n')
-      substi = substi.split('\n')
-      document.body[i : i+4] = substi
-      i += len(substi)
-      j = find_token(document.body, "\\end_layout", i)
-      if j == -1:
-          document.warning("Malformed LyX document: Could not find end of HPhantom inset.")
-          return
-      substj = document.body[j].replace('\\end_layout', \
-                '\\size default\n\n\\begin_inset ERT\nstatus collapsed\n\n' \
-                '\\begin_layout Plain Layout\n\n' \
-                '}\n\\end_layout\n\n\\end_inset\n')
-      substj = substj.split('\n')
-      document.body[j : j+4] = substj
-      i += len(substj)
-
+    revert_phantom_types(document, "HPhantom", "hphantom")
 
 def revert_vphantom(document):
 
 def revert_vphantom(document):
-    " Reverts vphantom to ERT "
-    i = 0
-    j = 0
-    while True:
-      i = find_token(document.body, "\\begin_inset Phantom VPhantom", i)
-      if i == -1:
-          return
-      substi = document.body[i].replace('\\begin_inset Phantom VPhantom', \
-                '\\begin_inset ERT\nstatus collapsed\n\n' \
-                '\\begin_layout Plain Layout\n\n\n\\backslash\n' \
-                'vphantom{\n\\end_layout\n\n\\end_inset\n')
-      substi = substi.split('\n')
-      document.body[i : i+4] = substi
-      i += len(substi)
-      j = find_token(document.body, "\\end_layout", i)
-      if j == -1:
-          document.warning("Malformed LyX document: Could not find end of VPhantom inset.")
-          return
-      substj = document.body[j].replace('\\end_layout', \
-                '\\size default\n\n\\begin_inset ERT\nstatus collapsed\n\n' \
-                '\\begin_layout Plain Layout\n\n' \
-                '}\n\\end_layout\n\n\\end_inset\n')
-      substj = substj.split('\n')
-      document.body[j : j+4] = substj
-      i += len(substj)
+    revert_phantom_types(document, "VPhantom", "vphantom")
 
 
 def revert_xetex(document):
     " Reverts documents that use XeTeX "
 
 
 def revert_xetex(document):
     " Reverts documents that use XeTeX "
+
     i = find_token(document.header, '\\use_xetex', 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\use_xetex.")
         return
     i = find_token(document.header, '\\use_xetex', 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\use_xetex.")
         return
-    if get_value(document.header, "\\use_xetex", i) == 'false':
+    if not str2bool(get_value(document.header, "\\use_xetex", i)):
         del document.header[i]
         return
     del document.header[i]
         del document.header[i]
         return
     del document.header[i]
+
     # 1.) set doc encoding to utf8-plain
     i = find_token(document.header, "\\inputencoding", 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\inputencoding.")
     # 1.) set doc encoding to utf8-plain
     i = find_token(document.header, "\\inputencoding", 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\inputencoding.")
-    document.header[i] = "\\inputencoding utf8-plain"
+    else:
+        document.header[i] = "\\inputencoding utf8-plain"
+
     # 2.) check font settings
     # 2.) check font settings
-    l = find_token(document.header, "\\font_roman", 0)
-    if l == -1:
-        document.warning("Malformed LyX document: Missing \\font_roman.")
-    line = document.header[l]
-    l = re.compile(r'\\font_roman (.*)$')
-    m = l.match(line)
-    roman = m.group(1)
-    l = find_token(document.header, "\\font_sans", 0)
-    if l == -1:
-        document.warning("Malformed LyX document: Missing \\font_sans.")
-    line = document.header[l]
-    l = re.compile(r'\\font_sans (.*)$')
-    m = l.match(line)
-    sans = m.group(1)
-    l = find_token(document.header, "\\font_typewriter", 0)
-    if l == -1:
-        document.warning("Malformed LyX document: Missing \\font_typewriter.")
-    line = document.header[l]
-    l = re.compile(r'\\font_typewriter (.*)$')
-    m = l.match(line)
-    typewriter = m.group(1)
-    osf = get_value(document.header, '\\font_osf', 0) == "true"
-    sf_scale = float(get_value(document.header, '\\font_sf_scale', 0))
-    tt_scale = float(get_value(document.header, '\\font_tt_scale', 0))
-    # 3.) set preamble stuff
-    pretext = '%% This document must be processed with xelatex!\n'
-    pretext += '\\usepackage{fontspec}\n'
-    if roman != "default":
-        pretext += '\\setmainfont[Mapping=tex-text]{' + roman + '}\n'
-    if sans != "default":
-        pretext += '\\setsansfont['
-        if sf_scale != 100:
-            pretext += 'Scale=' + str(sf_scale / 100) + ','
-        pretext += 'Mapping=tex-text]{' + sans + '}\n'
-    if typewriter != "default":
-        pretext += '\\setmonofont'
-        if tt_scale != 100:
-            pretext += '[Scale=' + str(tt_scale / 100) + ']'
-        pretext += '{' + typewriter + '}\n'
-    if osf:
-        pretext += '\\defaultfontfeatures{Numbers=OldStyle}\n'
-    pretext += '\usepackage{xunicode}\n'
-    pretext += '\usepackage{xltxtra}\n'
-    insert_to_preamble(0, document, pretext)
-    # 4.) reset font settings
+    # defaults
+    roman = sans = typew = "default"
+    osf = False
+    sf_scale = tt_scale = 100.0
+    
     i = find_token(document.header, "\\font_roman", 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\font_roman.")
     i = find_token(document.header, "\\font_roman", 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\font_roman.")
-    document.header[i] = "\\font_roman default"
+    else:
+        roman = get_value(document.header, "\\font_roman", i)
+        document.header[i] = "\\font_roman default"
+
     i = find_token(document.header, "\\font_sans", 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\font_sans.")
     i = find_token(document.header, "\\font_sans", 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\font_sans.")
-    document.header[i] = "\\font_sans default"
+    else:
+        sans = get_value(document.header, "\\font_sans", i)
+        document.header[i] = "\\font_sans default"
+    
     i = find_token(document.header, "\\font_typewriter", 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\font_typewriter.")
     i = find_token(document.header, "\\font_typewriter", 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\font_typewriter.")
-    document.header[i] = "\\font_typewriter default"
+    else:
+        typew = get_value(document.header, "\\font_typewriter", i)
+        document.header[i] = "\\font_typewriter default"
+
     i = find_token(document.header, "\\font_osf", 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\font_osf.")
     i = find_token(document.header, "\\font_osf", 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\font_osf.")
-    document.header[i] = "\\font_osf false"
+    else:
+        osf = str2bool(get_value(document.header, "\\font_osf", i))
+        document.header[i] = "\\font_osf false"
+
     i = find_token(document.header, "\\font_sc", 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\font_sc.")
     i = find_token(document.header, "\\font_sc", 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\font_sc.")
-    document.header[i] = "\\font_sc false"
+    else:
+        # we do not need this value.
+        document.header[i] = "\\font_sc false"
+    
     i = find_token(document.header, "\\font_sf_scale", 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\font_sf_scale.")
     i = find_token(document.header, "\\font_sf_scale", 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\font_sf_scale.")
-    document.header[i] = "\\font_sf_scale 100"
+    else:
+      val = get_value(document.header, '\\font_sf_scale', i)
+      try:
+        # float() can throw
+        sf_scale = float(val)
+      except:
+        document.warning("Invalid font_sf_scale value: " + val)
+      document.header[i] = "\\font_sf_scale 100"
+
     i = find_token(document.header, "\\font_tt_scale", 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\font_tt_scale.")
     i = find_token(document.header, "\\font_tt_scale", 0)
     if i == -1:
         document.warning("Malformed LyX document: Missing \\font_tt_scale.")
-    document.header[i] = "\\font_tt_scale 100"
+    else:
+        val = get_value(document.header, '\\font_tt_scale', i)
+        try:
+          # float() can throw
+          tt_scale = float(val)
+        except:
+          document.warning("Invalid font_tt_scale value: " + val)
+        document.header[i] = "\\font_tt_scale 100"
+
+    # 3.) set preamble stuff
+    pretext = ['%% This document must be processed with xelatex!']
+    pretext.append('\\usepackage{fontspec}')
+    if roman != "default":
+        pretext.append('\\setmainfont[Mapping=tex-text]{' + roman + '}')
+    if sans != "default":
+        sf = '\\setsansfont['
+        if sf_scale != 100.0:
+            sf += 'Scale=' + str(sf_scale / 100.0) + ','
+        sf += 'Mapping=tex-text]{' + sans + '}'
+        pretext.append(sf)
+    if typew != "default":
+        tw = '\\setmonofont'
+        if tt_scale != 100.0:
+            tw += '[Scale=' + str(tt_scale / 100.0) + ']'
+        tw += '{' + typew + '}'
+        pretext.append(tw)
+    if osf:
+        pretext.append('\\defaultfontfeatures{Numbers=OldStyle}')
+    pretext.append('\usepackage{xunicode}')
+    pretext.append('\usepackage{xltxtra}')
+    insert_to_preamble(document, pretext)
 
 
 def revert_outputformat(document):
     " Remove default output format param "
 
 
 def revert_outputformat(document):
     " Remove default output format param "
-    i = find_token(document.header, '\\default_output_format', 0)
-    if i == -1:
+    
+    if not del_token(document.header, '\\default_output_format', 0):
         document.warning("Malformed LyX document: Missing \\default_output_format.")
         document.warning("Malformed LyX document: Missing \\default_output_format.")
-        return
-    del document.header[i]
 
 
 def revert_backgroundcolor(document):
     " Reverts background color to preamble code "
 
 
 def revert_backgroundcolor(document):
     " Reverts background color to preamble code "
-    i = 0
-    colorcode = ""
-    while True:
-      i = find_token(document.header, "\\backgroundcolor", i)
-      if i == -1:
-          return
-      colorcode = get_value(document.header, '\\backgroundcolor', 0)
-      del document.header[i]
-      # don't clutter the preamble if backgroundcolor is not set
-      if colorcode == "#ffffff":
-          continue
-      # the color code is in the form #rrggbb where every character denotes a hex number
-      # convert the string to an int
-      red = string.atoi(colorcode[1:3],16)
-      # we want the output "0.5" for the value "127" therefore add here
-      if red != 0:
-          red = red + 1
-      redout = float(red) / 256
-      green = string.atoi(colorcode[3:5],16)
-      if green != 0:
-          green = green + 1
-      greenout = float(green) / 256
-      blue = string.atoi(colorcode[5:7],16)
-      if blue != 0:
-          blue = blue + 1
-      blueout = float(blue) / 256
-      # write the preamble
-      insert_to_preamble(0, document,
-                           '% Commands inserted by lyx2lyx to set the background color\n'
-                           + '\\@ifundefined{definecolor}{\\usepackage{color}}{}\n'
-                           + '\\definecolor{page_backgroundcolor}{rgb}{'
-                           + str(redout) + ', ' + str(greenout)
-                           + ', ' + str(blueout) + '}\n'
-                           + '\\pagecolor{page_backgroundcolor}\n')
+    i = find_token(document.header, "\\backgroundcolor", 0)
+    if i == -1:
+        return
+    colorcode = get_value(document.header, '\\backgroundcolor', i)
+    del document.header[i]
+    # don't clutter the preamble if backgroundcolor is not set
+    if colorcode == "#ffffff":
+        return
+    red   = hex2ratio(colorcode[1:3])
+    green = hex2ratio(colorcode[3:5])
+    blue  = hex2ratio(colorcode[5:7])
+    insert_to_preamble(document, \
+        ['% To set the background color',
+        '\\@ifundefined{definecolor}{\\usepackage{color}}{}',
+        '\\definecolor{page_backgroundcolor}{rgb}{' + red + ',' + green + ',' + blue + '}',
+        '\\pagecolor{page_backgroundcolor}'])
 
 
 def revert_splitindex(document):
 
 
 def revert_splitindex(document):
@@ -575,11 +323,13 @@ def revert_splitindex(document):
     if i == -1:
         document.warning("Malformed LyX document: Missing \\use_indices.")
         return
     if i == -1:
         document.warning("Malformed LyX document: Missing \\use_indices.")
         return
-    indices = get_value(document.header, "\\use_indices", i)
-    preamble = ""
-    if indices == "true":
-         preamble += "\\usepackage{splitidx}\n"
+    useindices = str2bool(get_value(document.header, "\\use_indices", i))
     del document.header[i]
     del document.header[i]
+    preamble = []
+    if useindices:
+         preamble.append("\\usepackage{splitidx})")
+    
+    # deal with index declarations in the preamble
     i = 0
     while True:
         i = find_token(document.header, "\\index", i)
     i = 0
     while True:
         i = find_token(document.header, "\\index", i)
@@ -589,17 +339,20 @@ def revert_splitindex(document):
         if k == -1:
             document.warning("Malformed LyX document: Missing \\end_index.")
             return
         if k == -1:
             document.warning("Malformed LyX document: Missing \\end_index.")
             return
-        line = document.header[i]
-        l = re.compile(r'\\index (.*)$')
-        m = l.match(line)
-        iname = m.group(1)
-        ishortcut = get_value(document.header, '\\shortcut', i, k)
-        if ishortcut != "" and indices == "true":
-            preamble += "\\newindex[" + iname + "]{" + ishortcut + "}\n"
-        del document.header[i:k+1]
-        i = 0
-    if preamble != "":
-        insert_to_preamble(0, document, preamble)
+        if useindices:    
+          line = document.header[i]
+          l = re.compile(r'\\index (.*)$')
+          m = l.match(line)
+          iname = m.group(1)
+          ishortcut = get_value(document.header, '\\shortcut', i, k)
+          if ishortcut != "":
+              preamble.append("\\newindex[" + iname + "]{" + ishortcut + "}")
+        del document.header[i:k + 1]
+    if preamble:
+        insert_to_preamble(document, preamble)
+        
+    # deal with index insets
+    # these need to have the argument removed
     i = 0
     while True:
         i = find_token(document.body, "\\begin_inset Index", i)
     i = 0
     while True:
         i = find_token(document.body, "\\begin_inset Index", i)
@@ -614,28 +367,32 @@ def revert_splitindex(document):
         else:
             k = find_end_of_inset(document.body, i)
             if k == -1:
         else:
             k = find_end_of_inset(document.body, i)
             if k == -1:
-                 return
+                document.warning("Can't find end of index inset!")
+                i += 1
+                continue
             content = lyx2latex(document, document.body[i:k])
             # escape quotes
             content = content.replace('"', r'\"')
             content = lyx2latex(document, document.body[i:k])
             # escape quotes
             content = content.replace('"', r'\"')
-            subst = [old_put_cmd_in_ert("\\sindex[" + itype + "]{" + content + "}")]
-            document.body[i:k+1] = subst
+            subst = put_cmd_in_ert("\\sindex[" + itype + "]{" + content + "}")
+            document.body[i:k + 1] = subst
         i = i + 1
         i = i + 1
+        
+    # deal with index_print insets
     i = 0
     while True:
         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
         if i == -1:
             return
         k = find_end_of_inset(document.body, i)
     i = 0
     while True:
         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
         if i == -1:
             return
         k = find_end_of_inset(document.body, i)
-        ptype = get_value(document.body, 'type', i, k).strip('"')
+        ptype = get_quoted_value(document.body, 'type', i, k)
         if ptype == "idx":
             j = find_token(document.body, "type", i, k)
             del document.body[j]
         if ptype == "idx":
             j = find_token(document.body, "type", i, k)
             del document.body[j]
-        elif indices == "false":
-            del document.body[i:k+1]
+        elif not useindices:
+            del document.body[i:k + 1]
         else:
         else:
-            subst = [old_put_cmd_in_ert("\\printindex[" + ptype + "]{}")]
-            document.body[i:k+1] = subst
+            subst = put_cmd_in_ert("\\printindex[" + ptype + "]{}")
+            document.body[i:k + 1] = subst
         i = i + 1
 
 
         i = i + 1
 
 
@@ -669,7 +426,7 @@ def revert_subindex(document):
     if i == -1:
         document.warning("Malformed LyX document: Missing \\use_indices.")
         return
     if i == -1:
         document.warning("Malformed LyX document: Missing \\use_indices.")
         return
-    indices = get_value(document.header, "\\use_indices", i)
+    useindices = str2bool(get_value(document.header, "\\use_indices", i))
     i = 0
     while True:
         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
     i = 0
     while True:
         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
@@ -678,14 +435,14 @@ def revert_subindex(document):
         k = find_end_of_inset(document.body, i)
         ctype = get_value(document.body, 'LatexCommand', i, k)
         if ctype != "printsubindex":
         k = find_end_of_inset(document.body, i)
         ctype = get_value(document.body, 'LatexCommand', i, k)
         if ctype != "printsubindex":
-            i = i + 1
+            i = k + 1
             continue
             continue
-        ptype = get_value(document.body, 'type', i, k).strip('"')
-        if indices == "false":
-            del document.body[i:k+1]
+        ptype = get_quoted_value(document.body, 'type', i, k)
+        if not useindices:
+            del document.body[i:k + 1]
         else:
         else:
-            subst = [old_put_cmd_in_ert("\\printsubindex[" + ptype + "]{}")]
-            document.body[i:k+1] = subst
+            subst = put_cmd_in_ert("\\printsubindex[" + ptype + "]{}")
+            document.body[i:k + 1] = subst
         i = i + 1
 
 
         i = i + 1
 
 
@@ -695,7 +452,7 @@ def revert_printindexall(document):
     if i == -1:
         document.warning("Malformed LyX document: Missing \\use_indices.")
         return
     if i == -1:
         document.warning("Malformed LyX document: Missing \\use_indices.")
         return
-    indices = get_value(document.header, "\\use_indices", i)
+    useindices = str2bool(get_value(document.header, "\\use_indices", i))
     i = 0
     while True:
         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
     i = 0
     while True:
         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
@@ -704,66 +461,51 @@ def revert_printindexall(document):
         k = find_end_of_inset(document.body, i)
         ctype = get_value(document.body, 'LatexCommand', i, k)
         if ctype != "printindex*" and ctype != "printsubindex*":
         k = find_end_of_inset(document.body, i)
         ctype = get_value(document.body, 'LatexCommand', i, k)
         if ctype != "printindex*" and ctype != "printsubindex*":
-            i = i + 1
+            i = k
             continue
             continue
-        if indices == "false":
-            del document.body[i:k+1]
+        if not useindices:
+            del document.body[i:k + 1]
         else:
         else:
-            subst = [old_put_cmd_in_ert("\\" + ctype + "{}")]
-            document.body[i:k+1] = subst
+            subst = put_cmd_in_ert("\\" + ctype + "{}")
+            document.body[i:k + 1] = subst
         i = i + 1
 
 
 def revert_strikeout(document):
         i = i + 1
 
 
 def revert_strikeout(document):
-    " Reverts \\strikeout character style "
-    while True:
-        i = find_token(document.body, '\\strikeout', 0)
-        if i == -1:
-            return
-        del document.body[i]
-
-
-def revert_uulinewave(document):
-    " Reverts \\uuline, and \\uwave character styles "
-    while True:
-        i = find_token(document.body, '\\uuline', 0)
-        if i == -1:
-            break
-        del document.body[i]
-    while True:
-        i = find_token(document.body, '\\uwave', 0)
-        if i == -1:
-            return
-        del document.body[i]
+  " Reverts \\strikeout font attribute "
+  changed = revert_font_attrs(document.body, "\\uuline", "\\uuline")
+  changed = revert_font_attrs(document.body, "\\uwave", "\\uwave") or changed
+  changed = revert_font_attrs(document.body, "\\strikeout", "\\sout")  or changed
+  if changed == True:
+    insert_to_preamble(document, \
+        ['%  for proper underlining',
+        '\\PassOptionsToPackage{normalem}{ulem}',
+        '\\usepackage{ulem}'])
 
 
 def revert_ulinelatex(document):
 
 
 def revert_ulinelatex(document):
-    " Reverts \\uline character style "
+    " Reverts \\uline font attribute "
     i = find_token(document.body, '\\bar under', 0)
     if i == -1:
         return
     i = find_token(document.body, '\\bar under', 0)
     if i == -1:
         return
-    insert_to_preamble(0, document,
-            '% Commands inserted by lyx2lyx for proper underlining\n'
-            + '\\PassOptionsToPackage{normalem}{ulem}\n'
-            + '\\usepackage{ulem}\n'
-            + '\\let\\cite@rig\\cite\n'
-            + '\\newcommand{\\b@xcite}[2][\\%]{\\def\\def@pt{\\%}\\def\\pas@pt{#1}\n'
-            + '  \\mbox{\\ifx\\def@pt\\pas@pt\\cite@rig{#2}\\else\\cite@rig[#1]{#2}\\fi}}\n'
-            + '\\renewcommand{\\underbar}[1]{{\\let\\cite\\b@xcite\\uline{#1}}}\n')
+    insert_to_preamble(document,\
+            ['%  for proper underlining',
+            '\\PassOptionsToPackage{normalem}{ulem}',
+            '\\usepackage{ulem}',
+            '\\let\\cite@rig\\cite',
+            '\\newcommand{\\b@xcite}[2][\\%]{\\def\\def@pt{\\%}\\def\\pas@pt{#1}',
+            '  \\mbox{\\ifx\\def@pt\\pas@pt\\cite@rig{#2}\\else\\cite@rig[#1]{#2}\\fi}}',
+            '\\renewcommand{\\underbar}[1]{{\\let\\cite\\b@xcite\\uline{#1}}}'])
 
 
 def revert_custom_processors(document):
     " Remove bibtex_command and index_command params "
 
 
 def revert_custom_processors(document):
     " Remove bibtex_command and index_command params "
-    i = find_token(document.header, '\\bibtex_command', 0)
-    if i == -1:
+    
+    if not del_token(document.header, '\\bibtex_command', 0):
         document.warning("Malformed LyX document: Missing \\bibtex_command.")
         document.warning("Malformed LyX document: Missing \\bibtex_command.")
-        return
-    del document.header[i]
-    i = find_token(document.header, '\\index_command', 0)
-    if i == -1:
+    
+    if not del_token(document.header, '\\index_command', 0):
         document.warning("Malformed LyX document: Missing \\index_command.")
         document.warning("Malformed LyX document: Missing \\index_command.")
-        return
-    del document.header[i]
 
 
 def convert_nomencl_width(document):
 
 
 def convert_nomencl_width(document):
@@ -785,13 +527,9 @@ def revert_nomencl_width(document):
       if i == -1:
         break
       j = find_end_of_inset(document.body, i)
       if i == -1:
         break
       j = find_end_of_inset(document.body, i)
-      l = find_token(document.body, "set_width", i, j)
-      if l == -1:
-            document.warning("Can't find set_width option for nomencl_print!")
-            i = j
-            continue
-      del document.body[l]
-      i = i + 1
+      if not del_token(document.body, "set_width", i, j):
+        document.warning("Can't find set_width option for nomencl_print!")
+      i = j
 
 
 def revert_nomencl_cwidth(document):
 
 
 def revert_nomencl_cwidth(document):
@@ -804,41 +542,52 @@ def revert_nomencl_cwidth(document):
       j = find_end_of_inset(document.body, i)
       l = find_token(document.body, "width", i, j)
       if l == -1:
       j = find_end_of_inset(document.body, i)
       l = find_token(document.body, "width", i, j)
       if l == -1:
-            #Can't find width option for nomencl_print
-            i = j
-            continue
-      width = get_value(document.body, "width", i, j).strip('"')
+        document.warning("Can't find width option for nomencl_print!")
+        i = j
+        continue
+      width = get_quoted_value(document.body, "width", i, j)
       del document.body[l]
       del document.body[l]
-      add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
-      add_to_preamble(document, ["\\setlength{\\nomlabelwidth}{" + width + "}"])
-      i = i + 1
+      insert_to_preamble(document, ["\\setlength{\\nomlabelwidth}{" + width + "}"])
+      i = j - 1
 
 
 def revert_applemac(document):
     " Revert applemac encoding to auto "
 
 
 def revert_applemac(document):
     " Revert applemac encoding to auto "
-    i = 0
-    if document.encoding == "applemac":
-        document.encoding = "auto"
-        i = find_token(document.header, "\\encoding", 0)
-        if i != -1:
-            document.header[i] = "\\encoding auto"
+    if document.encoding != "applemac":
+      return
+    document.encoding = "auto"
+    i = find_token(document.header, "\\encoding", 0)
+    if i != -1:
+        document.header[i] = "\\encoding auto"
 
 
 def revert_longtable_align(document):
     " Remove longtable alignment setting "
     i = 0
 
 
 def revert_longtable_align(document):
     " Remove longtable alignment setting "
     i = 0
-    j = 0
     while True:
       i = find_token(document.body, "\\begin_inset Tabular", i)
       if i == -1:
           break
     while True:
       i = find_token(document.body, "\\begin_inset Tabular", i)
       if i == -1:
           break
-      # the alignment is 2 lines below \\begin_inset Tabular
-      j = document.body[i+2].find("longtabularalignment")
+      end = find_end_of_inset(document.body, i)
+      if end == -1:
+          document.warning("Can't find end of inset at line " + str(i))
+          i += 1
+          continue
+      fline = find_token(document.body, "<features", i, end)
+      if fline == -1:
+          document.warning("Can't find features for inset at line " + str(i))
+          i += 1
+          continue
+      j = document.body[fline].find("longtabularalignment")
       if j == -1:
       if j == -1:
-          break
-      document.body[i+2] = document.body[i+2][:j-1]
-      document.body[i+2] = document.body[i+2] + '>'
-      i = i + 1
+          i += 1
+          continue
+      # FIXME Is this correct? It wipes out everything after the 
+      # one we found.
+      document.body[fline] = document.body[fline][:j - 1] + '>'
+      # since there could be a tabular inside this one, we 
+      # cannot jump to end.
+      i += 1
 
 
 def revert_branch_filename(document):
 
 
 def revert_branch_filename(document):
@@ -853,49 +602,34 @@ def revert_branch_filename(document):
 
 def revert_paragraph_indentation(document):
     " Revert custom paragraph indentation to preamble code "
 
 def revert_paragraph_indentation(document):
     " Revert custom paragraph indentation to preamble code "
-    i = 0
-    while True:
-      i = find_token(document.header, "\\paragraph_indentation", i)
-      if i == -1:
-          break
-      # only remove the preamble line if default
-      # otherwise also write the value to the preamble
-      length = get_value(document.header, "\\paragraph_indentation", i)
-      if length == "default":
-          del document.header[i]
-          break
-      else:
-          # handle percent lengths
-          # latex_length returns "bool,length"
-          length = latex_length(length).split(",")[1]
-          add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
-          add_to_preamble(document, ["\\setlength{\\parindent}{" + length + "}"])
-          del document.header[i]
-      i = i + 1
+    i = find_token(document.header, "\\paragraph_indentation", 0)
+    if i == -1:
+      return
+    length = get_value(document.header, "\\paragraph_indentation", i)
+    # we need only remove the line if indentation is default
+    if length != "default":
+      # handle percent lengths
+      length = latex_length(length)[1]
+      insert_to_preamble(document, ["\\setlength{\\parindent}{" + length + "}"])
+    del document.header[i]
 
 
 def revert_percent_skip_lengths(document):
     " Revert relative lengths for paragraph skip separation to preamble code "
 
 
 def revert_percent_skip_lengths(document):
     " Revert relative lengths for paragraph skip separation to preamble code "
-    i = 0
-    while True:
-      i = find_token(document.header, "\\defskip", i)
-      if i == -1:
-          break
-      length = get_value(document.header, "\\defskip", i)
-      # only revert when a custom length was set and when
-      # it used a percent length
-      if length not in ('smallskip', 'medskip', 'bigskip'):
-          # handle percent lengths
-          length = latex_length(length)
-          # latex_length returns "bool,length"
-          percent = length.split(",")[0]
-          length = length.split(",")[1]
-          if percent == "True":
-              add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
-              add_to_preamble(document, ["\\setlength{\\parskip}{" + length + "}"])
-              # set defskip to medskip as default
-              document.header[i] = "\\defskip medskip"
-      i = i + 1
+    i = find_token(document.header, "\\defskip", 0)
+    if i == -1:
+        return
+    length = get_value(document.header, "\\defskip", i)
+    # only revert when a custom length was set and when
+    # it used a percent length
+    if length in ('smallskip', 'medskip', 'bigskip'):
+        return
+    # handle percent lengths
+    percent, length = latex_length(length)
+    if percent:
+        insert_to_preamble(document, ["\\setlength{\\parskip}{" + length + "}"])
+        # set defskip to medskip as default
+        document.header[i] = "\\defskip medskip"
 
 
 def revert_percent_vspace_lengths(document):
 
 
 def revert_percent_vspace_lengths(document):
@@ -907,214 +641,234 @@ def revert_percent_vspace_lengths(document):
           break
       # only revert if a custom length was set and if
       # it used a percent length
           break
       # only revert if a custom length was set and if
       # it used a percent length
-      line = document.body[i]
       r = re.compile(r'\\begin_inset VSpace (.*)$')
       r = re.compile(r'\\begin_inset VSpace (.*)$')
-      m = r.match(line)
+      m = r.match(document.body[i])
       length = m.group(1)
       length = m.group(1)
-      if length not in ('defskip', 'smallskip', 'medskip', 'bigskip', 'vfill'):
-          # check if the space has a star (protected space)
-          protected = (document.body[i].rfind("*") != -1)
+      if length in ('defskip', 'smallskip', 'medskip', 'bigskip', 'vfill'):
+         i += 1
+         continue
+      # check if the space has a star (protected space)
+      protected = (document.body[i].rfind("*") != -1)
+      if protected:
+          length = length.rstrip('*')
+      # handle percent lengths
+      percent, length = latex_length(length)
+      # revert the VSpace inset to ERT
+      if percent:
           if protected:
           if protected:
-              length = length.rstrip('*')
-          # handle percent lengths
-          length = latex_length(length)
-          # latex_length returns "bool,length"
-          percent = length.split(",")[0]
-          length = length.split(",")[1]
-          # revert the VSpace inset to ERT
-          if percent == "True":
-              if protected:
-                  subst = [old_put_cmd_in_ert("\\vspace*{" + length + "}")]
-              else:
-                  subst = [old_put_cmd_in_ert("\\vspace{" + length + "}")]
-              document.body[i:i+2] = subst
-      i = i + 1
+              subst = put_cmd_in_ert("\\vspace*{" + length + "}")
+          else:
+              subst = put_cmd_in_ert("\\vspace{" + length + "}")
+          document.body[i:i + 2] = subst
+      i += 1
 
 
 def revert_percent_hspace_lengths(document):
     " Revert relative HSpace lengths to ERT "
     i = 0
     while True:
 
 
 def revert_percent_hspace_lengths(document):
     " Revert relative HSpace lengths to ERT "
     i = 0
     while True:
-      i = find_token(document.body, "\\begin_inset space \\hspace", i)
+      i = find_token_exact(document.body, "\\begin_inset space \\hspace", i)
       if i == -1:
           break
       if i == -1:
           break
-      protected = (document.body[i].find("\\hspace*{}") != -1)
-      # only revert if a custom length was set and if
-      # it used a percent length
-      length = get_value(document.body, '\\length', i+1)
+      j = find_end_of_inset(document.body, i)
+      if j == -1:
+          document.warning("Can't find end of inset at line " + str(i))
+          i += 1
+          continue
+      # only revert if a custom length was set...
+      length = get_value(document.body, '\\length', i + 1, j)
       if length == '':
           document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
       if length == '':
           document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
-          return
-      # handle percent lengths
-      length = latex_length(length)
-      # latex_length returns "bool,length"
-      percent = length.split(",")[0]
-      length = length.split(",")[1]
+          i = j
+          continue
+      protected = ""
+      if document.body[i].find("\\hspace*{}") != -1:
+          protected = "*"
+      # ...and if it used a percent length
+      percent, length = latex_length(length)
       # revert the HSpace inset to ERT
       # revert the HSpace inset to ERT
-      if percent == "True":
-          if protected:
-              subst = [old_put_cmd_in_ert("\\hspace*{" + length + "}")]
-          else:
-              subst = [old_put_cmd_in_ert("\\hspace{" + length + "}")]
-          document.body[i:i+3] = subst
-      i = i + 2
+      if percent:
+          subst = put_cmd_in_ert("\\hspace" + protected + "{" + length + "}")
+          document.body[i:j + 1] = subst
+      # if we did a substitution, this will still be ok
+      i = j
 
 
 def revert_hspace_glue_lengths(document):
     " Revert HSpace glue lengths to ERT "
     i = 0
     while True:
 
 
 def revert_hspace_glue_lengths(document):
     " Revert HSpace glue lengths to ERT "
     i = 0
     while True:
-      i = find_token(document.body, "\\begin_inset space \\hspace", i)
+      i = find_token_exact(document.body, "\\begin_inset space \\hspace", i)
       if i == -1:
           break
       if i == -1:
           break
-      protected = (document.body[i].find("\\hspace*{}") != -1)
-      length = get_value(document.body, '\\length', i+1)
+      j = find_end_of_inset(document.body, i)
+      if j == -1:
+          document.warning("Can't find end of inset at line " + str(i))
+          i += 1
+          continue
+      length = get_value(document.body, '\\length', i + 1, j)
       if length == '':
           document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
       if length == '':
           document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
-          return
+          i = j
+          continue
+      protected = ""
+      if document.body[i].find("\\hspace*{}") != -1:
+          protected = "*"
       # only revert if the length contains a plus or minus at pos != 0
       # only revert if the length contains a plus or minus at pos != 0
-      glue  = re.compile(r'.+[\+-]')
-      if glue.search(length):
+      if length.find('-',1) != -1 or length.find('+',1) != -1:
           # handle percent lengths
           # handle percent lengths
-          # latex_length returns "bool,length"
-          length = latex_length(length).split(",")[1]
+          length = latex_length(length)[1]
           # revert the HSpace inset to ERT
           # revert the HSpace inset to ERT
-          if protected:
-              subst = [old_put_cmd_in_ert("\\hspace*{" + length + "}")]
-          else:
-              subst = [old_put_cmd_in_ert("\\hspace{" + length + "}")]
-          document.body[i:i+3] = subst
-      i = i + 2
+          subst = put_cmd_in_ert("\\hspace" + protected + "{" + length + "}")
+          document.body[i:j+1] = subst
+      i = j
+
 
 def convert_author_id(document):
     " Add the author_id to the \\author definition and make sure 0 is not used"
     i = 0
 
 def convert_author_id(document):
     " Add the author_id to the \\author definition and make sure 0 is not used"
     i = 0
-    j = 1
+    anum = 1
+    re_author = re.compile(r'(\\author) (\".*\")\s*(.*)$')
+    
     while True:
         i = find_token(document.header, "\\author", i)
         if i == -1:
             break
     while True:
         i = find_token(document.header, "\\author", i)
         if i == -1:
             break
-        
-        r = re.compile(r'(\\author) (\".*\")\s?(.*)$')
-        m = r.match(document.header[i])
-        if m != None:
+        m = re_author.match(document.header[i])
+        if m:
             name = m.group(2)
             name = m.group(2)
-            
-            email = ''
-            if m.lastindex == 3:
-                email = m.group(3)
-            document.header[i] = "\\author %i %s %s" % (j, name, email)
-        j = j + 1
-        i = i + 1
+            email = m.group(3)
+            document.header[i] = "\\author %i %s %s" % (anum, name, email)
+        anum += 1
+        i += 1
         
         
-    k = 0
+    i = 0
     while True:
     while True:
-        k = find_token(document.body, "\\change_", k)
-        if k == -1:
+        i = find_token(document.body, "\\change_", i)
+        if i == -1:
             break
             break
-
-        change = document.body[k].split(' ');
+        change = document.body[i].split(' ');
         if len(change) == 3:
             type = change[0]
             author_id = int(change[1])
             time = change[2]
         if len(change) == 3:
             type = change[0]
             author_id = int(change[1])
             time = change[2]
-            document.body[k] = "%s %i %s" % (type, author_id + 1, time)
-        k = k + 1
+            document.body[i] = "%s %i %s" % (type, author_id + 1, time)
+        i += 1
+
 
 def revert_author_id(document):
     " Remove the author_id from the \\author definition "
     i = 0
 
 def revert_author_id(document):
     " Remove the author_id from the \\author definition "
     i = 0
-    j = 0
+    anum = 0
+    rx = re.compile(r'(\\author)\s+(-?\d+)\s+(\".*\")\s*(.*)$')
     idmap = dict()
     idmap = dict()
+
     while True:
         i = find_token(document.header, "\\author", i)
         if i == -1:
             break
     while True:
         i = find_token(document.header, "\\author", i)
         if i == -1:
             break
-        
-        r = re.compile(r'(\\author) (\d+) (\".*\")\s?(.*)$')
-        m = r.match(document.header[i])
-        if m != None:
+        m = rx.match(document.header[i])
+        if m:
             author_id = int(m.group(2))
             author_id = int(m.group(2))
-            idmap[author_id] = j
+            idmap[author_id] = anum
             name = m.group(3)
             name = m.group(3)
-            
-            email = ''
-            if m.lastindex == 4:
-                email = m.group(4)
+            email = m.group(4)
             document.header[i] = "\\author %s %s" % (name, email)
             document.header[i] = "\\author %s %s" % (name, email)
-        i = i + 1
-        j = j + 1
+        i += 1
+        # FIXME Should this be incremented if we didn't match?
+        anum += 1
 
 
-    k = 0
+    i = 0
     while True:
     while True:
-        k = find_token(document.body, "\\change_", k)
-        if k == -1:
+        i = find_token(document.body, "\\change_", i)
+        if i == -1:
             break
             break
-
-        change = document.body[k].split(' ');
+        change = document.body[i].split(' ');
         if len(change) == 3:
             type = change[0]
             author_id = int(change[1])
             time = change[2]
         if len(change) == 3:
             type = change[0]
             author_id = int(change[1])
             time = change[2]
-            document.body[k] = "%s %i %s" % (type, idmap[author_id], time)
-        k = k + 1
+            document.body[i] = "%s %i %s" % (type, idmap[author_id], time)
+        i += 1
 
 
 def revert_suppress_date(document):
     " Revert suppressing of default document date to preamble code "
 
 
 def revert_suppress_date(document):
     " Revert suppressing of default document date to preamble code "
-    i = 0
-    while True:
-      i = find_token(document.header, "\\suppress_date", i)
-      if i == -1:
-          break
-      # remove the preamble line and write to the preamble
-      # when suppress_date was true
-      date = get_value(document.header, "\\suppress_date", i)
-      if date == "true":
-          add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
-          add_to_preamble(document, ["\\date{}"])
-      del document.header[i]
-      i = i + 1
+    i = find_token(document.header, "\\suppress_date", 0)
+    if i == -1:
+        return
+    # remove the preamble line and write to the preamble
+    # when suppress_date was true
+    date = str2bool(get_value(document.header, "\\suppress_date", i))
+    if date:
+        add_to_preamble(document, ["\\date{}"])
+    del document.header[i]
+
+
+def convert_mhchem(document):
+    "Set mhchem to off for versions older than 1.6.x"
+    if document.start < 277:
+        # LyX 1.5.x and older did never load mhchem.
+        # Therefore we must switch it off: Documents that use mhchem have
+        # a manual \usepackage anyway, and documents not using mhchem but
+        # custom macros with the same names as mhchem commands might get
+        # corrupted if mhchem is automatically loaded.
+        mhchem = 0 # off
+    else:
+        # LyX 1.6.x did always load mhchem automatically.
+        mhchem = 1 # auto
+    i = find_token(document.header, "\\use_esint", 0)
+    if i == -1:
+        # pre-1.5.x document
+        i = find_token(document.header, "\\use_amsmath", 0)
+    if i == -1:
+        document.warning("Malformed LyX document: Could not find amsmath os esint setting.")
+        return
+    document.header.insert(i + 1, "\\use_mhchem %d" % mhchem)
 
 
 def revert_mhchem(document):
     "Revert mhchem loading to preamble code"
 
 
 def revert_mhchem(document):
     "Revert mhchem loading to preamble code"
-    i = 0
-    j = 0
-    k = 0
-    i = find_token(document.header, "\\use_mhchem 1", 0)
-    if i != -1:
+
+    mhchem = "off"
+    i = find_token(document.header, "\\use_mhchem", 0)
+    if i == -1:
+        document.warning("Malformed LyX document: Could not find mhchem setting.")
         mhchem = "auto"
     else:
         mhchem = "auto"
     else:
-        i = find_token(document.header, "\\use_mhchem 2", 0)
-        if i != -1:
+        val = get_value(document.header, "\\use_mhchem", i)
+        if val == "1":
+            mhchem = "auto"
+        elif val == "2":
             mhchem = "on"
             mhchem = "on"
+        del document.header[i]
+
+    if mhchem == "off":
+      # don't load case
+      return 
+
     if mhchem == "auto":
     if mhchem == "auto":
-        j = find_token(document.body, "\\cf{", 0)
-        if j != -1:
-            mhchem = "on"
-        else:
-            j = find_token(document.body, "\\ce{", 0)
-            if j != -1:
-                mhchem = "on"
+        i = 0
+        while True:
+            i = find_token(document.body, "\\begin_inset Formula", i)
+            if i == -1:
+               break
+            line = document.body[i]
+            if line.find("\\ce{") != -1 or line.find("\\cf{") != -1:
+              mhchem = "on"
+              break
+            i += 1
+
     if mhchem == "on":
     if mhchem == "on":
-        add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
-        add_to_preamble(document, ["\\PassOptionsToPackage{version=3}{mhchem}"])
-        add_to_preamble(document, ["\\usepackage{mhchem}"])
-    k = find_token(document.header, "\\use_mhchem", 0)
-    if k == -1:
-        document.warning("Malformed LyX document: Could not find mhchem setting.")
-        return
-    del document.header[k]
+        pre = ["\\PassOptionsToPackage{version=3}{mhchem}", 
+          "\\usepackage{mhchem}"]
+        insert_to_preamble(document, pre) 
 
 
 def revert_fontenc(document):
     " Remove fontencoding param "
 
 
 def revert_fontenc(document):
     " Remove fontencoding param "
-    i = find_token(document.header, '\\fontencoding', 0)
-    if i == -1:
+    if not del_token(document.header, '\\fontencoding', 0):
         document.warning("Malformed LyX document: Missing \\fontencoding.")
         document.warning("Malformed LyX document: Missing \\fontencoding.")
-        return
-    del document.header[i]
 
 
 def merge_gbrief(document):
 
 
 def merge_gbrief(document):
@@ -1172,12 +926,8 @@ def revert_gbrief(document):
 
 def revert_html_options(document):
     " Remove html options "
 
 def revert_html_options(document):
     " Remove html options "
-    i = find_token(document.header, '\\html_use_mathml', 0)
-    if i != -1:
-        del document.header[i]
-    i = find_token(document.header, '\\html_be_strict', 0)
-    if i != -1:
-        del document.header[i]
+    del_token(document.header, '\\html_use_mathml', 0)
+    del_token(document.header, '\\html_be_strict', 0)
 
 
 def revert_includeonly(document):
 
 
 def revert_includeonly(document):
@@ -1188,58 +938,142 @@ def revert_includeonly(document):
             return
         j = find_end_of(document.header, i, "\\begin_includeonly", "\\end_includeonly")
         if j == -1:
             return
         j = find_end_of(document.header, i, "\\begin_includeonly", "\\end_includeonly")
         if j == -1:
-            # this should not happen
+            document.warning("Unable to find end of includeonly section!!")
             break
         document.header[i : j + 1] = []
 
 
 def revert_includeall(document):
     " Remove maintain_unincluded_children param "
             break
         document.header[i : j + 1] = []
 
 
 def revert_includeall(document):
     " Remove maintain_unincluded_children param "
-    i = find_token(document.header, '\\maintain_unincluded_children', 0)
-    if i != -1:
-        del document.header[i]
+    del_token(document.header, '\\maintain_unincluded_children', 0)
 
 
 def revert_multirow(document):
 
 
 def revert_multirow(document):
-    " Revert multirow cells in tables "
-    i = 0
-    multirow = False
+    " Revert multirow cells in tables to TeX-code"
+
+    # first, let's find out if we need to do anything
+    # cell type 3 is multirow begin cell
+    i = find_token(document.body, '<cell multirow="3"', 0)
+    if i == -1:
+      return
+
+    add_to_preamble(document, ["\\usepackage{multirow}"])
+
+    begin_table = 0
     while True:
     while True:
-      # cell type 3 is multirow begin cell
-      i = find_token(document.body, '<cell multirow="3"', i)
-      if i == -1:
-          break
-      # a multirow cell was found
-      multirow = True
-      # remove the multirow tag, set the valignment to top
-      # and remove the bottom line
-      document.body[i] = document.body[i].replace(' multirow="3" ', ' ')
-      document.body[i] = document.body[i].replace('valignment="middle"', 'valignment="top"')
-      document.body[i] = document.body[i].replace(' bottomline="true" ', ' ')
-      # write ERT to create the multirow cell
-      # use 2 rows and 2cm as default with because the multirow span
-      # and the column width is only hardly accessible
-      subst = [old_put_cmd_in_ert("\\multirow{2}{2cm}{")]
-      document.body[i + 4:i + 4] = subst
-      i = find_token(document.body, "</cell>", i)
-      if i == -1:
-           document.warning("Malformed LyX document: Could not find end of tabular cell.")
-           break
-      subst = [old_put_cmd_in_ert("}")]
-      document.body[i - 3:i - 3] = subst
-      # cell type 4 is multirow part cell
-      i = find_token(document.body, '<cell multirow="4"', i)
-      if i == -1:
-          break
-      # remove the multirow tag, set the valignment to top
-      # and remove the top line
-      document.body[i] = document.body[i].replace(' multirow="4" ', ' ')
-      document.body[i] = document.body[i].replace('valignment="middle"', 'valignment="top"')
-      document.body[i] = document.body[i].replace(' topline="true" ', ' ')
-      i = i + 1
-    if multirow == True:
-        add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
-        add_to_preamble(document, ["\\usepackage{multirow}"])
+        # find begin/end of table
+        begin_table = find_token(document.body, '<lyxtabular version=', begin_table)
+        if begin_table == -1:
+            break
+        end_table = find_end_of(document.body, begin_table, '<lyxtabular', '</lyxtabular>')
+        if end_table == -1:
+            document.warning("Malformed LyX document: Could not find end of table.")
+            begin_table += 1
+            continue
+        # does this table have multirow?
+        i = find_token(document.body, '<cell multirow="3"', begin_table, end_table)
+        if i == -1:
+            begin_table = end_table
+            continue
+        
+        # store the number of rows and columns
+        numrows = get_option_value(document.body[begin_table], "rows")
+        numcols = get_option_value(document.body[begin_table], "columns")
+        try:
+          numrows = int(numrows)
+          numcols = int(numcols)
+        except:
+          document.warning("Unable to determine rows and columns!")
+          begin_table = end_table
+          continue
+
+        mrstarts = []
+        multirows = []
+        # collect info on rows and columns of this table.
+        begin_row = begin_table
+        for row in range(numrows):
+            begin_row = find_token(document.body, '<row>', begin_row, end_table)
+            if begin_row == -1:
+              document.warning("Can't find row " + str(row + 1))
+              break
+            end_row = find_end_of(document.body, begin_row, '<row>', '</row>')
+            if end_row == -1:
+              document.warning("Can't find end of row " + str(row + 1))
+              break
+            begin_cell = begin_row
+            multirows.append([])
+            for column in range(numcols):            
+                begin_cell = find_token(document.body, '<cell ', begin_cell, end_row)
+                if begin_cell == -1:
+                  document.warning("Can't find column " + str(column + 1) + \
+                    "in row " + str(row + 1))
+                  break
+                # NOTE 
+                # this will fail if someone puts "</cell>" in a cell, but
+                # that seems fairly unlikely.
+                end_cell = find_end_of(document.body, begin_cell, '<cell', '</cell>')
+                if end_cell == -1:
+                  document.warning("Can't find end of column " + str(column + 1) + \
+                    "in row " + str(row + 1))
+                  break
+                multirows[row].append([begin_cell, end_cell, 0])
+                if document.body[begin_cell].find('multirow="3"') != -1:
+                  multirows[row][column][2] = 3 # begin multirow
+                  mrstarts.append([row, column])
+                elif document.body[begin_cell].find('multirow="4"') != -1:
+                  multirows[row][column][2] = 4 # in multirow
+                begin_cell = end_cell
+            begin_row = end_row
+        # end of table info collection
+
+        # work from the back to avoid messing up numbering
+        mrstarts.reverse()
+        for m in mrstarts:
+            row = m[0]
+            col = m[1]
+            # get column width
+            col_width = get_option_value(document.body[begin_table + 2 + col], "width")
+            # "0pt" means that no width is specified
+            if not col_width or col_width == "0pt":
+              col_width = "*"
+            # determine the number of cells that are part of the multirow
+            nummrs = 1
+            for r in range(row + 1, numrows):
+                if multirows[r][col][2] != 4:
+                  break
+                nummrs += 1
+                # take the opportunity to revert this line
+                lineno = multirows[r][col][0]
+                document.body[lineno] = document.body[lineno].\
+                  replace(' multirow="4" ', ' ').\
+                  replace('valignment="middle"', 'valignment="top"').\
+                  replace(' topline="true" ', ' ')
+                # remove bottom line of previous multirow-part cell
+                lineno = multirows[r-1][col][0]
+                document.body[lineno] = document.body[lineno].replace(' bottomline="true" ', ' ')
+            # revert beginning cell
+            bcell = multirows[row][col][0]
+            ecell = multirows[row][col][1]
+            document.body[bcell] = document.body[bcell].\
+              replace(' multirow="3" ', ' ').\
+              replace('valignment="middle"', 'valignment="top"')
+            blay = find_token(document.body, "\\begin_layout", bcell, ecell)
+            if blay == -1:
+              document.warning("Can't find layout for cell!")
+              continue
+            bend = find_end_of_layout(document.body, blay)
+            if bend == -1:
+              document.warning("Can't find end of layout for cell!")
+              continue
+            # do the later one first, so as not to mess up the numbering
+            # we are wrapping the whole cell in this ert
+            # so before the end of the layout...
+            document.body[bend:bend] = put_cmd_in_ert("}")
+            # ...and after the beginning
+            document.body[blay + 1:blay + 1] = \
+              put_cmd_in_ert("\\multirow{" + str(nummrs) + "}{" + col_width + "}{")
+
+        begin_table = end_table
 
 
 def convert_math_output(document):
 
 
 def convert_math_output(document):
@@ -1249,12 +1083,14 @@ def convert_math_output(document):
         return
     rgx = re.compile(r'\\html_use_mathml\s+(\w+)')
     m = rgx.match(document.header[i])
         return
     rgx = re.compile(r'\\html_use_mathml\s+(\w+)')
     m = rgx.match(document.header[i])
-    if rgx:
-        newval = "0" # MathML
-        val = m.group(1)
-        if val != "true":
-            newval = "2" # Images
-        document.header[i] = "\\html_math_output " + newval
+    newval = "0" # MathML
+    if m:
+      val = str2bool(m.group(1))
+      if not val:
+        newval = "2" # Images
+    else:
+      document.warning("Can't match " + document.header[i])
+    document.header[i] = "\\html_math_output " + newval
 
 
 def revert_math_output(document):
 
 
 def revert_math_output(document):
@@ -1265,7 +1101,7 @@ def revert_math_output(document):
     rgx = re.compile(r'\\html_math_output\s+(\d)')
     m = rgx.match(document.header[i])
     newval = "true"
     rgx = re.compile(r'\\html_math_output\s+(\d)')
     m = rgx.match(document.header[i])
     newval = "true"
-    if rgx:
+    if m:
         val = m.group(1)
         if val == "1" or val == "2":
             newval = "false"
         val = m.group(1)
         if val == "1" or val == "2":
             newval = "false"
@@ -1278,45 +1114,89 @@ def revert_math_output(document):
 def revert_inset_preview(document):
     " Dissolves the preview inset "
     i = 0
 def revert_inset_preview(document):
     " Dissolves the preview inset "
     i = 0
-    j = 0
-    k = 0
     while True:
       i = find_token(document.body, "\\begin_inset Preview", i)
       if i == -1:
           return
     while True:
       i = find_token(document.body, "\\begin_inset Preview", i)
       if i == -1:
           return
-      j = find_end_of_inset(document.body, i)
-      if j == -1:
+      iend = find_end_of_inset(document.body, i)
+      if iend == -1:
           document.warning("Malformed LyX document: Could not find end of Preview inset.")
           document.warning("Malformed LyX document: Could not find end of Preview inset.")
-          return
-      #If the layout is Standard we need to remove it, otherwise there
-      #will be paragraph breaks that shouldn't be there.
-      k = find_token(document.body, "\\begin_layout Standard", i)
-      if k == i+2:
-          del document.body[i : i+3]
-          del document.body[j-5 : j-2]
-          i -= 6
-      else:
+          i += 1
+          continue
+      
+      # This has several issues.
+      # We need to do something about the layouts inside InsetPreview.
+      # If we just leave the first one, then we have something like:
+      # \begin_layout Standard
+      # ...
+      # \begin_layout Standard
+      # and we get a "no \end_layout" error. So something has to be done.
+      # Ideally, we would check if it is the same as the layout we are in.
+      # If so, we just remove it; if not, we end the active one. But it is 
+      # not easy to know what layout we are in, due to depth changes, etc,
+      # and it is not clear to me how much work it is worth doing. In most
+      # cases, the layout will probably be the same.
+      # 
+      # For the same reason, we have to remove the \end_layout tag at the
+      # end of the last layout in the inset. Again, that will sometimes be
+      # wrong, but it will usually be right. To know what to do, we would
+      # again have to know what layout the inset is in.
+      
+      blay = find_token(document.body, "\\begin_layout", i, iend)
+      if blay == -1:
+          document.warning("Can't find layout for preview inset!")
+          # always do the later one first...
+          del document.body[iend]
+          del document.body[i]
+          # deletions mean we do not need to reset i
+          continue
+
+      # This is where we would check what layout we are in.
+      # The check for Standard is definitely wrong.
+      # 
+      # lay = document.body[blay].split(None, 1)[1]
+      # if lay != oldlayout:
+      #     # record a boolean to tell us what to do later....
+      #     # better to do it later, since (a) it won't mess up
+      #     # the numbering and (b) we only modify at the end.
+        
+      # we want to delete the last \\end_layout in this inset, too.
+      # note that this may not be the \\end_layout that goes with blay!!
+      bend = find_end_of_layout(document.body, blay)
+      while True:
+          tmp = find_token(document.body, "\\end_layout", bend + 1, iend)
+          if tmp == -1:
+              break
+          bend = tmp
+      if bend == blay:
+          document.warning("Unable to find last layout in preview inset!")
+          del document.body[iend]
           del document.body[i]
           del document.body[i]
-          del document.body[j-1]
-          i -= 2
+          # deletions mean we do not need to reset i
+          continue
+      # always do the later one first...
+      del document.body[iend]
+      del document.body[bend]
+      del document.body[i:blay + 1]
+      # we do not need to reset i
                 
 
 def revert_equalspacing_xymatrix(document):
     " Revert a Formula with xymatrix@! to an ERT inset "
     i = 0
                 
 
 def revert_equalspacing_xymatrix(document):
     " Revert a Formula with xymatrix@! to an ERT inset "
     i = 0
-    j = 0
     has_preamble = False
     has_equal_spacing = False
     has_preamble = False
     has_equal_spacing = False
+
     while True:
     while True:
-      found = -1
       i = find_token(document.body, "\\begin_inset Formula", i)
       if i == -1:
           break
       j = find_end_of_inset(document.body, i)
       if j == -1:
           document.warning("Malformed LyX document: Could not find end of Formula inset.")
       i = find_token(document.body, "\\begin_inset Formula", i)
       if i == -1:
           break
       j = find_end_of_inset(document.body, i)
       if j == -1:
           document.warning("Malformed LyX document: Could not find end of Formula inset.")
-          break
-          
+          i += 1
+          continue
+      
       for curline in range(i,j):
           found = document.body[curline].find("\\xymatrix@!")
           if found != -1:
       for curline in range(i,j):
           found = document.body[curline].find("\\xymatrix@!")
           if found != -1:
@@ -1325,10 +1205,10 @@ def revert_equalspacing_xymatrix(document):
       if found != -1:
           has_equal_spacing = True
           content = [document.body[i][21:]]
       if found != -1:
           has_equal_spacing = True
           content = [document.body[i][21:]]
-          content += document.body[i+1:j]
+          content += document.body[i + 1:j]
           subst = put_cmd_in_ert(content)
           subst = put_cmd_in_ert(content)
-          document.body[i:j+1] = subst
-          i += len(subst)
+          document.body[i:j + 1] = subst
+          i += len(subst) - (j - i) + 1
       else:
           for curline in range(i,j):
               l = document.body[curline].find("\\xymatrix")
       else:
           for curline in range(i,j):
               l = document.body[curline].find("\\xymatrix")
@@ -1336,139 +1216,107 @@ def revert_equalspacing_xymatrix(document):
                   has_preamble = True;
                   break;
           i = j + 1
                   has_preamble = True;
                   break;
           i = j + 1
+  
     if has_equal_spacing and not has_preamble:
         add_to_preamble(document, ['\\usepackage[all]{xy}'])
 
 
 def revert_notefontcolor(document):
     " Reverts greyed-out note font color to preamble code "
     if has_equal_spacing and not has_preamble:
         add_to_preamble(document, ['\\usepackage[all]{xy}'])
 
 
 def revert_notefontcolor(document):
     " Reverts greyed-out note font color to preamble code "
-    i = 0
-    colorcode = ""
-    while True:
-      i = find_token(document.header, "\\notefontcolor", i)
-      if i == -1:
-          return
-      colorcode = get_value(document.header, '\\notefontcolor', 0)
-      del document.header[i]
-      # the color code is in the form #rrggbb where every character denotes a hex number
-      # convert the string to an int
-      red = string.atoi(colorcode[1:3],16)
-      # we want the output "0.5" for the value "127" therefore increment here
-      if red != 0:
-          red = red + 1
-      redout = float(red) / 256
-      green = string.atoi(colorcode[3:5],16)
-      if green != 0:
-          green = green + 1
-      greenout = float(green) / 256
-      blue = string.atoi(colorcode[5:7],16)
-      if blue != 0:
-          blue = blue + 1
-      blueout = float(blue) / 256
-      # write the preamble
-      insert_to_preamble(0, document,
-                           '% Commands inserted by lyx2lyx to set the font color\n'
-                           '% for greyed-out notes\n'
-                           + '\\@ifundefined{definecolor}{\\usepackage{color}}{}\n'
-                           + '\\definecolor{note_fontcolor}{rgb}{'
-                           + str(redout) + ', ' + str(greenout)
-                           + ', ' + str(blueout) + '}\n'
-                           + '\\renewenvironment{lyxgreyedout}\n'
-                           + ' {\\textcolor{note_fontcolor}\\bgroup}{\\egroup}\n')
+
+    i = find_token(document.header, "\\notefontcolor", 0)
+    if i == -1:
+        return
+
+    colorcode = get_value(document.header, '\\notefontcolor', i)
+    del document.header[i]
+
+    # are there any grey notes?
+    if find_token(document.body, "\\begin_inset Note Greyedout", 0) == -1:
+        # no need to do anything else, and \renewcommand will throw 
+        # an error since lyxgreyedout will not exist.
+        return
+
+    # the color code is in the form #rrggbb where every character denotes a hex number
+    red = hex2ratio(colorcode[1:3])
+    green = hex2ratio(colorcode[3:5])
+    blue = hex2ratio(colorcode[5:7])
+    # write the preamble
+    insert_to_preamble(document,
+      [ '%  for greyed-out notes',
+        '\\@ifundefined{definecolor}{\\usepackage{color}}{}'
+        '\\definecolor{note_fontcolor}{rgb}{%s,%s,%s}' % (red, green, blue),
+        '\\renewenvironment{lyxgreyedout}',
+        ' {\\textcolor{note_fontcolor}\\bgroup}{\\egroup}'])
 
 
 def revert_turkmen(document):
     "Set language Turkmen to English" 
 
 
 def revert_turkmen(document):
     "Set language Turkmen to English" 
-    i = 0 
+
     if document.language == "turkmen": 
         document.language = "english" 
         i = find_token(document.header, "\\language", 0) 
         if i != -1: 
             document.header[i] = "\\language english" 
     if document.language == "turkmen": 
         document.language = "english" 
         i = find_token(document.header, "\\language", 0) 
         if i != -1: 
             document.header[i] = "\\language english" 
+
     j = 0 
     while True: 
         j = find_token(document.body, "\\lang turkmen", j) 
         if j == -1: 
             return 
         document.body[j] = document.body[j].replace("\\lang turkmen", "\\lang english") 
     j = 0 
     while True: 
         j = find_token(document.body, "\\lang turkmen", j) 
         if j == -1: 
             return 
         document.body[j] = document.body[j].replace("\\lang turkmen", "\\lang english") 
-        j = j + 1 
+        j += 1 
 
 
 def revert_fontcolor(document):
     " Reverts font color to preamble code "
 
 
 def revert_fontcolor(document):
     " Reverts font color to preamble code "
-    i = 0
-    colorcode = ""
-    while True:
-      i = find_token(document.header, "\\fontcolor", i)
-      if i == -1:
-          return
-      colorcode = get_value(document.header, '\\fontcolor', 0)
-      del document.header[i]
-      # don't clutter the preamble if backgroundcolor is not set
-      if colorcode == "#000000":
-          continue
-      # the color code is in the form #rrggbb where every character denotes a hex number
-      # convert the string to an int
-      red = string.atoi(colorcode[1:3],16)
-      # we want the output "0.5" for the value "127" therefore add here
-      if red != 0:
-          red = red + 1
-      redout = float(red) / 256
-      green = string.atoi(colorcode[3:5],16)
-      if green != 0:
-          green = green + 1
-      greenout = float(green) / 256
-      blue = string.atoi(colorcode[5:7],16)
-      if blue != 0:
-          blue = blue + 1
-      blueout = float(blue) / 256
-      # write the preamble
-      insert_to_preamble(0, document,
-                           '% Commands inserted by lyx2lyx to set the font color\n'
-                           + '\\@ifundefined{definecolor}{\\usepackage{color}}{}\n'
-                           + '\\definecolor{document_fontcolor}{rgb}{'
-                           + str(redout) + ', ' + str(greenout)
-                           + ', ' + str(blueout) + '}\n'
-                           + '\\color{document_fontcolor}\n')
+    i = find_token(document.header, "\\fontcolor", 0)
+    if i == -1:
+        return
+    colorcode = get_value(document.header, '\\fontcolor', i)
+    del document.header[i]
+    # don't clutter the preamble if font color is not set
+    if colorcode == "#000000":
+        return
+    # the color code is in the form #rrggbb where every character denotes a hex number
+    red = hex2ratio(colorcode[1:3])
+    green = hex2ratio(colorcode[3:5])
+    blue = hex2ratio(colorcode[5:7])
+    # write the preamble
+    insert_to_preamble(document,
+      ['%  Set the font color',
+      '\\@ifundefined{definecolor}{\\usepackage{color}}{}',
+      '\\definecolor{document_fontcolor}{rgb}{%s,%s,%s}' % (red, green, blue),
+      '\\color{document_fontcolor}'])
 
 
 def revert_shadedboxcolor(document):
     " Reverts shaded box color to preamble code "
 
 
 def revert_shadedboxcolor(document):
     " Reverts shaded box color to preamble code "
-    i = 0
-    colorcode = ""
-    while True:
-      i = find_token(document.header, "\\boxbgcolor", i)
-      if i == -1:
-          return
-      colorcode = get_value(document.header, '\\boxbgcolor', 0)
-      del document.header[i]
-      # the color code is in the form #rrggbb where every character denotes a hex number
-      # convert the string to an int
-      red = string.atoi(colorcode[1:3],16)
-      # we want the output "0.5" for the value "127" therefore increment here
-      if red != 0:
-          red = red + 1
-      redout = float(red) / 256
-      green = string.atoi(colorcode[3:5],16)
-      if green != 0:
-          green = green + 1
-      greenout = float(green) / 256
-      blue = string.atoi(colorcode[5:7],16)
-      if blue != 0:
-          blue = blue + 1
-      blueout = float(blue) / 256
-      # write the preamble
-      insert_to_preamble(0, document,
-                           '% Commands inserted by lyx2lyx to set the color\n'
-                           '% of boxes with shaded background\n'
-                           + '\\@ifundefined{definecolor}{\\usepackage{color}}{}\n'
-                           + '\\definecolor{shadecolor}{rgb}{'
-                           + str(redout) + ', ' + str(greenout)
-                           + ', ' + str(blueout) + '}\n')
+    i = find_token(document.header, "\\boxbgcolor", 0)
+    if i == -1:
+        return
+    colorcode = get_value(document.header, '\\boxbgcolor', i)
+    del document.header[i]
+    # the color code is in the form #rrggbb
+    red = hex2ratio(colorcode[1:3])
+    green = hex2ratio(colorcode[3:5])
+    blue = hex2ratio(colorcode[5:7])
+    # write the preamble
+    insert_to_preamble(document,
+      ['%  Set the color of boxes with shaded background',
+      '\\@ifundefined{definecolor}{\\usepackage{color}}{}',
+      "\\definecolor{shadecolor}{rgb}{%s,%s,%s}" % (red, green, blue)])
 
 
 def revert_lyx_version(document):
     " Reverts LyX Version information from Inset Info "
 
 
 def revert_lyx_version(document):
     " Reverts LyX Version information from Inset Info "
+    version = "LyX version"
+    try:
+        import lyx2lyx_version
+        version = lyx2lyx_version.version
+    except:
+        pass
+
     i = 0
     while 1:
         i = find_token(document.body, '\\begin_inset Info', i)
     i = 0
     while 1:
         i = find_token(document.body, '\\begin_inset Info', i)
@@ -1476,33 +1324,1133 @@ def revert_lyx_version(document):
             return
         j = find_end_of_inset(document.body, i + 1)
         if j == -1:
             return
         j = find_end_of_inset(document.body, i + 1)
         if j == -1:
-            # should not happen
             document.warning("Malformed LyX document: Could not find end of Info inset.")
             document.warning("Malformed LyX document: Could not find end of Info inset.")
+            i += 1
+            continue
+
         # We expect:
         # \begin_inset Info
         # type  "lyxinfo"
         # arg   "version"
         # \end_inset
         # We expect:
         # \begin_inset Info
         # type  "lyxinfo"
         # arg   "version"
         # \end_inset
-        # but we shall try to be forgiving.
-        arg = typ = ""
-        for k in range(i, j):
-            if document.body[k].startswith("arg"):
-                arg = document.body[k][3:].strip().strip('"')
-            if document.body[k].startswith("type"):
-                typ = document.body[k][4:].strip().strip('"')
+        typ = get_quoted_value(document.body, "type", i, j)
+        arg = get_quoted_value(document.body, "arg", i, j)
         if arg != "version" or typ != "lyxinfo":
         if arg != "version" or typ != "lyxinfo":
-            i = j+1
+            i = j + 1
             continue
             continue
+
         # We do not actually know the version of LyX used to produce the document.
         # But we can use our version, since we are reverting.
         # We do not actually know the version of LyX used to produce the document.
         # But we can use our version, since we are reverting.
-        s = [lyx2lyx_version.version]
+        s = [version]
         # Now we want to check if the line after "\end_inset" is empty. It normally
         # is, so we want to remove it, too.
         # Now we want to check if the line after "\end_inset" is empty. It normally
         # is, so we want to remove it, too.
-        lastline = j+1
-        if document.body[j+1].strip() == "":
-            lastline = j+2
+        lastline = j + 1
+        if document.body[j + 1].strip() == "":
+            lastline = j + 2
         document.body[i: lastline] = s
         document.body[i: lastline] = s
-        i = i+1
+        i = i + 1
+
+
+def revert_math_scale(document):
+  " Remove math scaling and LaTeX options "
+  del_token(document.header, '\\html_math_img_scale', 0)
+  del_token(document.header, '\\html_latex_start', 0)
+  del_token(document.header, '\\html_latex_end', 0)
+
+
+def revert_pagesizes(document):
+  " Revert page sizes to default "
+  i = find_token(document.header, '\\papersize', 0)
+  if i != -1:
+    size = document.header[i][11:]
+    if size == "a0paper" or size == "a1paper" or size == "a2paper" \
+    or size == "a6paper" or size == "b0paper" or size == "b1paper" \
+    or size == "b2paper" or size == "b6paper" or size == "b0j" \
+    or size == "b1j" or size == "b2j" or size == "b3j" or size == "b4j" \
+    or size == "b5j" or size == "b6j":
+      del document.header[i]
+
+
+def revert_DIN_C_pagesizes(document):
+  " Revert DIN C page sizes to default "
+  i = find_token(document.header, '\\papersize', 0)
+  if i != -1:
+    size = document.header[i][11:]
+    if size == "c0paper" or size == "c1paper" or size == "c2paper" \
+    or size == "c3paper" or size == "c4paper" or size == "c5paper" \
+    or size == "c6paper":
+      del document.header[i]
+
+
+def convert_html_quotes(document):
+  " Remove quotes around html_latex_start and html_latex_end "
+
+  i = find_token(document.header, '\\html_latex_start', 0)
+  if i != -1:
+    line = document.header[i]
+    l = re.compile(r'\\html_latex_start\s+"(.*)"')
+    m = l.match(line)
+    if m:
+      document.header[i] = "\\html_latex_start " + m.group(1)
+      
+  i = find_token(document.header, '\\html_latex_end', 0)
+  if i != -1:
+    line = document.header[i]
+    l = re.compile(r'\\html_latex_end\s+"(.*)"')
+    m = l.match(line)
+    if m:
+      document.header[i] = "\\html_latex_end " + m.group(1)
+      
+
+def revert_html_quotes(document):
+  " Remove quotes around html_latex_start and html_latex_end "
+  
+  i = find_token(document.header, '\\html_latex_start', 0)
+  if i != -1:
+    line = document.header[i]
+    l = re.compile(r'\\html_latex_start\s+(.*)')
+    m = l.match(line)
+    if not m:
+        document.warning("Weird html_latex_start line: " + line)
+        del document.header[i]
+    else:
+        document.header[i] = "\\html_latex_start \"" + m.group(1) + "\""
+      
+  i = find_token(document.header, '\\html_latex_end', 0)
+  if i != -1:
+    line = document.header[i]
+    l = re.compile(r'\\html_latex_end\s+(.*)')
+    m = l.match(line)
+    if not m:
+        document.warning("Weird html_latex_end line: " + line)
+        del document.header[i]
+    else:
+        document.header[i] = "\\html_latex_end \"" + m.group(1) + "\""
+
+
+def revert_output_sync(document):
+  " Remove forward search options "
+  del_token(document.header, '\\output_sync_macro', 0)
+  del_token(document.header, '\\output_sync', 0)
+
+
+def revert_align_decimal(document):
+  i = 0
+  while True:
+    i = find_token(document.body, "\\begin_inset Tabular", i)
+    if i == -1:
+      return
+    j = find_end_of_inset(document.body, i)
+    if j == -1:
+      document.warning("Unable to find end of Tabular inset at line " + str(i))
+      i += 1
+      continue
+    cell = find_token(document.body, "<cell", i, j)
+    if cell == -1:
+      document.warning("Can't find any cells in Tabular inset at line " + str(i))
+      i = j
+      continue
+    k = i + 1
+    while True:
+      k = find_token(document.body, "<column", k, cell)
+      if k == -1:
+        return
+      if document.body[k].find('alignment="decimal"') == -1:
+        k += 1
+        continue
+      remove_option(document.body, k, 'decimal_point')
+      document.body[k] = \
+        document.body[k].replace('alignment="decimal"', 'alignment="center"')
+      k += 1
+
+
+def convert_optarg(document):
+  " Convert \\begin_inset OptArg to \\begin_inset Argument "
+  i = 0
+  while 1:
+    i = find_token(document.body, '\\begin_inset OptArg', i)
+    if i == -1:
+      return
+    document.body[i] = "\\begin_inset Argument"
+    i += 1
+
+
+def revert_argument(document):
+  " Convert \\begin_inset Argument to \\begin_inset OptArg "
+  i = 0
+  while 1:
+    i = find_token(document.body, '\\begin_inset Argument', i)
+    if i == -1:
+      return
+    document.body[i] = "\\begin_inset OptArg"
+    i += 1
+
+
+def revert_makebox(document):
+  " Convert \\makebox to TeX code "
+  i = 0
+  while 1:
+    i = find_token(document.body, '\\begin_inset Box', i)
+    if i == -1:
+      break
+    z = find_end_of_inset(document.body, i)
+    if z == -1:
+      document.warning("Malformed LyX document: Can't find end of box inset.")
+      i += 1
+      continue
+    blay = find_token(document.body, "\\begin_layout", i, z)
+    if blay == -1:
+      document.warning("Malformed LyX document: Can't find layout in box.")
+      i = z
+      continue
+    # by looking before the layout we make sure we're actually finding
+    # an option, not text.
+    j = find_token(document.body, 'use_makebox', i, blay)
+    if j == -1:
+        i = z
+        continue
+    
+    if not check_token(document.body[i], "\\begin_inset Box Frameless") \
+      or get_value(document.body, 'use_makebox', j) != 1:
+        del document.body[j]
+        i = z
+        continue
+    bend = find_end_of_layout(document.body, blay)
+    if bend == -1 or bend > z:
+        document.warning("Malformed LyX document: Can't find end of layout in box.")
+        i = z
+        continue
+    # determine the alignment
+    align = get_quoted_value(document.body, 'hor_pos', i, blay, "c")
+    # determine the width
+    length = get_quoted_value(document.body, 'width', i, blay, "50col%")
+    length = latex_length(length)[1]
+    # remove the \end_layout \end_inset pair
+    document.body[bend:z + 1] = put_cmd_in_ert("}")
+    subst = "\\makebox[" + length + "][" \
+      + align + "]{"
+    document.body[i:blay + 1] = put_cmd_in_ert(subst)
+    i += 1
+
+
+def convert_use_makebox(document):
+  " Adds use_makebox option for boxes "
+  i = 0
+  while 1:
+    i = find_token(document.body, '\\begin_inset Box', i)
+    if i == -1:
+      return
+    # all of this is to make sure we actually find the use_parbox
+    # that is an option for this box, not some text elsewhere.
+    z = find_end_of_inset(document.body, i)
+    if z == -1:
+      document.warning("Can't find end of box inset!!")
+      i += 1
+      continue
+    blay = find_token(document.body, "\\begin_layout", i, z)
+    if blay == -1:
+      document.warning("Can't find layout in box inset!!")
+      i = z
+      continue
+    # so now we are looking for use_parbox before the box's layout
+    k = find_token(document.body, 'use_parbox', i, blay)
+    if k == -1:
+      document.warning("Malformed LyX document: Can't find use_parbox statement in box.")
+      i = z
+      continue
+    document.body.insert(k + 1, "use_makebox 0")
+    i = blay + 1 # not z + 1 (box insets may be nested)
+
+
+def revert_IEEEtran(document):
+  " Convert IEEEtran layouts and styles to TeX code "
+  if document.textclass != "IEEEtran":
+    return
+  revert_flex_inset(document.body, "IEEE membership", "\\IEEEmembership")
+  revert_flex_inset(document.body, "Lowercase", "\\MakeLowercase")
+  layouts = ("Special Paper Notice", "After Title Text", "Publication ID",
+             "Page headings", "Biography without photo")
+  latexcmd = {"Special Paper Notice": "\\IEEEspecialpapernotice",
+              "After Title Text":     "\\IEEEaftertitletext",
+              "Publication ID":       "\\IEEEpubid"}
+  obsoletedby = {"Page headings":            "MarkBoth",
+                 "Biography without photo":  "BiographyNoPhoto"}
+  for layout in layouts:
+    i = 0
+    while True:
+        i = find_token(document.body, '\\begin_layout ' + layout, i)
+        if i == -1:
+          break
+        j = find_end_of_layout(document.body, i)
+        if j == -1:
+          document.warning("Malformed LyX document: Can't find end of " + layout + " layout.")
+          i += 1
+          continue
+        if layout in obsoletedby:
+          document.body[i] = "\\begin_layout " + obsoletedby[layout]
+          i = j
+          continue
+        content = lyx2latex(document, document.body[i:j + 1])
+        add_to_preamble(document, [latexcmd[layout] + "{" + content + "}"])
+        del document.body[i:j + 1]
+        # no need to reset i
+
+
+def convert_prettyref(document):
+       " Converts prettyref references to neutral formatted refs "
+       re_ref = re.compile("^\s*reference\s+\"(\w+):(\S+)\"")
+       nm_ref = re.compile("^\s*name\s+\"(\w+):(\S+)\"")
+
+       i = 0
+       while True:
+               i = find_token(document.body, "\\begin_inset CommandInset ref", i)
+               if i == -1:
+                       break
+               j = find_end_of_inset(document.body, i)
+               if j == -1:
+                       document.warning("Malformed LyX document: No end of InsetRef!")
+                       i += 1
+                       continue
+               k = find_token(document.body, "LatexCommand prettyref", i, j)
+               if k != -1:
+                       document.body[k] = "LatexCommand formatted"
+               i = j + 1
+       document.header.insert(-1, "\\use_refstyle 0")
+               
+def revert_refstyle(document):
+       " Reverts neutral formatted refs to prettyref "
+       re_ref = re.compile("^reference\s+\"(\w+):(\S+)\"")
+       nm_ref = re.compile("^\s*name\s+\"(\w+):(\S+)\"")
+
+       i = 0
+       while True:
+               i = find_token(document.body, "\\begin_inset CommandInset ref", i)
+               if i == -1:
+                       break
+               j = find_end_of_inset(document.body, i)
+               if j == -1:
+                       document.warning("Malformed LyX document: No end of InsetRef")
+                       i += 1
+                       continue
+               k = find_token(document.body, "LatexCommand formatted", i, j)
+               if k != -1:
+                       document.body[k] = "LatexCommand prettyref"
+               i = j + 1
+       i = find_token(document.header, "\\use_refstyle", 0)
+       if i != -1:
+               document.header.pop(i)
+
+def revert_nameref(document):
+  " Convert namerefs to regular references "
+  cmds = ["Nameref", "nameref"]
+  foundone = False
+  rx = re.compile(r'reference "(.*)"')
+  for cmd in cmds:
+    i = 0
+    oldcmd = "LatexCommand " + cmd
+    while 1:
+      # It seems better to look for this, as most of the reference
+      # insets won't be ones we care about.
+      i = find_token(document.body, oldcmd, i)
+      if i == -1:
+        break
+      cmdloc = i
+      i += 1
+      # Make sure it is actually in an inset!
+      # A normal line could begin with "LatexCommand nameref"!
+      val = is_in_inset(document.body, cmdloc, \
+          "\\begin_inset CommandInset ref")
+      if not val:
+          continue
+      stins, endins = val
+
+      # ok, so it is in an InsetRef
+      refline = find_token(document.body, "reference", stins, endins)
+      if refline == -1:
+        document.warning("Can't find reference for inset at line " + stinst + "!!")
+        continue
+      m = rx.match(document.body[refline])
+      if not m:
+        document.warning("Can't match reference line: " + document.body[ref])
+        continue
+      foundone = True
+      ref = m.group(1)
+      newcontent = put_cmd_in_ert('\\' + cmd + '{' + ref + '}')
+      document.body[stins:endins + 1] = newcontent
+
+  if foundone:
+    add_to_preamble(document, ["\usepackage{nameref}"])
+
+
+def remove_Nameref(document):
+  " Convert Nameref commands to nameref commands "
+  i = 0
+  while 1:
+    # It seems better to look for this, as most of the reference
+    # insets won't be ones we care about.
+    i = find_token(document.body, "LatexCommand Nameref" , i)
+    if i == -1:
+      break
+    cmdloc = i
+    i += 1
+    
+    # Make sure it is actually in an inset!
+    val = is_in_inset(document.body, cmdloc, \
+        "\\begin_inset CommandInset ref")
+    if not val:
+      continue
+    document.body[cmdloc] = "LatexCommand nameref"
+
+
+def revert_mathrsfs(document):
+    " Load mathrsfs if \mathrsfs us use in the document "
+    i = 0
+    for line in document.body:
+      if line.find("\\mathscr{") != -1:
+        add_to_preamble(document, ["\\usepackage{mathrsfs}"])
+        return
+
+
+def convert_flexnames(document):
+    "Convert \\begin_inset Flex Custom:Style to \\begin_inset Flex Style and similarly for CharStyle and Element."
+    
+    i = 0
+    rx = re.compile(r'^\\begin_inset Flex (?:Custom|CharStyle|Element):(.+)$')
+    while True:
+      i = find_token(document.body, "\\begin_inset Flex", i)
+      if i == -1:
+        return
+      m = rx.match(document.body[i])
+      if m:
+        document.body[i] = "\\begin_inset Flex " + m.group(1)
+      i += 1
+
+
+flex_insets = {
+  "Alert" : "CharStyle:Alert",
+  "Code" : "CharStyle:Code",
+  "Concepts" : "CharStyle:Concepts",
+  "E-Mail" : "CharStyle:E-Mail",
+  "Emph" : "CharStyle:Emph",
+  "Expression" : "CharStyle:Expression",
+  "Initial" : "CharStyle:Initial",
+  "Institute" : "CharStyle:Institute",
+  "Meaning" : "CharStyle:Meaning",
+  "Noun" : "CharStyle:Noun",
+  "Strong" : "CharStyle:Strong",
+  "Structure" : "CharStyle:Structure",
+  "ArticleMode" : "Custom:ArticleMode",
+  "Endnote" : "Custom:Endnote",
+  "Glosse" : "Custom:Glosse",
+  "PresentationMode" : "Custom:PresentationMode",
+  "Tri-Glosse" : "Custom:Tri-Glosse"
+}
+
+flex_elements = {
+  "Abbrev" : "Element:Abbrev",
+  "CCC-Code" : "Element:CCC-Code",
+  "Citation-number" : "Element:Citation-number",
+  "City" : "Element:City",
+  "Code" : "Element:Code",
+  "CODEN" : "Element:CODEN",
+  "Country" : "Element:Country",
+  "Day" : "Element:Day",
+  "Directory" : "Element:Directory",
+  "Dscr" : "Element:Dscr",
+  "Email" : "Element:Email",
+  "Emph" : "Element:Emph",
+  "Filename" : "Element:Filename",
+  "Firstname" : "Element:Firstname",
+  "Fname" : "Element:Fname",
+  "GuiButton" : "Element:GuiButton",
+  "GuiMenu" : "Element:GuiMenu",
+  "GuiMenuItem" : "Element:GuiMenuItem",
+  "ISSN" : "Element:ISSN",
+  "Issue-day" : "Element:Issue-day",
+  "Issue-months" : "Element:Issue-months",
+  "Issue-number" : "Element:Issue-number",
+  "KeyCap" : "Element:KeyCap",
+  "KeyCombo" : "Element:KeyCombo",
+  "Keyword" : "Element:Keyword",
+  "Literal" : "Element:Literal",
+  "MenuChoice" : "Element:MenuChoice",
+  "Month" : "Element:Month",
+  "Orgdiv" : "Element:Orgdiv",
+  "Orgname" : "Element:Orgname",
+  "Postcode" : "Element:Postcode",
+  "SS-Code" : "Element:SS-Code",
+  "SS-Title" : "Element:SS-Title",
+  "State" : "Element:State",
+  "Street" : "Element:Street",
+  "Surname" : "Element:Surname",
+  "Volume" : "Element:Volume",
+  "Year" : "Element:Year"
+}
+
+
+def revert_flexnames(document):
+  if document.backend == "latex":
+    flexlist = flex_insets
+  else:
+    flexlist = flex_elements
+  
+  rx = re.compile(r'^\\begin_inset Flex\s+(.+)$')
+  i = 0
+  while True:
+    i = find_token(document.body, "\\begin_inset Flex", i)
+    if i == -1:
+      return
+    m = rx.match(document.body[i])
+    if not m:
+      document.warning("Illegal flex inset: " + document.body[i])
+      i += 1
+      continue
+    style = m.group(1)
+    if style in flexlist:
+      document.body[i] = "\\begin_inset Flex " + flexlist[style]
+    i += 1
+
+
+def convert_mathdots(document):
+    " Load mathdots automatically "
+    i = find_token(document.header, "\\use_mhchem" , 0)
+    if i == -1:
+        i = find_token(document.header, "\\use_esint" , 0)
+    if i == -1:
+        document.warning("Malformed LyX document: Can't find \\use_mhchem.")
+        return;
+    j = find_token(document.preamble, "\\usepackage{mathdots}", 0)
+    if j == -1:
+        document.header.insert(i + 1, "\\use_mathdots 0")
+    else:
+        document.header.insert(i + 1, "\\use_mathdots 2")
+        del document.preamble[j]
+
+
+def revert_mathdots(document):
+    " Load mathdots if used in the document "
+
+    mathdots = find_token(document.header, "\\use_mathdots" , 0)
+    if mathdots == -1:
+      document.warning("No \\use_mathdots line. Assuming auto.")
+    else:
+      val = get_value(document.header, "\\use_mathdots", mathdots)
+      del document.header[mathdots]
+      try:
+        usedots = int(val)
+      except:
+        document.warning("Invalid \\use_mathdots value: " + val + ". Assuming auto.")
+        # probably usedots has not been changed, but be safe.
+        usedots = 1
+
+      if usedots == 0:
+        # do not load case
+        return
+      if usedots == 2:
+        # force load case
+        add_to_preamble(document, ["\\usepackage{mathdots}"])
+        return
+    
+    # so we are in the auto case. we want to load mathdots if \iddots is used.
+    i = 0
+    while True:
+      i = find_token(document.body, '\\begin_inset Formula', i)
+      if i == -1:
+        return
+      j = find_end_of_inset(document.body, i)
+      if j == -1:
+        document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
+        i += 1
+        continue
+      code = "\n".join(document.body[i:j])
+      if code.find("\\iddots") != -1:
+        add_to_preamble(document, ["\\@ifundefined{iddots}{\\usepackage{mathdots}}"])
+        return
+      i = j
+
+
+def convert_rule(document):
+    " Convert \\lyxline to CommandInset line. "
+    i = 0
+    
+    inset = ['\\begin_inset CommandInset line',
+      'LatexCommand rule',
+      'offset "0.5ex"',
+      'width "100line%"',
+      'height "1pt"', '',
+      '\\end_inset', '', '']
+
+    # if paragraphs are indented, we may have to unindent to get the
+    # line to be full-width.
+    indent = get_value(document.header, "\\paragraph_separation", 0)
+    have_indent = (indent == "indent")
+
+    while True:
+      i = find_token(document.body, "\\lyxline" , i)
+      if i == -1:
+        return
+
+      # we need to find out if this line follows other content
+      # in its paragraph. find its layout....
+      lastlay = find_token_backwards(document.body, "\\begin_layout", i)
+      if lastlay == -1:
+        document.warning("Can't find layout for line at " + str(i))
+        # do the best we can.
+        document.body[i:i+1] = inset
+        i += len(inset)
+        continue
+
+      # ...and look for other content before it.
+      lineisfirst = True
+      for line in document.body[lastlay + 1:i]:
+        # is it empty or a paragraph option?
+        if not line or line[0] == '\\':
+          continue
+        lineisfirst = False
+        break
+
+      if lineisfirst:
+        document.body[i:i+1] = inset
+        if indent:
+          # we need to unindent, lest the line be too long
+          document.body.insert(lastlay + 1, "\\noindent")
+        i += len(inset)
+      else:
+        # so our line is in the middle of a paragraph
+        # we need to add a new line, lest this line follow the
+        # other content on that line and run off the side of the page
+        document.body[i:i+1] = inset
+        document.body[i:i] = ["\\begin_inset Newline newline", "\\end_inset", ""]
+      i += len(inset)
+
+
+def revert_rule(document):
+    " Revert line insets to Tex code "
+    i = 0
+    while 1:
+      i = find_token(document.body, "\\begin_inset CommandInset line" , i)
+      if i == -1:
+        return
+      # find end of inset
+      j = find_token(document.body, "\\end_inset" , i)
+      if j == -1:
+        document.warning("Malformed LyX document: Can't find end of line inset.")
+        return
+      # determine the optional offset
+      offset = get_quoted_value(document.body, 'offset', i, j)
+      if offset:
+        offset = '[' + offset + ']'
+      # determine the width
+      width = get_quoted_value(document.body, 'width', i, j, "100col%")
+      width = latex_length(width)[1]
+      # determine the height
+      height = get_quoted_value(document.body, 'height', i, j, "1pt")
+      height = latex_length(height)[1]
+      # output the \rule command
+      subst = "\\rule[" + offset + "]{" + width + "}{" + height + "}"
+      document.body[i:j + 1] = put_cmd_in_ert(subst)
+      i += len(subst) - (j - i)
+
+
+def revert_diagram(document):
+  " Add the feyn package if \\Diagram is used in math "
+  i = 0
+  while True:
+    i = find_token(document.body, '\\begin_inset Formula', i)
+    if i == -1:
+      return
+    j = find_end_of_inset(document.body, i)
+    if j == -1:
+        document.warning("Malformed LyX document: Can't find end of Formula inset.")
+        return 
+    lines = "\n".join(document.body[i:j])
+    if lines.find("\\Diagram") == -1:
+      i = j
+      continue
+    add_to_preamble(document, ["\\usepackage{feyn}"])
+    # only need to do it once!
+    return
+
+chapters = ("amsbook", "book", "docbook-book", "elsart", "extbook", "extreport", 
+    "jbook", "jreport", "jsbook", "literate-book", "literate-report", "memoir", 
+    "mwbk", "mwrep", "recipebook", "report", "scrbook", "scrreprt", "svmono", 
+    "svmult", "tbook", "treport", "tufte-book")
+
+def convert_bibtex_clearpage(document):
+  " insert a clear(double)page bibliographystyle if bibtotoc option is used "
+
+  if document.textclass not in chapters:
+    return
+
+  i = find_token(document.header, '\\papersides', 0)
+  sides = 0
+  if i == -1:
+    document.warning("Malformed LyX document: Can't find papersides definition.")
+    document.warning("Assuming single sided.")
+    sides = 1
+  else:
+    val = get_value(document.header, "\\papersides", i)
+    try:
+      sides = int(val)
+    except:
+      pass
+    if sides != 1 and sides != 2:
+      document.warning("Invalid papersides value: " + val)
+      document.warning("Assuming single sided.")
+      sides = 1
+
+  j = 0
+  while True:
+    j = find_token(document.body, "\\begin_inset CommandInset bibtex", j)
+    if j == -1:
+      return
+
+    k = find_end_of_inset(document.body, j)
+    if k == -1:
+      document.warning("Can't find end of Bibliography inset at line " + str(j))
+      j += 1
+      continue
+
+    # only act if there is the option "bibtotoc"
+    val = get_value(document.body, 'options', j, k)
+    if not val:
+      document.warning("Can't find options for bibliography inset at line " + str(j))
+      j = k
+      continue
+    
+    if val.find("bibtotoc") == -1:
+      j = k
+      continue
+    
+    # so we want to insert a new page right before the paragraph that
+    # this bibliography thing is in. 
+    lay = find_token_backwards(document.body, "\\begin_layout", j)
+    if lay == -1:
+      document.warning("Can't find layout containing bibliography inset at line " + str(j))
+      j = k
+      continue
+
+    if sides == 1:
+      cmd = "clearpage"
+    else:
+      cmd = "cleardoublepage"
+    subst = ['\\begin_layout Standard',
+        '\\begin_inset Newpage ' + cmd,
+        '\\end_inset', '', '',
+        '\\end_layout', '']
+    document.body[lay:lay] = subst
+    j = k + len(subst)
+
+
+def check_passthru(document):
+  tc = document.textclass
+  ok = (tc == "literate-article" or tc == "literate-book" or tc == "literate-report")
+  if not ok:
+    mods = document.get_module_list()
+    for mod in mods:
+      if mod == "sweave" or mod == "noweb":
+        ok = True
+        break
+  return ok
+
+
+def convert_passthru(document):
+    " http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg161298.html "
+    if not check_passthru:
+      return
+    
+    rx = re.compile("\\\\begin_layout \s*(\w+)")
+    beg = 0
+    for lay in ["Chunk", "Scrap"]:
+      while True:
+        beg = find_token(document.body, "\\begin_layout " + lay, beg)
+        if beg == -1:
+          break
+        end = find_end_of_layout(document.body, beg)
+        if end == -1:
+          document.warning("Can't find end of layout at line " + str(beg))
+          beg += 1
+          continue
+
+        # we are now going to replace newline insets within this layout
+        # by new instances of this layout. so we have repeated layouts
+        # instead of newlines.
+
+        # if the paragraph has any customization, however, we do not want to
+        # do the replacement.
+        if document.body[beg + 1].startswith("\\"):
+          beg = end + 1
+          continue
+
+        ns = beg
+        while True:
+          ns = find_token(document.body, "\\begin_inset Newline newline", ns, end)
+          if ns == -1:
+            break
+          ne = find_end_of_inset(document.body, ns)
+          if ne == -1 or ne > end:
+            document.warning("Can't find end of inset at line " + str(nb))
+            ns += 1
+            continue
+          if document.body[ne + 1] == "":
+            ne += 1
+          subst = ["\\end_layout", "", "\\begin_layout " + lay]
+          document.body[ns:ne + 1] = subst
+          # now we need to adjust end, in particular, but might as well
+          # do ns properly, too
+          newlines = (ne - ns) - len(subst)
+          ns += newlines + 2
+          end += newlines + 2
+
+        # ok, we now want to find out if the next layout is the
+        # same as this one. if so, we will insert an extra copy of it
+        didit = False
+        next = find_token(document.body, "\\begin_layout", end)
+        if next != -1:
+          m = rx.match(document.body[next])
+          if m:
+            nextlay = m.group(1)
+            if nextlay == lay:
+              subst = ["\\begin_layout " + lay, "", "\\end_layout", ""]
+              document.body[next:next] = subst
+              didit = True
+        beg = end + 1
+        if didit:
+          beg += 4 # for the extra layout
+    
+
+def revert_passthru(document):
+    " http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg161298.html "
+    if not check_passthru:
+      return
+    rx = re.compile("\\\\begin_layout \s*(\w+)")
+    beg = 0
+    for lay in ["Chunk", "Scrap"]:
+      while True:
+        beg = find_token(document.body, "\\begin_layout " + lay, beg)
+        if beg == -1:
+          break
+        end = find_end_of_layout(document.body, beg)
+        if end == -1:
+          document.warning("Can't find end of layout at line " + str(beg))
+          beg += 1
+          continue
+        
+        # we now want to find out if the next layout is the
+        # same as this one. but we will need to do this over and
+        # over again.
+        while True:
+          next = find_token(document.body, "\\begin_layout", end)
+          if next == -1:
+            break
+          m = rx.match(document.body[next])
+          if not m:
+            break
+          nextlay = m.group(1)
+          if nextlay != lay:
+            break
+          # so it is the same layout again. we now want to know if it is empty.
+          # but first let's check and make sure there is no content between the
+          # two layouts. i'm not sure if that can happen or not.
+          for l in range(end + 1, next):
+            document.warning("c'" + document.body[l] + "'")
+            if document.body[l] != "":
+              document.warning("Found content between adjacent " + lay + " layouts!")
+              break
+          nextend = find_end_of_layout(document.body, next)
+          if nextend == -1:
+            document.warning("Can't find end of layout at line " + str(next))
+            break
+          empty = True
+          for l in range(next + 1, nextend):
+            document.warning("e'" + document.body[l] + "'")
+            if document.body[l] != "":
+              empty = False
+              break
+          if empty:
+            # empty layouts just get removed
+            # should we check if it's before yet another such layout?
+            del document.body[next : nextend + 1]
+            # and we do not want to check again. we know the next layout
+            # should be another Chunk and should be left as is.
+            break
+          else:
+            # if it's not empty, then we want to insert a newline in place
+            # of the layout switch
+            subst = ["\\begin_inset Newline newline", "\\end_inset", ""]
+            document.body[end : next + 1] = subst
+            # and now we have to find the end of the new, larger layout
+            newend = find_end_of_layout(document.body, beg)
+            if newend == -1:
+              document.warning("Can't find end of new layout at line " + str(beg))
+              break
+            end = newend
+        beg = end + 1
+
+
+def revert_multirowOffset(document):
+    " Revert multirow cells with offset in tables to TeX-code"
+    # this routine is the same as the revert_multirow routine except that
+    # it checks additionally for the offset
+
+    # first, let's find out if we need to do anything
+    i = find_token(document.body, '<cell multirow="3" mroffset=', 0)
+    if i == -1:
+      return
+
+    add_to_preamble(document, ["\\usepackage{multirow}"])
+
+    rgx = re.compile(r'mroffset="[^"]+?"')
+    begin_table = 0
+
+    while True:
+        # find begin/end of table
+        begin_table = find_token(document.body, '<lyxtabular version=', begin_table)
+        if begin_table == -1:
+            break
+        end_table = find_end_of(document.body, begin_table, '<lyxtabular', '</lyxtabular>')
+        if end_table == -1:
+            document.warning("Malformed LyX document: Could not find end of table.")
+            begin_table += 1
+            continue
+        # does this table have multirow?
+        i = find_token(document.body, '<cell multirow="3"', begin_table, end_table)
+        if i == -1:
+            begin_table = end_table
+            continue
+        
+        # store the number of rows and columns
+        numrows = get_option_value(document.body[begin_table], "rows")
+        numcols = get_option_value(document.body[begin_table], "columns")
+        try:
+          numrows = int(numrows)
+          numcols = int(numcols)
+        except:
+          document.warning("Unable to determine rows and columns!")
+          begin_table = end_table
+          continue
+
+        mrstarts = []
+        multirows = []
+        # collect info on rows and columns of this table.
+        begin_row = begin_table
+        for row in range(numrows):
+            begin_row = find_token(document.body, '<row>', begin_row, end_table)
+            if begin_row == -1:
+              document.warning("Can't find row " + str(row + 1))
+              break
+            end_row = find_end_of(document.body, begin_row, '<row>', '</row>')
+            if end_row == -1:
+              document.warning("Can't find end of row " + str(row + 1))
+              break
+            begin_cell = begin_row
+            multirows.append([])
+            for column in range(numcols):            
+                begin_cell = find_token(document.body, '<cell ', begin_cell, end_row)
+                if begin_cell == -1:
+                  document.warning("Can't find column " + str(column + 1) + \
+                    "in row " + str(row + 1))
+                  break
+                # NOTE 
+                # this will fail if someone puts "</cell>" in a cell, but
+                # that seems fairly unlikely.
+                end_cell = find_end_of(document.body, begin_cell, '<cell', '</cell>')
+                if end_cell == -1:
+                  document.warning("Can't find end of column " + str(column + 1) + \
+                    "in row " + str(row + 1))
+                  break
+                multirows[row].append([begin_cell, end_cell, 0])
+                if document.body[begin_cell].find('multirow="3" mroffset=') != -1:
+                  multirows[row][column][2] = 3 # begin multirow
+                  mrstarts.append([row, column])
+                elif document.body[begin_cell].find('multirow="4"') != -1:
+                  multirows[row][column][2] = 4 # in multirow
+                begin_cell = end_cell
+            begin_row = end_row
+        # end of table info collection
+
+        # work from the back to avoid messing up numbering
+        mrstarts.reverse()
+        for m in mrstarts:
+            row = m[0]
+            col = m[1]
+            # get column width
+            col_width = get_option_value(document.body[begin_table + 2 + col], "width")
+            # "0pt" means that no width is specified
+            if not col_width or col_width == "0pt":
+              col_width = "*"
+            # determine the number of cells that are part of the multirow
+            nummrs = 1
+            for r in range(row + 1, numrows):
+                if multirows[r][col][2] != 4:
+                  break
+                nummrs += 1
+                # take the opportunity to revert this line
+                lineno = multirows[r][col][0]
+                document.body[lineno] = document.body[lineno].\
+                  replace(' multirow="4" ', ' ').\
+                  replace('valignment="middle"', 'valignment="top"').\
+                  replace(' topline="true" ', ' ')
+                # remove bottom line of previous multirow-part cell
+                lineno = multirows[r-1][col][0]
+                document.body[lineno] = document.body[lineno].replace(' bottomline="true" ', ' ')
+            # revert beginning cell
+            bcell = multirows[row][col][0]
+            ecell = multirows[row][col][1]
+            offset = get_option_value(document.body[bcell], "mroffset")
+            document.body[bcell] = document.body[bcell].\
+              replace(' multirow="3" ', ' ').\
+              replace('valignment="middle"', 'valignment="top"')
+            # remove mroffset option
+            document.body[bcell] = rgx.sub('', document.body[bcell])
+            
+            blay = find_token(document.body, "\\begin_layout", bcell, ecell)
+            if blay == -1:
+              document.warning("Can't find layout for cell!")
+              continue
+            bend = find_end_of_layout(document.body, blay)
+            if bend == -1:
+              document.warning("Can't find end of layout for cell!")
+              continue
+            # do the later one first, so as not to mess up the numbering
+            # we are wrapping the whole cell in this ert
+            # so before the end of the layout...
+            document.body[bend:bend] = put_cmd_in_ert("}")
+            # ...and after the beginning
+            document.body[blay + 1:blay + 1] = \
+              put_cmd_in_ert("\\multirow{" + str(nummrs) + "}{" + col_width + "}[" \
+                  + offset + "]{")
+
+        # on to the next table
+        begin_table = end_table
+
+
+def revert_script(document):
+    " Convert subscript/superscript inset to TeX code "
+    i = 0
+    foundsubscript = False
+    while 1:
+        i = find_token(document.body, '\\begin_inset script', i)
+        if i == -1:
+            break
+        z = find_end_of_inset(document.body, i)
+        if z == -1:
+            document.warning("Malformed LyX document: Can't find end of script inset.")
+            i += 1
+            continue
+        blay = find_token(document.body, "\\begin_layout", i, z)
+        if blay == -1:
+            document.warning("Malformed LyX document: Can't find layout in script inset.")
+            i = z
+            continue
+
+        if check_token(document.body[i], "\\begin_inset script subscript"):
+            subst = '\\textsubscript{'
+            foundsubscript = True
+        elif check_token(document.body[i], "\\begin_inset script superscript"):
+            subst = '\\textsuperscript{'
+        else:
+            document.warning("Malformed LyX document: Unknown type of script inset.")
+            i = z
+            continue
+        bend = find_end_of_layout(document.body, blay)
+        if bend == -1 or bend > z:
+            document.warning("Malformed LyX document: Can't find end of layout in script inset.")
+            i = z
+            continue
+        # remove the \end_layout \end_inset pair
+        document.body[bend:z + 1] = put_cmd_in_ert("}")
+        document.body[i:blay + 1] = put_cmd_in_ert(subst)
+        i += 1
+    # these classes provide a \textsubscript command:
+    # FIXME: Would be nice if we could use the information of the .layout file here
+    classes = ["memoir", "scrartcl", "scrbook", "scrlttr2", "scrreprt"]
+    if foundsubscript and find_token_exact(classes, document.textclass, 0) == -1:
+        add_to_preamble(document, ['\\usepackage{subscript}'])
+
+
+def convert_use_xetex(document):
+    " convert \\use_xetex to \\use_non_tex_fonts "
+    i = 0
+    i = find_token(document.header, "\\use_xetex", 0)
+    if i == -1:
+        return
+    
+    val = get_value(document.header, "\\use_xetex", 0)
+    document.header[i] = "\\use_non_tex_fonts " + val
+
+
+def revert_use_xetex(document):
+    " revert \\use_non_tex_fonts to \\use_xetex "
+    i = 0
+    i = find_token(document.header, "\\use_non_tex_fonts", 0)
+    if i == -1:
+        document.warning("Malformed document. No \\use_non_tex_fonts param!")
+        return
+
+    val = get_value(document.header, "\\use_non_tex_fonts", 0)
+    document.header[i] = "\\use_xetex " + val
+
+
+def revert_labeling(document):
+    koma = ("scrartcl", "scrarticle-beamer", "scrbook", "scrlettr",
+        "scrlttr2", "scrreprt")
+    if document.textclass in koma:
+        return
+    i = 0
+    while True:
+        i = find_token_exact(document.body, "\\begin_layout Labeling", i)
+        if i == -1:
+            return
+        document.body[i] = "\\begin_layout List"
+
+
+def revert_langpack(document):
+    " revert \\language_package parameter "
+    i = 0
+    i = find_token(document.header, "\\language_package", 0)
+    if i == -1:
+        document.warning("Malformed document. No \\language_package param!")
+        return
+
+    del document.header[i]
+
+
+def convert_langpack(document):
+    " Add \\language_package parameter "
+    i = find_token(document.header, "\language" , 0)
+    if i == -1:
+        document.warning("Malformed document. No \\language defined!")
+        return
+
+    document.header.insert(i + 1, "\\language_package default")
+
+
+def revert_tabularwidth(document):
+  i = 0
+  while True:
+    i = find_token(document.body, "\\begin_inset Tabular", i)
+    if i == -1:
+      return
+    j = find_end_of_inset(document.body, i)
+    if j == -1:
+      document.warning("Unable to find end of Tabular inset at line " + str(i))
+      i += 1
+      continue
+    i += 1
+    features = find_token(document.body, "<features", i, j)
+    if features == -1:
+      document.warning("Can't find any features in Tabular inset at line " + str(i))
+      i = j
+      continue
+    if document.body[features].find('alignment="tabularwidth"') != -1:
+      remove_option(document.body, features, 'tabularwidth')
+
+def revert_html_css_as_file(document):
+  if not del_token(document.header, '\\html_css_as_file', 0):
+    document.warning("Malformed LyX document: Missing \\html_css_as_file.")
 
 
 ##
 
 
 ##
@@ -1535,7 +2483,7 @@ convert = [[346, []],
            [368, []],
            [369, [convert_author_id]],
            [370, []],
            [368, []],
            [369, [convert_author_id]],
            [370, []],
-           [371, []],
+           [371, [convert_mhchem]],
            [372, []],
            [373, [merge_gbrief]],
            [374, []],
            [372, []],
            [373, [merge_gbrief]],
            [374, []],
@@ -1550,10 +2498,64 @@ convert = [[346, []],
            [383, []],
            [384, []],
            [385, []],
            [383, []],
            [384, []],
            [385, []],
-           [386, []]
-          ]
-
-revert =  [[385, [revert_lyx_version]],
+           [386, []],
+           [387, []],
+           [388, []],
+           [389, [convert_html_quotes]],
+           [390, []],
+           [391, []],
+           [392, []],
+           [393, [convert_optarg]],
+           [394, [convert_use_makebox]],
+           [395, []],
+           [396, []],
+           [397, [remove_Nameref]],
+           [398, []],
+           [399, [convert_mathdots]],
+           [400, [convert_rule]],
+           [401, []],
+           [402, [convert_bibtex_clearpage]],
+           [403, [convert_flexnames]],
+           [404, [convert_prettyref]],
+           [405, []],
+           [406, [convert_passthru]],
+           [407, []],
+           [408, []],
+           [409, [convert_use_xetex]],
+           [410, []],
+           [411, [convert_langpack]],
+           [412, []],
+           [413, []]
+]
+
+revert =  [[412, [revert_html_css_as_file]],
+           [411, [revert_tabularwidth]],
+           [410, [revert_langpack]],
+           [409, [revert_labeling]],
+           [408, [revert_use_xetex]],
+           [407, [revert_script]],
+           [406, [revert_multirowOffset]],
+           [405, [revert_passthru]],
+           [404, []],
+           [403, [revert_refstyle]],
+           [402, [revert_flexnames]],
+           [401, []],
+           [400, [revert_diagram]],
+           [399, [revert_rule]],
+           [398, [revert_mathdots]],
+           [397, [revert_mathrsfs]],
+           [396, []],
+           [395, [revert_nameref]],
+           [394, [revert_DIN_C_pagesizes]],
+           [393, [revert_makebox]],
+           [392, [revert_argument]],
+           [391, []],
+           [390, [revert_align_decimal, revert_IEEEtran]],
+           [389, [revert_output_sync]],
+           [388, [revert_html_quotes]],
+           [387, [revert_pagesizes]],
+           [386, [revert_math_scale]],
+           [385, [revert_lyx_version]],
            [384, [revert_shadedboxcolor]],
            [383, [revert_fontcolor]],
            [382, [revert_turkmen]],
            [384, [revert_shadedboxcolor]],
            [383, [revert_fontcolor]],
            [382, [revert_turkmen]],
@@ -1583,7 +2585,7 @@ revert =  [[385, [revert_lyx_version]],
            [358, [revert_nomencl_width]],
            [357, [revert_custom_processors]],
            [356, [revert_ulinelatex]],
            [358, [revert_nomencl_width]],
            [357, [revert_custom_processors]],
            [356, [revert_ulinelatex]],
-           [355, [revert_uulinewave]],
+           [355, []],
            [354, [revert_strikeout]],
            [353, [revert_printindexall]],
            [352, [revert_subindex]],
            [354, [revert_strikeout]],
            [353, [revert_printindexall]],
            [352, [revert_subindex]],