]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_2_0.py
lyx2lyx/lyx_2_0.py: correct LateX units - there are no units named \pagexxxx
[lyx.git] / lib / lyx2lyx / lyx_2_0.py
1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # -*- coding: utf-8 -*-
4 # Copyright (C) 2008 José Matos  <jamatos@lyx.org>
5 #
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 """ Convert files to the file format generated by lyx 2.0"""
21
22 import re, string
23 import unicodedata
24 import sys, os
25
26 from parser_tools import find_token, find_end_of, find_tokens, get_value, get_value_string
27
28 ####################################################################
29 # Private helper functions
30
31 def remove_option(document, m, option):
32     l = document.body[m].find(option)
33     if l != -1:
34         val = document.body[m][l:].split('"')[1]
35         document.body[m] = document.body[m][:l-1] + document.body[m][l+len(option + '="' + val + '"'):]
36     return l
37
38 def find_end_of_inset(lines, i):
39     " Find end of inset, where lines[i] is included."
40     return find_end_of(lines, i, "\\begin_inset", "\\end_inset")
41
42
43 def add_to_preamble(document, text):
44     """ Add text to the preamble if it is not already there.
45     Only the first line is checked!"""
46
47     if find_token(document.preamble, text[0], 0) != -1:
48         return
49
50     document.preamble.extend(text)
51
52
53 def insert_to_preamble(index, document, text):
54     """ Insert text to the preamble at a given line"""
55
56     document.preamble.insert(index, text)
57
58
59 def read_unicodesymbols():
60     " Read the unicodesymbols list of unicode characters and corresponding commands."
61     pathname = os.path.abspath(os.path.dirname(sys.argv[0]))
62     fp = open(os.path.join(pathname.strip('lyx2lyx'), 'unicodesymbols'))
63     spec_chars = []
64     # Two backslashes, followed by some non-word character, and then a character
65     # in brackets. The idea is to check for constructs like: \"{u}, which is how
66     # they are written in the unicodesymbols file; but they can also be written
67     # as: \"u or even \" u.
68     r = re.compile(r'\\\\(\W)\{(\w)\}')
69     for line in fp.readlines():
70         if line[0] != '#' and line.strip() != "":
71             line=line.replace(' "',' ') # remove all quotation marks with spaces before
72             line=line.replace('" ',' ') # remove all quotation marks with spaces after
73             line=line.replace(r'\"','"') # replace \" by " (for characters with diaeresis)
74             try:
75                 [ucs4,command,dead] = line.split(None,2)
76                 if command[0:1] != "\\":
77                     continue
78                 spec_chars.append([command, unichr(eval(ucs4))])
79             except:
80                 continue
81             m = r.match(command)
82             if m != None:
83                 command = "\\\\"
84                 # If the character is a double-quote, then we need to escape it, too,
85                 # since it is done that way in the LyX file.
86                 if m.group(1) == "\"":
87                     command += "\\"
88                 commandbl = command
89                 command += m.group(1) + m.group(2)
90                 commandbl += m.group(1) + ' ' + m.group(2)
91                 spec_chars.append([command, unichr(eval(ucs4))])
92                 spec_chars.append([commandbl, unichr(eval(ucs4))])
93     fp.close()
94     return spec_chars
95
96
97 unicode_reps = read_unicodesymbols()
98
99
100 # DO NOT USE THIS ROUTINE ANY MORE. Better yet, replace the uses that
101 # have been made of it with uses of put_cmd_in_ert.
102 def old_put_cmd_in_ert(string):
103     for rep in unicode_reps:
104         string = string.replace(rep[1], rep[0].replace('\\\\', '\\'))
105     string = string.replace('\\', "\\backslash\n")
106     string = "\\begin_inset ERT\nstatus collapsed\n\\begin_layout Plain Layout\n" \
107       + string + "\n\\end_layout\n\\end_inset"
108     return string
109
110
111 # This routine wraps some content in an ERT inset. It returns a 
112 # LIST of strings. This is how lyx2lyx works: with a list of strings, 
113 # each representing a line of a LyX file. Embedded newlines confuse
114 # lyx2lyx very much.
115 # For this same reason, we expect as input a LIST of strings, not
116 # something with embedded newlines. That said, if any of your strings
117 # do have embedded newlines, the string will eventually get split on
118 # them and you'll get a list back.
119 #
120 # A call to this routine will often go something like this:
121 #   i = find_token('\\begin_inset FunkyInset', ...)
122 #   ...
123 #   j = find_end_of_inset(document.body, i)
124 #   content = ...extract content from insets
125 #   ert = put_cmd_in_ert(content)
126 #   document.body[i:j] = ert
127 # Now, before we continue, we need to reset i appropriately. Normally,
128 # this would be: 
129 #   i += len(ert)
130 # That puts us right after the ERT we just inserted.
131 def put_cmd_in_ert(strlist):
132     ret = ["\\begin_inset ERT", "status collapsed", "\\begin_layout Plain Layout", ""]
133     # Despite the warnings just given, it will be faster for us to work
134     # with a single string internally. That way, we only go through the
135     # unicode_reps loop once.
136     s = "\n".join(strlist)
137     for rep in unicode_reps:
138         s = s.replace(rep[1], rep[0].replace('\\\\', '\\'))
139     s = s.replace('\\', "\\backslash\n")
140     ret += s.splitlines()
141     ret += ["\\end_layout", "\\end_inset"]
142     return ret
143
144             
145 def lyx2latex(document, lines):
146     'Convert some LyX stuff into corresponding LaTeX stuff, as best we can.'
147     # clean up multiline stuff
148     content = ""
149     ert_end = 0
150
151     for curline in range(len(lines)):
152       line = lines[curline]
153       if line.startswith("\\begin_inset ERT"):
154           # We don't want to replace things inside ERT, so figure out
155           # where the end of the inset is.
156           ert_end = find_end_of_inset(lines, curline + 1)
157           continue
158       elif line.startswith("\\begin_inset Formula"):
159           line = line[20:]
160       elif line.startswith("\\begin_inset Quotes"):
161           # For now, we do a very basic reversion. Someone who understands
162           # quotes is welcome to fix it up.
163           qtype = line[20:].strip()
164           # lang = qtype[0]
165           side = qtype[1]
166           dbls = qtype[2]
167           if side == "l":
168               if dbls == "d":
169                   line = "``"
170               else:
171                   line = "`"
172           else:
173               if dbls == "d":
174                   line = "''"
175               else:
176                   line = "'"
177       elif line.isspace() or \
178             line.startswith("\\begin_layout") or \
179             line.startswith("\\end_layout") or \
180             line.startswith("\\begin_inset") or \
181             line.startswith("\\end_inset") or \
182             line.startswith("\\lang") or \
183             line.strip() == "status collapsed" or \
184             line.strip() == "status open":
185           #skip all that stuff
186           continue
187
188       # this needs to be added to the preamble because of cases like
189       # \textmu, \textbackslash, etc.
190       add_to_preamble(document, ['% added by lyx2lyx for converted index entries',
191                                  '\\@ifundefined{textmu}',
192                                  ' {\\usepackage{textcomp}}{}'])
193       # a lossless reversion is not possible
194       # try at least to handle some common insets and settings
195       if ert_end >= curline:
196           line = line.replace(r'\backslash', r'\\')
197       else:
198           line = line.replace('&', '\\&{}')
199           line = line.replace('#', '\\#{}')
200           line = line.replace('^', '\\^{}')
201           line = line.replace('%', '\\%{}')
202           line = line.replace('_', '\\_{}')
203           line = line.replace('$', '\\${}')
204
205           # Do the LyX text --> LaTeX conversion
206           for rep in unicode_reps:
207             line = line.replace(rep[1], rep[0] + "{}")
208           line = line.replace(r'\backslash', r'\textbackslash{}')
209           line = line.replace(r'\series bold', r'\bfseries{}').replace(r'\series default', r'\mdseries{}')
210           line = line.replace(r'\shape italic', r'\itshape{}').replace(r'\shape smallcaps', r'\scshape{}')
211           line = line.replace(r'\shape slanted', r'\slshape{}').replace(r'\shape default', r'\upshape{}')
212           line = line.replace(r'\emph on', r'\em{}').replace(r'\emph default', r'\em{}')
213           line = line.replace(r'\noun on', r'\scshape{}').replace(r'\noun default', r'\upshape{}')
214           line = line.replace(r'\bar under', r'\underbar{').replace(r'\bar default', r'}')
215           line = line.replace(r'\family sans', r'\sffamily{}').replace(r'\family default', r'\normalfont{}')
216           line = line.replace(r'\family typewriter', r'\ttfamily{}').replace(r'\family roman', r'\rmfamily{}')
217           line = line.replace(r'\InsetSpace ', r'').replace(r'\SpecialChar ', r'')
218       content += line
219     return content
220
221
222 def latex_length(string):
223     'Convert lengths to their LaTeX representation.'
224     i = 0
225     percent = False
226     # the string has the form
227     # ValueUnit+ValueUnit-ValueUnit or
228     # ValueUnit+-ValueUnit
229     # the + and - (glue lengths) are optional
230     # the + always precedes the -
231
232     # Convert relative lengths to LaTeX units
233     units = {"text%":"\\textwidth", "col%":"\\columnwidth",
234              "page%":"\\paperwidth", "line%":"\\linewidth",
235              "theight%":"\\textheight", "pheight%":"\\paperheight"}
236     for unit in units.keys():
237         i = string.find(unit)
238         if i != -1:
239             percent = True
240             minus = string.rfind("-", 1, i)
241             plus = string.rfind("+", 0, i)
242             latex_unit = units[unit]
243             if plus == -1 and minus == -1:
244                 value = string[:i]
245                 value = str(float(value)/100)
246                 end = string[i + len(unit):]
247                 string = value + latex_unit + end
248             if plus > minus:
249                 value = string[plus+1:i]
250                 value = str(float(value)/100)
251                 begin = string[:plus+1]
252                 end = string[i+len(unit):]
253                 string = begin + value + latex_unit + end
254             if plus < minus:
255                 value = string[minus+1:i]
256                 value = str(float(value)/100)
257                 begin = string[:minus+1]
258                 string = begin + value + latex_unit
259
260     # replace + and -, but only if the - is not the first character
261     string = string[0] + string[1:].replace("+", " plus ").replace("-", " minus ")
262     # handle the case where "+-1mm" was used, because LaTeX only understands
263     # "plus 1mm minus 1mm"
264     if string.find("plus  minus"):
265         lastvaluepos = string.rfind(" ")
266         lastvalue = string[lastvaluepos:]
267         string = string.replace("  ", lastvalue + " ")
268     if percent ==  False:
269         return "False," + string
270     else:
271         return "True," + string
272         
273
274 ####################################################################
275
276
277 def revert_swiss(document):
278     " Set language german-ch to ngerman "
279     i = 0
280     if document.language == "german-ch":
281         document.language = "ngerman"
282         i = find_token(document.header, "\\language", 0)
283         if i != -1:
284             document.header[i] = "\\language ngerman"
285     j = 0
286     while True:
287         j = find_token(document.body, "\\lang german-ch", j)
288         if j == -1:
289             return
290         document.body[j] = document.body[j].replace("\\lang german-ch", "\\lang ngerman")
291         j = j + 1
292
293
294 def revert_tabularvalign(document):
295    " Revert the tabular valign option "
296    i = 0
297    while True:
298        i = find_token(document.body, "\\begin_inset Tabular", i)
299        if i == -1:
300            return
301        j = find_token(document.body, "</cell>", i)
302        if j == -1:
303            document.warning("Malformed LyX document: Could not find end of tabular cell.")
304            i = j
305            continue
306        # don't set a box for longtables, only delete tabularvalignment
307        # the alignment is 2 lines below \\begin_inset Tabular
308        p = document.body[i+2].find("islongtable")
309        if p > -1:
310            q = document.body[i+2].find("tabularvalignment")
311            if q > -1:
312                document.body[i+2] = document.body[i+2][:q-1]
313                document.body[i+2] = document.body[i+2] + '>'
314            i = i + 1
315
316        # when no longtable
317        if p == -1:
318          tabularvalignment = 'c'
319          # which valignment is specified?
320          m = document.body[i+2].find('tabularvalignment="top"')
321          if m > -1:
322              tabularvalignment = 't'
323          m = document.body[i+2].find('tabularvalignment="bottom"')
324          if m > -1:
325              tabularvalignment = 'b'
326          # delete tabularvalignment
327          q = document.body[i+2].find("tabularvalignment")
328          if q > -1:
329              document.body[i+2] = document.body[i+2][:q-1]
330              document.body[i+2] = document.body[i+2] + '>'
331
332          # don't add a box when centered
333          if tabularvalignment == 'c':
334              i = j
335              continue
336          subst = ['\\end_layout', '\\end_inset']
337          document.body[j:j] = subst # just inserts those lines
338          subst = ['\\begin_inset Box Frameless',
339              'position "' + tabularvalignment +'"',
340              'hor_pos "c"',
341              'has_inner_box 1',
342              'inner_pos "c"',
343              'use_parbox 0',
344              # we don't know the width, assume 50%
345              'width "50col%"',
346              'special "none"',
347              'height "1in"',
348              'height_special "totalheight"',
349              'status open',
350              '',
351              '\\begin_layout Plain Layout']
352          document.body[i:i] = subst # this just inserts the array at i
353          i += len(subst) + 2 # adjust i to save a few cycles
354
355
356 def revert_phantom(document):
357     " Reverts phantom to ERT "
358     i = 0
359     j = 0
360     while True:
361       i = find_token(document.body, "\\begin_inset Phantom Phantom", i)
362       if i == -1:
363           return
364       substi = document.body[i].replace('\\begin_inset Phantom Phantom', \
365                 '\\begin_inset ERT\nstatus collapsed\n\n' \
366                 '\\begin_layout Plain Layout\n\n\n\\backslash\n' \
367                 'phantom{\n\\end_layout\n\n\\end_inset\n')
368       substi = substi.split('\n')
369       document.body[i : i+4] = substi
370       i += len(substi)
371       j = find_token(document.body, "\\end_layout", i)
372       if j == -1:
373           document.warning("Malformed LyX document: Could not find end of Phantom inset.")
374           return
375       substj = document.body[j].replace('\\end_layout', \
376                 '\\size default\n\n\\begin_inset ERT\nstatus collapsed\n\n' \
377                 '\\begin_layout Plain Layout\n\n' \
378                 '}\n\\end_layout\n\n\\end_inset\n')
379       substj = substj.split('\n')
380       document.body[j : j+4] = substj
381       i += len(substj)
382
383
384 def revert_hphantom(document):
385     " Reverts hphantom to ERT "
386     i = 0
387     j = 0
388     while True:
389       i = find_token(document.body, "\\begin_inset Phantom HPhantom", i)
390       if i == -1:
391           return
392       substi = document.body[i].replace('\\begin_inset Phantom HPhantom', \
393                 '\\begin_inset ERT\nstatus collapsed\n\n' \
394                 '\\begin_layout Plain Layout\n\n\n\\backslash\n' \
395                 'hphantom{\n\\end_layout\n\n\\end_inset\n')
396       substi = substi.split('\n')
397       document.body[i : i+4] = substi
398       i += len(substi)
399       j = find_token(document.body, "\\end_layout", i)
400       if j == -1:
401           document.warning("Malformed LyX document: Could not find end of HPhantom inset.")
402           return
403       substj = document.body[j].replace('\\end_layout', \
404                 '\\size default\n\n\\begin_inset ERT\nstatus collapsed\n\n' \
405                 '\\begin_layout Plain Layout\n\n' \
406                 '}\n\\end_layout\n\n\\end_inset\n')
407       substj = substj.split('\n')
408       document.body[j : j+4] = substj
409       i += len(substj)
410
411
412 def revert_vphantom(document):
413     " Reverts vphantom to ERT "
414     i = 0
415     j = 0
416     while True:
417       i = find_token(document.body, "\\begin_inset Phantom VPhantom", i)
418       if i == -1:
419           return
420       substi = document.body[i].replace('\\begin_inset Phantom VPhantom', \
421                 '\\begin_inset ERT\nstatus collapsed\n\n' \
422                 '\\begin_layout Plain Layout\n\n\n\\backslash\n' \
423                 'vphantom{\n\\end_layout\n\n\\end_inset\n')
424       substi = substi.split('\n')
425       document.body[i : i+4] = substi
426       i += len(substi)
427       j = find_token(document.body, "\\end_layout", i)
428       if j == -1:
429           document.warning("Malformed LyX document: Could not find end of VPhantom inset.")
430           return
431       substj = document.body[j].replace('\\end_layout', \
432                 '\\size default\n\n\\begin_inset ERT\nstatus collapsed\n\n' \
433                 '\\begin_layout Plain Layout\n\n' \
434                 '}\n\\end_layout\n\n\\end_inset\n')
435       substj = substj.split('\n')
436       document.body[j : j+4] = substj
437       i += len(substj)
438
439
440 def revert_xetex(document):
441     " Reverts documents that use XeTeX "
442     i = find_token(document.header, '\\use_xetex', 0)
443     if i == -1:
444         document.warning("Malformed LyX document: Missing \\use_xetex.")
445         return
446     if get_value(document.header, "\\use_xetex", i) == 'false':
447         del document.header[i]
448         return
449     del document.header[i]
450     # 1.) set doc encoding to utf8-plain
451     i = find_token(document.header, "\\inputencoding", 0)
452     if i == -1:
453         document.warning("Malformed LyX document: Missing \\inputencoding.")
454     document.header[i] = "\\inputencoding utf8-plain"
455     # 2.) check font settings
456     l = find_token(document.header, "\\font_roman", 0)
457     if l == -1:
458         document.warning("Malformed LyX document: Missing \\font_roman.")
459     line = document.header[l]
460     l = re.compile(r'\\font_roman (.*)$')
461     m = l.match(line)
462     roman = m.group(1)
463     l = find_token(document.header, "\\font_sans", 0)
464     if l == -1:
465         document.warning("Malformed LyX document: Missing \\font_sans.")
466     line = document.header[l]
467     l = re.compile(r'\\font_sans (.*)$')
468     m = l.match(line)
469     sans = m.group(1)
470     l = find_token(document.header, "\\font_typewriter", 0)
471     if l == -1:
472         document.warning("Malformed LyX document: Missing \\font_typewriter.")
473     line = document.header[l]
474     l = re.compile(r'\\font_typewriter (.*)$')
475     m = l.match(line)
476     typewriter = m.group(1)
477     osf = get_value(document.header, '\\font_osf', 0) == "true"
478     sf_scale = float(get_value(document.header, '\\font_sf_scale', 0))
479     tt_scale = float(get_value(document.header, '\\font_tt_scale', 0))
480     # 3.) set preamble stuff
481     pretext = '%% This document must be processed with xelatex!\n'
482     pretext += '\\usepackage{fontspec}\n'
483     if roman != "default":
484         pretext += '\\setmainfont[Mapping=tex-text]{' + roman + '}\n'
485     if sans != "default":
486         pretext += '\\setsansfont['
487         if sf_scale != 100:
488             pretext += 'Scale=' + str(sf_scale / 100) + ','
489         pretext += 'Mapping=tex-text]{' + sans + '}\n'
490     if typewriter != "default":
491         pretext += '\\setmonofont'
492         if tt_scale != 100:
493             pretext += '[Scale=' + str(tt_scale / 100) + ']'
494         pretext += '{' + typewriter + '}\n'
495     if osf:
496         pretext += '\\defaultfontfeatures{Numbers=OldStyle}\n'
497     pretext += '\usepackage{xunicode}\n'
498     pretext += '\usepackage{xltxtra}\n'
499     insert_to_preamble(0, document, pretext)
500     # 4.) reset font settings
501     i = find_token(document.header, "\\font_roman", 0)
502     if i == -1:
503         document.warning("Malformed LyX document: Missing \\font_roman.")
504     document.header[i] = "\\font_roman default"
505     i = find_token(document.header, "\\font_sans", 0)
506     if i == -1:
507         document.warning("Malformed LyX document: Missing \\font_sans.")
508     document.header[i] = "\\font_sans default"
509     i = find_token(document.header, "\\font_typewriter", 0)
510     if i == -1:
511         document.warning("Malformed LyX document: Missing \\font_typewriter.")
512     document.header[i] = "\\font_typewriter default"
513     i = find_token(document.header, "\\font_osf", 0)
514     if i == -1:
515         document.warning("Malformed LyX document: Missing \\font_osf.")
516     document.header[i] = "\\font_osf false"
517     i = find_token(document.header, "\\font_sc", 0)
518     if i == -1:
519         document.warning("Malformed LyX document: Missing \\font_sc.")
520     document.header[i] = "\\font_sc false"
521     i = find_token(document.header, "\\font_sf_scale", 0)
522     if i == -1:
523         document.warning("Malformed LyX document: Missing \\font_sf_scale.")
524     document.header[i] = "\\font_sf_scale 100"
525     i = find_token(document.header, "\\font_tt_scale", 0)
526     if i == -1:
527         document.warning("Malformed LyX document: Missing \\font_tt_scale.")
528     document.header[i] = "\\font_tt_scale 100"
529
530
531 def revert_outputformat(document):
532     " Remove default output format param "
533     i = find_token(document.header, '\\default_output_format', 0)
534     if i == -1:
535         document.warning("Malformed LyX document: Missing \\default_output_format.")
536         return
537     del document.header[i]
538
539
540 def revert_backgroundcolor(document):
541     " Reverts background color to preamble code "
542     i = 0
543     colorcode = ""
544     while True:
545       i = find_token(document.header, "\\backgroundcolor", i)
546       if i == -1:
547           return
548       colorcode = get_value(document.header, '\\backgroundcolor', 0)
549       del document.header[i]
550       # don't clutter the preamble if backgroundcolor is not set
551       if colorcode == "#ffffff":
552           continue
553       # the color code is in the form #rrggbb where every character denotes a hex number
554       # convert the string to an int
555       red = string.atoi(colorcode[1:3],16)
556       # we want the output "0.5" for the value "127" therefore add here
557       if red != 0:
558           red = red + 1
559       redout = float(red) / 256
560       green = string.atoi(colorcode[3:5],16)
561       if green != 0:
562           green = green + 1
563       greenout = float(green) / 256
564       blue = string.atoi(colorcode[5:7],16)
565       if blue != 0:
566           blue = blue + 1
567       blueout = float(blue) / 256
568       # write the preamble
569       insert_to_preamble(0, document,
570                            '% Commands inserted by lyx2lyx to set the background color\n'
571                            + '\\@ifundefined{definecolor}{\\usepackage{color}}{}\n'
572                            + '\\definecolor{page_backgroundcolor}{rgb}{'
573                            + str(redout) + ', ' + str(greenout)
574                            + ', ' + str(blueout) + '}\n'
575                            + '\\pagecolor{page_backgroundcolor}\n')
576
577
578 def revert_splitindex(document):
579     " Reverts splitindex-aware documents "
580     i = find_token(document.header, '\\use_indices', 0)
581     if i == -1:
582         document.warning("Malformed LyX document: Missing \\use_indices.")
583         return
584     indices = get_value(document.header, "\\use_indices", i)
585     preamble = ""
586     if indices == "true":
587          preamble += "\\usepackage{splitidx}\n"
588     del document.header[i]
589     i = 0
590     while True:
591         i = find_token(document.header, "\\index", i)
592         if i == -1:
593             break
594         k = find_token(document.header, "\\end_index", i)
595         if k == -1:
596             document.warning("Malformed LyX document: Missing \\end_index.")
597             return
598         line = document.header[i]
599         l = re.compile(r'\\index (.*)$')
600         m = l.match(line)
601         iname = m.group(1)
602         ishortcut = get_value(document.header, '\\shortcut', i, k)
603         if ishortcut != "" and indices == "true":
604             preamble += "\\newindex[" + iname + "]{" + ishortcut + "}\n"
605         del document.header[i:k+1]
606         i = 0
607     if preamble != "":
608         insert_to_preamble(0, document, preamble)
609     i = 0
610     while True:
611         i = find_token(document.body, "\\begin_inset Index", i)
612         if i == -1:
613             break
614         line = document.body[i]
615         l = re.compile(r'\\begin_inset Index (.*)$')
616         m = l.match(line)
617         itype = m.group(1)
618         if itype == "idx" or indices == "false":
619             document.body[i] = "\\begin_inset Index"
620         else:
621             k = find_end_of_inset(document.body, i)
622             if k == -1:
623                  return
624             content = lyx2latex(document, document.body[i:k])
625             # escape quotes
626             content = content.replace('"', r'\"')
627             subst = [old_put_cmd_in_ert("\\sindex[" + itype + "]{" + content + "}")]
628             document.body[i:k+1] = subst
629         i = i + 1
630     i = 0
631     while True:
632         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
633         if i == -1:
634             return
635         k = find_end_of_inset(document.body, i)
636         ptype = get_value(document.body, 'type', i, k).strip('"')
637         if ptype == "idx":
638             j = find_token(document.body, "type", i, k)
639             del document.body[j]
640         elif indices == "false":
641             del document.body[i:k+1]
642         else:
643             subst = [old_put_cmd_in_ert("\\printindex[" + ptype + "]{}")]
644             document.body[i:k+1] = subst
645         i = i + 1
646
647
648 def convert_splitindex(document):
649     " Converts index and printindex insets to splitindex-aware format "
650     i = 0
651     while True:
652         i = find_token(document.body, "\\begin_inset Index", i)
653         if i == -1:
654             break
655         document.body[i] = document.body[i].replace("\\begin_inset Index",
656             "\\begin_inset Index idx")
657         i = i + 1
658     i = 0
659     while True:
660         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
661         if i == -1:
662             return
663         if document.body[i + 1].find('LatexCommand printindex') == -1:
664             document.warning("Malformed LyX document: Incomplete printindex inset.")
665             return
666         subst = ["LatexCommand printindex", 
667             "type \"idx\""]
668         document.body[i + 1:i + 2] = subst
669         i = i + 1
670
671
672 def revert_subindex(document):
673     " Reverts \\printsubindex CommandInset types "
674     i = find_token(document.header, '\\use_indices', 0)
675     if i == -1:
676         document.warning("Malformed LyX document: Missing \\use_indices.")
677         return
678     indices = get_value(document.header, "\\use_indices", i)
679     i = 0
680     while True:
681         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
682         if i == -1:
683             return
684         k = find_end_of_inset(document.body, i)
685         ctype = get_value(document.body, 'LatexCommand', i, k)
686         if ctype != "printsubindex":
687             i = i + 1
688             continue
689         ptype = get_value(document.body, 'type', i, k).strip('"')
690         if indices == "false":
691             del document.body[i:k+1]
692         else:
693             subst = [old_put_cmd_in_ert("\\printsubindex[" + ptype + "]{}")]
694             document.body[i:k+1] = subst
695         i = i + 1
696
697
698 def revert_printindexall(document):
699     " Reverts \\print[sub]index* CommandInset types "
700     i = find_token(document.header, '\\use_indices', 0)
701     if i == -1:
702         document.warning("Malformed LyX document: Missing \\use_indices.")
703         return
704     indices = get_value(document.header, "\\use_indices", i)
705     i = 0
706     while True:
707         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
708         if i == -1:
709             return
710         k = find_end_of_inset(document.body, i)
711         ctype = get_value(document.body, 'LatexCommand', i, k)
712         if ctype != "printindex*" and ctype != "printsubindex*":
713             i = i + 1
714             continue
715         if indices == "false":
716             del document.body[i:k+1]
717         else:
718             subst = [old_put_cmd_in_ert("\\" + ctype + "{}")]
719             document.body[i:k+1] = subst
720         i = i + 1
721
722
723 def revert_strikeout(document):
724     " Reverts \\strikeout character style "
725     while True:
726         i = find_token(document.body, '\\strikeout', 0)
727         if i == -1:
728             return
729         del document.body[i]
730
731
732 def revert_uulinewave(document):
733     " Reverts \\uuline, and \\uwave character styles "
734     while True:
735         i = find_token(document.body, '\\uuline', 0)
736         if i == -1:
737             break
738         del document.body[i]
739     while True:
740         i = find_token(document.body, '\\uwave', 0)
741         if i == -1:
742             return
743         del document.body[i]
744
745
746 def revert_ulinelatex(document):
747     " Reverts \\uline character style "
748     i = find_token(document.body, '\\bar under', 0)
749     if i == -1:
750         return
751     insert_to_preamble(0, document,
752             '% Commands inserted by lyx2lyx for proper underlining\n'
753             + '\\PassOptionsToPackage{normalem}{ulem}\n'
754             + '\\usepackage{ulem}\n'
755             + '\\let\\cite@rig\\cite\n'
756             + '\\newcommand{\\b@xcite}[2][\\%]{\\def\\def@pt{\\%}\\def\\pas@pt{#1}\n'
757             + '  \\mbox{\\ifx\\def@pt\\pas@pt\\cite@rig{#2}\\else\\cite@rig[#1]{#2}\\fi}}\n'
758             + '\\renewcommand{\\underbar}[1]{{\\let\\cite\\b@xcite\\uline{#1}}}\n')
759
760
761 def revert_custom_processors(document):
762     " Remove bibtex_command and index_command params "
763     i = find_token(document.header, '\\bibtex_command', 0)
764     if i == -1:
765         document.warning("Malformed LyX document: Missing \\bibtex_command.")
766         return
767     del document.header[i]
768     i = find_token(document.header, '\\index_command', 0)
769     if i == -1:
770         document.warning("Malformed LyX document: Missing \\index_command.")
771         return
772     del document.header[i]
773
774
775 def convert_nomencl_width(document):
776     " Add set_width param to nomencl_print "
777     i = 0
778     while True:
779       i = find_token(document.body, "\\begin_inset CommandInset nomencl_print", i)
780       if i == -1:
781         break
782       document.body.insert(i + 2, "set_width \"none\"")
783       i = i + 1
784
785
786 def revert_nomencl_width(document):
787     " Remove set_width param from nomencl_print "
788     i = 0
789     while True:
790       i = find_token(document.body, "\\begin_inset CommandInset nomencl_print", i)
791       if i == -1:
792         break
793       j = find_end_of_inset(document.body, i)
794       l = find_token(document.body, "set_width", i, j)
795       if l == -1:
796             document.warning("Can't find set_width option for nomencl_print!")
797             i = j
798             continue
799       del document.body[l]
800       i = i + 1
801
802
803 def revert_nomencl_cwidth(document):
804     " Remove width param from nomencl_print "
805     i = 0
806     while True:
807       i = find_token(document.body, "\\begin_inset CommandInset nomencl_print", i)
808       if i == -1:
809         break
810       j = find_end_of_inset(document.body, i)
811       l = find_token(document.body, "width", i, j)
812       if l == -1:
813             #Can't find width option for nomencl_print
814             i = j
815             continue
816       width = get_value(document.body, "width", i, j).strip('"')
817       del document.body[l]
818       add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
819       add_to_preamble(document, ["\\setlength{\\nomlabelwidth}{" + width + "}"])
820       i = i + 1
821
822
823 def revert_applemac(document):
824     " Revert applemac encoding to auto "
825     i = 0
826     if document.encoding == "applemac":
827         document.encoding = "auto"
828         i = find_token(document.header, "\\encoding", 0)
829         if i != -1:
830             document.header[i] = "\\encoding auto"
831
832
833 def revert_longtable_align(document):
834     " Remove longtable alignment setting "
835     i = 0
836     j = 0
837     while True:
838       i = find_token(document.body, "\\begin_inset Tabular", i)
839       if i == -1:
840           break
841       # the alignment is 2 lines below \\begin_inset Tabular
842       j = document.body[i+2].find("longtabularalignment")
843       if j == -1:
844           break
845       document.body[i+2] = document.body[i+2][:j-1]
846       document.body[i+2] = document.body[i+2] + '>'
847       i = i + 1
848
849
850 def revert_branch_filename(document):
851     " Remove \\filename_suffix parameter from branches "
852     i = 0
853     while True:
854         i = find_token(document.header, "\\filename_suffix", i)
855         if i == -1:
856             return
857         del document.header[i]
858
859
860 def revert_paragraph_indentation(document):
861     " Revert custom paragraph indentation to preamble code "
862     i = 0
863     while True:
864       i = find_token(document.header, "\\paragraph_indentation", i)
865       if i == -1:
866           break
867       # only remove the preamble line if default
868       # otherwise also write the value to the preamble
869       length = get_value(document.header, "\\paragraph_indentation", i)
870       if length == "default":
871           del document.header[i]
872           break
873       else:
874           # handle percent lengths
875           # latex_length returns "bool,length"
876           length = latex_length(length).split(",")[1]
877           add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
878           add_to_preamble(document, ["\\setlength{\\parindent}{" + length + "}"])
879           del document.header[i]
880       i = i + 1
881
882
883 def revert_percent_skip_lengths(document):
884     " Revert relative lengths for paragraph skip separation to preamble code "
885     i = 0
886     while True:
887       i = find_token(document.header, "\\defskip", i)
888       if i == -1:
889           break
890       length = get_value(document.header, "\\defskip", i)
891       # only revert when a custom length was set and when
892       # it used a percent length
893       if length not in ('smallskip', 'medskip', 'bigskip'):
894           # handle percent lengths
895           length = latex_length(length)
896           # latex_length returns "bool,length"
897           percent = length.split(",")[0]
898           length = length.split(",")[1]
899           if percent == "True":
900               add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
901               add_to_preamble(document, ["\\setlength{\\parskip}{" + length + "}"])
902               # set defskip to medskip as default
903               document.header[i] = "\\defskip medskip"
904       i = i + 1
905
906
907 def revert_percent_vspace_lengths(document):
908     " Revert relative VSpace lengths to ERT "
909     i = 0
910     while True:
911       i = find_token(document.body, "\\begin_inset VSpace", i)
912       if i == -1:
913           break
914       # only revert if a custom length was set and if
915       # it used a percent length
916       line = document.body[i]
917       r = re.compile(r'\\begin_inset VSpace (.*)$')
918       m = r.match(line)
919       length = m.group(1)
920       if length not in ('defskip', 'smallskip', 'medskip', 'bigskip', 'vfill'):
921           # check if the space has a star (protected space)
922           protected = (document.body[i].rfind("*") != -1)
923           if protected:
924               length = length.rstrip('*')
925           # handle percent lengths
926           length = latex_length(length)
927           # latex_length returns "bool,length"
928           percent = length.split(",")[0]
929           length = length.split(",")[1]
930           # revert the VSpace inset to ERT
931           if percent == "True":
932               if protected:
933                   subst = [old_put_cmd_in_ert("\\vspace*{" + length + "}")]
934               else:
935                   subst = [old_put_cmd_in_ert("\\vspace{" + length + "}")]
936               document.body[i:i+2] = subst
937       i = i + 1
938
939
940 def revert_percent_hspace_lengths(document):
941     " Revert relative HSpace lengths to ERT "
942     i = 0
943     while True:
944       i = find_token(document.body, "\\begin_inset space \\hspace", i)
945       if i == -1:
946           break
947       protected = (document.body[i].find("\\hspace*{}") != -1)
948       # only revert if a custom length was set and if
949       # it used a percent length
950       length = get_value(document.body, '\\length', i+1)
951       if length == '':
952           document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
953           return
954       # handle percent lengths
955       length = latex_length(length)
956       # latex_length returns "bool,length"
957       percent = length.split(",")[0]
958       length = length.split(",")[1]
959       # revert the HSpace inset to ERT
960       if percent == "True":
961           if protected:
962               subst = [old_put_cmd_in_ert("\\hspace*{" + length + "}")]
963           else:
964               subst = [old_put_cmd_in_ert("\\hspace{" + length + "}")]
965           document.body[i:i+3] = subst
966       i = i + 2
967
968
969 def revert_hspace_glue_lengths(document):
970     " Revert HSpace glue lengths to ERT "
971     i = 0
972     while True:
973       i = find_token(document.body, "\\begin_inset space \\hspace", i)
974       if i == -1:
975           break
976       protected = (document.body[i].find("\\hspace*{}") != -1)
977       length = get_value(document.body, '\\length', i+1)
978       if length == '':
979           document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
980           return
981       # only revert if the length contains a plus or minus at pos != 0
982       glue  = re.compile(r'.+[\+-]')
983       if glue.search(length):
984           # handle percent lengths
985           # latex_length returns "bool,length"
986           length = latex_length(length).split(",")[1]
987           # revert the HSpace inset to ERT
988           if protected:
989               subst = [old_put_cmd_in_ert("\\hspace*{" + length + "}")]
990           else:
991               subst = [old_put_cmd_in_ert("\\hspace{" + length + "}")]
992           document.body[i:i+3] = subst
993       i = i + 2
994
995 def convert_author_id(document):
996     " Add the author_id to the \\author definition and make sure 0 is not used"
997     i = 0
998     j = 1
999     while True:
1000         i = find_token(document.header, "\\author", i)
1001         if i == -1:
1002             break
1003         
1004         r = re.compile(r'(\\author) (\".*\")\s?(.*)$')
1005         m = r.match(document.header[i])
1006         if m != None:
1007             name = m.group(2)
1008             
1009             email = ''
1010             if m.lastindex == 3:
1011                 email = m.group(3)
1012             document.header[i] = "\\author %i %s %s" % (j, name, email)
1013         j = j + 1
1014         i = i + 1
1015         
1016     k = 0
1017     while True:
1018         k = find_token(document.body, "\\change_", k)
1019         if k == -1:
1020             break
1021
1022         change = document.body[k].split(' ');
1023         if len(change) == 3:
1024             type = change[0]
1025             author_id = int(change[1])
1026             time = change[2]
1027             document.body[k] = "%s %i %s" % (type, author_id + 1, time)
1028         k = k + 1
1029
1030 def revert_author_id(document):
1031     " Remove the author_id from the \\author definition "
1032     i = 0
1033     j = 0
1034     idmap = dict()
1035     while True:
1036         i = find_token(document.header, "\\author", i)
1037         if i == -1:
1038             break
1039         
1040         r = re.compile(r'(\\author) (\d+) (\".*\")\s?(.*)$')
1041         m = r.match(document.header[i])
1042         if m != None:
1043             author_id = int(m.group(2))
1044             idmap[author_id] = j
1045             name = m.group(3)
1046             
1047             email = ''
1048             if m.lastindex == 4:
1049                 email = m.group(4)
1050             document.header[i] = "\\author %s %s" % (name, email)
1051         i = i + 1
1052         j = j + 1
1053
1054     k = 0
1055     while True:
1056         k = find_token(document.body, "\\change_", k)
1057         if k == -1:
1058             break
1059
1060         change = document.body[k].split(' ');
1061         if len(change) == 3:
1062             type = change[0]
1063             author_id = int(change[1])
1064             time = change[2]
1065             document.body[k] = "%s %i %s" % (type, idmap[author_id], time)
1066         k = k + 1
1067
1068
1069 def revert_suppress_date(document):
1070     " Revert suppressing of default document date to preamble code "
1071     i = 0
1072     while True:
1073       i = find_token(document.header, "\\suppress_date", i)
1074       if i == -1:
1075           break
1076       # remove the preamble line and write to the preamble
1077       # when suppress_date was true
1078       date = get_value(document.header, "\\suppress_date", i)
1079       if date == "true":
1080           add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
1081           add_to_preamble(document, ["\\date{}"])
1082       del document.header[i]
1083       i = i + 1
1084
1085
1086 def revert_mhchem(document):
1087     "Revert mhchem loading to preamble code"
1088     i = 0
1089     j = 0
1090     k = 0
1091     mhchem = "off"
1092     i = find_token(document.header, "\\use_mhchem 1", 0)
1093     if i != -1:
1094         mhchem = "auto"
1095     else:
1096         i = find_token(document.header, "\\use_mhchem 2", 0)
1097         if i != -1:
1098             mhchem = "on"
1099     if mhchem == "auto":
1100         j = find_token(document.body, "\\cf{", 0)
1101         if j != -1:
1102             mhchem = "on"
1103         else:
1104             j = find_token(document.body, "\\ce{", 0)
1105             if j != -1:
1106                 mhchem = "on"
1107     if mhchem == "on":
1108         add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
1109         add_to_preamble(document, ["\\PassOptionsToPackage{version=3}{mhchem}"])
1110         add_to_preamble(document, ["\\usepackage{mhchem}"])
1111     k = find_token(document.header, "\\use_mhchem", 0)
1112     if k == -1:
1113         document.warning("Malformed LyX document: Could not find mhchem setting.")
1114         return
1115     del document.header[k]
1116
1117
1118 def revert_fontenc(document):
1119     " Remove fontencoding param "
1120     i = find_token(document.header, '\\fontencoding', 0)
1121     if i == -1:
1122         document.warning("Malformed LyX document: Missing \\fontencoding.")
1123         return
1124     del document.header[i]
1125
1126
1127 def merge_gbrief(document):
1128     " Merge g-brief-en and g-brief-de to one class "
1129
1130     if document.textclass != "g-brief-de":
1131         if document.textclass == "g-brief-en":
1132             document.textclass = "g-brief"
1133             document.set_textclass()
1134         return
1135
1136     obsoletedby = { "Brieftext":       "Letter",
1137                     "Unterschrift":    "Signature",
1138                     "Strasse":         "Street",
1139                     "Zusatz":          "Addition",
1140                     "Ort":             "Town",
1141                     "Land":            "State",
1142                     "RetourAdresse":   "ReturnAddress",
1143                     "MeinZeichen":     "MyRef",
1144                     "IhrZeichen":      "YourRef",
1145                     "IhrSchreiben":    "YourMail",
1146                     "Telefon":         "Phone",
1147                     "BLZ":             "BankCode",
1148                     "Konto":           "BankAccount",
1149                     "Postvermerk":     "PostalComment",
1150                     "Adresse":         "Address",
1151                     "Datum":           "Date",
1152                     "Betreff":         "Reference",
1153                     "Anrede":          "Opening",
1154                     "Anlagen":         "Encl.",
1155                     "Verteiler":       "cc",
1156                     "Gruss":           "Closing"}
1157     i = 0
1158     while 1:
1159         i = find_token(document.body, "\\begin_layout", i)
1160         if i == -1:
1161             break
1162
1163         layout = document.body[i][14:]
1164         if layout in obsoletedby:
1165             document.body[i] = "\\begin_layout " + obsoletedby[layout]
1166
1167         i += 1
1168         
1169     document.textclass = "g-brief"
1170     document.set_textclass()
1171
1172
1173 def revert_gbrief(document):
1174     " Revert g-brief to g-brief-en "
1175     if document.textclass == "g-brief":
1176         document.textclass = "g-brief-en"
1177         document.set_textclass()
1178
1179
1180 def revert_html_options(document):
1181     " Remove html options "
1182     i = find_token(document.header, '\\html_use_mathml', 0)
1183     if i != -1:
1184         del document.header[i]
1185     i = find_token(document.header, '\\html_be_strict', 0)
1186     if i != -1:
1187         del document.header[i]
1188
1189
1190 def revert_includeonly(document):
1191     i = 0
1192     while True:
1193         i = find_token(document.header, "\\begin_includeonly", i)
1194         if i == -1:
1195             return
1196         j = find_end_of(document.header, i, "\\begin_includeonly", "\\end_includeonly")
1197         if j == -1:
1198             # this should not happen
1199             break
1200         document.header[i : j + 1] = []
1201
1202
1203 def revert_includeall(document):
1204     " Remove maintain_unincluded_children param "
1205     i = find_token(document.header, '\\maintain_unincluded_children', 0)
1206     if i != -1:
1207         del document.header[i]
1208
1209
1210 def revert_multirow(document):
1211     " Revert multirow cells in tables "
1212     i = 0
1213     multirow = False
1214     while True:
1215       # cell type 3 is multirow begin cell
1216       i = find_token(document.body, '<cell multirow="3"', i)
1217       if i == -1:
1218           break
1219       # a multirow cell was found
1220       multirow = True
1221       # remove the multirow tag, set the valignment to top
1222       # and remove the bottom line
1223       document.body[i] = document.body[i].replace(' multirow="3" ', ' ')
1224       document.body[i] = document.body[i].replace('valignment="middle"', 'valignment="top"')
1225       document.body[i] = document.body[i].replace(' bottomline="true" ', ' ')
1226       # write ERT to create the multirow cell
1227       # use 2 rows and 2cm as default with because the multirow span
1228       # and the column width is only hardly accessible
1229       subst = [old_put_cmd_in_ert("\\multirow{2}{2cm}{")]
1230       document.body[i + 4:i + 4] = subst
1231       i = find_token(document.body, "</cell>", i)
1232       if i == -1:
1233            document.warning("Malformed LyX document: Could not find end of tabular cell.")
1234            break
1235       subst = [old_put_cmd_in_ert("}")]
1236       document.body[i - 3:i - 3] = subst
1237       # cell type 4 is multirow part cell
1238       i = find_token(document.body, '<cell multirow="4"', i)
1239       if i == -1:
1240           break
1241       # remove the multirow tag, set the valignment to top
1242       # and remove the top line
1243       document.body[i] = document.body[i].replace(' multirow="4" ', ' ')
1244       document.body[i] = document.body[i].replace('valignment="middle"', 'valignment="top"')
1245       document.body[i] = document.body[i].replace(' topline="true" ', ' ')
1246       i = i + 1
1247     if multirow == True:
1248         add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
1249         add_to_preamble(document, ["\\usepackage{multirow}"])
1250
1251
1252 def convert_math_output(document):
1253     " Convert \html_use_mathml to \html_math_output "
1254     i = find_token(document.header, "\\html_use_mathml", 0)
1255     if i == -1:
1256         return
1257     rgx = re.compile(r'\\html_use_mathml\s+(\w+)')
1258     m = rgx.match(document.header[i])
1259     if rgx:
1260         newval = "0" # MathML
1261         val = m.group(1)
1262         if val != "true":
1263             newval = "2" # Images
1264         document.header[i] = "\\html_math_output " + newval
1265
1266
1267 def revert_math_output(document):
1268     " Revert \html_math_output to \html_use_mathml "
1269     i = find_token(document.header, "\\html_math_output", 0)
1270     if i == -1:
1271         return
1272     rgx = re.compile(r'\\html_math_output\s+(\d)')
1273     m = rgx.match(document.header[i])
1274     newval = "true"
1275     if rgx:
1276         val = m.group(1)
1277         if val == "1" or val == "2":
1278             newval = "false"
1279     else:
1280         document.warning("Unable to match " + document.header[i])
1281     document.header[i] = "\\html_use_mathml " + newval
1282                 
1283
1284
1285 def revert_inset_preview(document):
1286     " Dissolves the preview inset "
1287     i = 0
1288     j = 0
1289     k = 0
1290     while True:
1291       i = find_token(document.body, "\\begin_inset Preview", i)
1292       if i == -1:
1293           return
1294       j = find_end_of_inset(document.body, i)
1295       if j == -1:
1296           document.warning("Malformed LyX document: Could not find end of Preview inset.")
1297           return
1298       #If the layout is Standard we need to remove it, otherwise there
1299       #will be paragraph breaks that shouldn't be there.
1300       k = find_token(document.body, "\\begin_layout Standard", i)
1301       if k == i+2:
1302           del document.body[i : i+3]
1303           del document.body[j-5 : j-2]
1304           i -= 6
1305       else:
1306           del document.body[i]
1307           del document.body[j-1]
1308           i -= 2
1309                 
1310
1311 def revert_equalspacing_xymatrix(document):
1312     " Revert a Formula with xymatrix@! to an ERT inset "
1313     i = 0
1314     j = 0
1315     has_preamble = False
1316     has_equal_spacing = False
1317     while True:
1318       found = -1
1319       i = find_token(document.body, "\\begin_inset Formula", i)
1320       if i == -1:
1321           break
1322       j = find_end_of_inset(document.body, i)
1323       if j == -1:
1324           document.warning("Malformed LyX document: Could not find end of Formula inset.")
1325           break
1326           
1327       for curline in range(i,j):
1328           found = document.body[curline].find("\\xymatrix@!")
1329           if found != -1:
1330               break
1331  
1332       if found != -1:
1333           has_equal_spacing = True
1334           content = [document.body[i][21:]]
1335           content += document.body[i+1:j]
1336           subst = put_cmd_in_ert(content)
1337           document.body[i:j+1] = subst
1338           i += len(subst)
1339       else:
1340           for curline in range(i,j):
1341               l = document.body[curline].find("\\xymatrix")
1342               if l != -1:
1343                   has_preamble = True;
1344                   break;
1345           i = j + 1
1346     if has_equal_spacing and not has_preamble:
1347         add_to_preamble(document, ['\\usepackage[all]{xy}'])
1348
1349
1350 def revert_notefontcolor(document):
1351     " Reverts greyed-out note font color to preamble code "
1352     i = 0
1353     colorcode = ""
1354     while True:
1355       i = find_token(document.header, "\\notefontcolor", i)
1356       if i == -1:
1357           return
1358       colorcode = get_value(document.header, '\\notefontcolor', 0)
1359       del document.header[i]
1360       # the color code is in the form #rrggbb where every character denotes a hex number
1361       # convert the string to an int
1362       red = string.atoi(colorcode[1:3],16)
1363       # we want the output "0.5" for the value "127" therefore increment here
1364       if red != 0:
1365           red = red + 1
1366       redout = float(red) / 256
1367       green = string.atoi(colorcode[3:5],16)
1368       if green != 0:
1369           green = green + 1
1370       greenout = float(green) / 256
1371       blue = string.atoi(colorcode[5:7],16)
1372       if blue != 0:
1373           blue = blue + 1
1374       blueout = float(blue) / 256
1375       # write the preamble
1376       insert_to_preamble(0, document,
1377                            '% Commands inserted by lyx2lyx to set the font color\n'
1378                            '% for greyed-out notes\n'
1379                            + '\\@ifundefined{definecolor}{\\usepackage{color}}{}\n'
1380                            + '\\definecolor{note_fontcolor}{rgb}{'
1381                            + str(redout) + ', ' + str(greenout)
1382                            + ', ' + str(blueout) + '}\n'
1383                            + '\\renewenvironment{lyxgreyedout}\n'
1384                            + ' {\\textcolor{note_fontcolor}\\bgroup}{\\egroup}\n')
1385
1386
1387 def revert_turkmen(document):
1388     "Set language Turkmen to English" 
1389     i = 0 
1390     if document.language == "turkmen": 
1391         document.language = "english" 
1392         i = find_token(document.header, "\\language", 0) 
1393         if i != -1: 
1394             document.header[i] = "\\language english" 
1395     j = 0 
1396     while True: 
1397         j = find_token(document.body, "\\lang turkmen", j) 
1398         if j == -1: 
1399             return 
1400         document.body[j] = document.body[j].replace("\\lang turkmen", "\\lang english") 
1401         j = j + 1 
1402
1403
1404 def revert_fontcolor(document):
1405     " Reverts font color to preamble code "
1406     i = 0
1407     colorcode = ""
1408     while True:
1409       i = find_token(document.header, "\\fontcolor", i)
1410       if i == -1:
1411           return
1412       colorcode = get_value(document.header, '\\fontcolor', 0)
1413       del document.header[i]
1414       # don't clutter the preamble if backgroundcolor is not set
1415       if colorcode == "#000000":
1416           continue
1417       # the color code is in the form #rrggbb where every character denotes a hex number
1418       # convert the string to an int
1419       red = string.atoi(colorcode[1:3],16)
1420       # we want the output "0.5" for the value "127" therefore add here
1421       if red != 0:
1422           red = red + 1
1423       redout = float(red) / 256
1424       green = string.atoi(colorcode[3:5],16)
1425       if green != 0:
1426           green = green + 1
1427       greenout = float(green) / 256
1428       blue = string.atoi(colorcode[5:7],16)
1429       if blue != 0:
1430           blue = blue + 1
1431       blueout = float(blue) / 256
1432       # write the preamble
1433       insert_to_preamble(0, document,
1434                            '% Commands inserted by lyx2lyx to set the font color\n'
1435                            + '\\@ifundefined{definecolor}{\\usepackage{color}}{}\n'
1436                            + '\\definecolor{document_fontcolor}{rgb}{'
1437                            + str(redout) + ', ' + str(greenout)
1438                            + ', ' + str(blueout) + '}\n'
1439                            + '\\color{document_fontcolor}\n')
1440
1441
1442 def revert_shadedboxcolor(document):
1443     " Reverts shaded box color to preamble code "
1444     i = 0
1445     colorcode = ""
1446     while True:
1447       i = find_token(document.header, "\\boxbgcolor", i)
1448       if i == -1:
1449           return
1450       colorcode = get_value(document.header, '\\boxbgcolor', 0)
1451       del document.header[i]
1452       # the color code is in the form #rrggbb where every character denotes a hex number
1453       # convert the string to an int
1454       red = string.atoi(colorcode[1:3],16)
1455       # we want the output "0.5" for the value "127" therefore increment here
1456       if red != 0:
1457           red = red + 1
1458       redout = float(red) / 256
1459       green = string.atoi(colorcode[3:5],16)
1460       if green != 0:
1461           green = green + 1
1462       greenout = float(green) / 256
1463       blue = string.atoi(colorcode[5:7],16)
1464       if blue != 0:
1465           blue = blue + 1
1466       blueout = float(blue) / 256
1467       # write the preamble
1468       insert_to_preamble(0, document,
1469                            '% Commands inserted by lyx2lyx to set the color\n'
1470                            '% of boxes with shaded background\n'
1471                            + '\\@ifundefined{definecolor}{\\usepackage{color}}{}\n'
1472                            + '\\definecolor{shadecolor}{rgb}{'
1473                            + str(redout) + ', ' + str(greenout)
1474                            + ', ' + str(blueout) + '}\n')
1475
1476
1477 def revert_lyx_version(document):
1478     " Reverts LyX Version information from Inset Info "
1479     version = "LyX version"
1480     try:
1481         import lyx2lyx_version
1482         version = lyx2lyx_version.version
1483     except:
1484         pass
1485
1486     i = 0
1487     while 1:
1488         i = find_token(document.body, '\\begin_inset Info', i)
1489         if i == -1:
1490             return
1491         j = find_end_of_inset(document.body, i + 1)
1492         if j == -1:
1493             # should not happen
1494             document.warning("Malformed LyX document: Could not find end of Info inset.")
1495         # We expect:
1496         # \begin_inset Info
1497         # type  "lyxinfo"
1498         # arg   "version"
1499         # \end_inset
1500         # but we shall try to be forgiving.
1501         arg = typ = ""
1502         for k in range(i, j):
1503             if document.body[k].startswith("arg"):
1504                 arg = document.body[k][3:].strip().strip('"')
1505             if document.body[k].startswith("type"):
1506                 typ = document.body[k][4:].strip().strip('"')
1507         if arg != "version" or typ != "lyxinfo":
1508             i = j+1
1509             continue
1510
1511         # We do not actually know the version of LyX used to produce the document.
1512         # But we can use our version, since we are reverting.
1513         s = [version]
1514         # Now we want to check if the line after "\end_inset" is empty. It normally
1515         # is, so we want to remove it, too.
1516         lastline = j+1
1517         if document.body[j+1].strip() == "":
1518             lastline = j+2
1519         document.body[i: lastline] = s
1520         i = i+1
1521
1522
1523 def revert_math_scale(document):
1524   " Remove math scaling and LaTeX options "
1525   i = find_token(document.header, '\\html_math_img_scale', 0)
1526   if i != -1:
1527     del document.header[i]
1528   i = find_token(document.header, '\\html_latex_start', 0)
1529   if i != -1:
1530     del document.header[i]
1531   i = find_token(document.header, '\\html_latex_end', 0)
1532   if i != -1:
1533     del document.header[i]
1534
1535
1536 def revert_pagesizes(document):
1537   i = 0
1538   " Revert page sizes to default "
1539   i = find_token(document.header, '\\papersize', 0)
1540   if i != -1:
1541     size = document.header[i][11:]
1542     if size == "a0paper" or size == "a1paper" or size == "a2paper" \
1543     or size == "a6paper" or size == "b0paper" or size == "b1paper" \
1544     or size == "b2paper" or size == "b6paper" or size == "b0j" \
1545     or size == "b1j" or size == "b2j" or size == "b3j" or size == "b4j" \
1546     or size == "b5j" or size == "b6j":
1547       del document.header[i]
1548
1549
1550 def convert_html_quotes(document):
1551   " Remove quotes around html_latex_start and html_latex_end "
1552
1553   i = find_token(document.header, '\\html_latex_start', 0)
1554   if i != -1:
1555     line = document.header[i]
1556     l = re.compile(r'\\html_latex_start\s+"(.*)"')
1557     m = l.match(line)
1558     if m != None:
1559       document.header[i] = "\\html_latex_start " + m.group(1)
1560       
1561   i = find_token(document.header, '\\html_latex_end', 0)
1562   if i != -1:
1563     line = document.header[i]
1564     l = re.compile(r'\\html_latex_end\s+"(.*)"')
1565     m = l.match(line)
1566     if m != None:
1567       document.header[i] = "\\html_latex_end " + m.group(1)
1568       
1569
1570 def revert_html_quotes(document):
1571   " Remove quotes around html_latex_start and html_latex_end "
1572   
1573   i = find_token(document.header, '\\html_latex_start', 0)
1574   if i != -1:
1575     line = document.header[i]
1576     l = re.compile(r'\\html_latex_start\s+(.*)')
1577     m = l.match(line)
1578     document.header[i] = "\\html_latex_start \"" + m.group(1) + "\""
1579       
1580   i = find_token(document.header, '\\html_latex_end', 0)
1581   if i != -1:
1582     line = document.header[i]
1583     l = re.compile(r'\\html_latex_end\s+(.*)')
1584     m = l.match(line)
1585     document.header[i] = "\\html_latex_end \"" + m.group(1) + "\""
1586
1587
1588 def revert_output_sync(document):
1589   " Remove forward search options "
1590   i = find_token(document.header, '\\output_sync_macro', 0)
1591   if i != -1:
1592     del document.header[i]
1593   i = find_token(document.header, '\\output_sync', 0)
1594   if i != -1:
1595     del document.header[i]
1596
1597
1598 def convert_beamer_args(document):
1599   " Convert ERT arguments in Beamer to InsetArguments "
1600
1601   if document.textclass != "beamer" and document.textclass != "article-beamer":
1602     return
1603   
1604   layouts = ("Block", "ExampleBlock", "AlertBlock")
1605   for layout in layouts:
1606     blay = 0
1607     while True:
1608       blay = find_token(document.body, '\\begin_layout ' + layout, blay)
1609       if blay == -1:
1610         break
1611       elay = find_end_of(document.body, blay, '\\begin_layout', '\\end_layout')
1612       if elay == -1:
1613         document.warning("Malformed LyX document: Can't find end of " + layout + " layout.")
1614         blay += 1
1615         continue
1616       bert = find_token(document.body, '\\begin_inset ERT', blay)
1617       if bert == -1:
1618         document.warning("Malformed Beamer LyX document: Can't find argument of " + layout + " layout.")
1619         blay = elay + 1
1620         continue
1621       eert = find_end_of_inset(document.body, bert)
1622       if eert == -1:
1623         document.warning("Malformed LyX document: Can't find end of ERT.")
1624         blay = elay + 1
1625         continue
1626       
1627       # So the ERT inset begins at line k and goes to line l. We now wrap it in 
1628       # an argument inset.
1629       # Do the end first, so as not to mess up the variables.
1630       document.body[eert + 1:eert + 1] = ['', '\\end_layout', '', '\\end_inset', '']
1631       document.body[bert:bert] = ['\\begin_inset OptArg', 'status open', '', 
1632           '\\begin_layout Plain Layout']
1633       blay = elay + 9
1634
1635
1636 def revert_beamer_args(document):
1637   " Revert Beamer arguments to ERT "
1638   
1639   if document.textclass != "beamer" and document.textclass != "article-beamer":
1640     return
1641     
1642   layouts = ("Block", "ExampleBlock", "AlertBlock")
1643   for layout in layouts:
1644     blay = 0
1645     while True:
1646       blay = find_token(document.body, '\\begin_layout ' + layout, blay)
1647       if blay == -1:
1648         break
1649       elay = find_end_of(document.body, blay, '\\begin_layout', '\\end_layout')
1650       if elay == -1:
1651         document.warning("Malformed LyX document: Can't find end of " + layout + " layout.")
1652         blay += 1
1653         continue
1654       bopt = find_token(document.body, '\\begin_inset OptArg', blay)
1655       if bopt == -1:
1656         # it is legal not to have one of these
1657         blay = elay + 1
1658         continue
1659       eopt = find_end_of_inset(document.body, bopt)
1660       if eopt == -1:
1661         document.warning("Malformed LyX document: Can't find end of argument.")
1662         blay = elay + 1
1663         continue
1664       bplay = find_token(document.body, '\\begin_layout Plain Layout', blay)
1665       if bplay == -1:
1666         document.warning("Malformed LyX document: Can't find plain layout.")
1667         blay = elay + 1
1668         continue
1669       eplay = find_end_of(document.body, bplay, '\\begin_layout', '\\end_layout')
1670       if eplay == -1:
1671         document.warning("Malformed LyX document: Can't find end of plain layout.")
1672         blay = elay + 1
1673         continue
1674       # So the content of the argument inset goes from bplay + 1 to eplay - 1
1675       bcont = bplay + 1
1676       if bcont >= eplay:
1677         # Hmm.
1678         document.warning(str(bcont) + " " + str(eplay))
1679         blay = blay + 1
1680         continue
1681       # we convert the content of the argument into pure LaTeX...
1682       content = lyx2latex(document, document.body[bcont:eplay])
1683       strlist = put_cmd_in_ert(["{" + content + "}"])
1684       
1685       # now replace the optional argument with the ERT
1686       document.body[bopt:eopt + 1] = strlist
1687       blay = blay + 1
1688
1689
1690 def revert_align_decimal(document):
1691   l = 0
1692   while True:
1693     l = document.body[l].find('alignment=decimal')
1694     if l == -1:
1695         break
1696     remove_option(document, l, 'decimal_point')
1697     document.body[l].replace('decimal', 'center')
1698
1699
1700 def convert_optarg(document):
1701   " Convert \\begin_inset OptArg to \\begin_inset Argument "
1702   i = 0
1703   while 1:
1704     i = find_token(document.body, '\\begin_inset OptArg', i)
1705     if i == -1:
1706       return
1707     document.body[i] = "\\begin_inset Argument"
1708     i += 1
1709
1710
1711 def revert_argument(document):
1712   " Convert \\begin_inset Argument to \\begin_inset OptArg "
1713   i = 0
1714   while 1:
1715     i = find_token(document.body, '\\begin_inset Argument', i)
1716     if i == -1:
1717       return
1718       document.body[i] = "\\begin_inset OptArg"
1719       i += 1
1720       
1721 ##
1722 # Conversion hub
1723 #
1724
1725 supported_versions = ["2.0.0","2.0"]
1726 convert = [[346, []],
1727            [347, []],
1728            [348, []],
1729            [349, []],
1730            [350, []],
1731            [351, []],
1732            [352, [convert_splitindex]],
1733            [353, []],
1734            [354, []],
1735            [355, []],
1736            [356, []],
1737            [357, []],
1738            [358, []],
1739            [359, [convert_nomencl_width]],
1740            [360, []],
1741            [361, []],
1742            [362, []],
1743            [363, []],
1744            [364, []],
1745            [365, []],
1746            [366, []],
1747            [367, []],
1748            [368, []],
1749            [369, [convert_author_id]],
1750            [370, []],
1751            [371, []],
1752            [372, []],
1753            [373, [merge_gbrief]],
1754            [374, []],
1755            [375, []],
1756            [376, []],
1757            [377, []],
1758            [378, []],
1759            [379, [convert_math_output]],
1760            [380, []],
1761            [381, []],
1762            [382, []],
1763            [383, []],
1764            [384, []],
1765            [385, []],
1766            [386, []],
1767            [387, []],
1768            [388, []],
1769            [389, [convert_html_quotes]],
1770            [390, []],
1771            [391, []],
1772            [392, [convert_beamer_args]],
1773            [393, [convert_optarg]]
1774           ]
1775
1776 revert =  [[392, [revert_argument]],
1777            [391, [revert_beamer_args]],
1778            [390, [revert_align_decimal]],
1779            [389, [revert_output_sync]],
1780            [388, [revert_html_quotes]],
1781            [387, [revert_pagesizes]],
1782            [386, [revert_math_scale]],
1783            [385, [revert_lyx_version]],
1784            [384, [revert_shadedboxcolor]],
1785            [383, [revert_fontcolor]],
1786            [382, [revert_turkmen]],
1787            [381, [revert_notefontcolor]],
1788            [380, [revert_equalspacing_xymatrix]],
1789            [379, [revert_inset_preview]],
1790            [378, [revert_math_output]],
1791            [377, []],
1792            [376, [revert_multirow]],
1793            [375, [revert_includeall]],
1794            [374, [revert_includeonly]],
1795            [373, [revert_html_options]],
1796            [372, [revert_gbrief]],
1797            [371, [revert_fontenc]],
1798            [370, [revert_mhchem]],
1799            [369, [revert_suppress_date]],
1800            [368, [revert_author_id]],
1801            [367, [revert_hspace_glue_lengths]],
1802            [366, [revert_percent_vspace_lengths, revert_percent_hspace_lengths]],
1803            [365, [revert_percent_skip_lengths]],
1804            [364, [revert_paragraph_indentation]],
1805            [363, [revert_branch_filename]],
1806            [362, [revert_longtable_align]],
1807            [361, [revert_applemac]],
1808            [360, []],
1809            [359, [revert_nomencl_cwidth]],
1810            [358, [revert_nomencl_width]],
1811            [357, [revert_custom_processors]],
1812            [356, [revert_ulinelatex]],
1813            [355, [revert_uulinewave]],
1814            [354, [revert_strikeout]],
1815            [353, [revert_printindexall]],
1816            [352, [revert_subindex]],
1817            [351, [revert_splitindex]],
1818            [350, [revert_backgroundcolor]],
1819            [349, [revert_outputformat]],
1820            [348, [revert_xetex]],
1821            [347, [revert_phantom, revert_hphantom, revert_vphantom]],
1822            [346, [revert_tabularvalign]],
1823            [345, [revert_swiss]]
1824           ]
1825
1826
1827 if __name__ == "__main__":
1828     pass