]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_2_0.py
Fix spacing problem and thereby fix 6802.
[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 def revert_flex_inset(document, name, LaTeXname, position):
275   " Convert flex insets to TeX code "
276   i = 0
277   z = 0
278   while True:
279     i = find_token(document.body, '\\begin_inset Flex ' + name, position)
280     if i == -1:
281       return
282     else:
283       z = find_end_of_inset(document.body, i)
284       if z == -1:
285         document.warning("Malformed LyX document: Can't find end of Flex " + name + " inset.")
286         return
287       # remove the \end_inset
288       document.body[z - 2:z + 1] = put_cmd_in_ert("}")
289       # we need to reset character layouts if necessary
290       j = find_token(document.body, '\\emph on', i)
291       k = find_token(document.body, '\\noun on', i)
292       l = find_token(document.body, '\\series', i)
293       m = find_token(document.body, '\\family', i)
294       n = find_token(document.body, '\\shape', i)
295       o = find_token(document.body, '\\color', i)
296       p = find_token(document.body, '\\size', i)
297       q = find_token(document.body, '\\bar under', i)
298       r = find_token(document.body, '\\uuline on', i)
299       s = find_token(document.body, '\\uwave on', i)
300       t = find_token(document.body, '\\strikeout on', i)
301       if j != -1 and j < z:
302         document.body.insert(z-2, "\\emph default")
303       if k != -1 and k < z:
304         document.body.insert(z-2, "\\noun default")
305       if l != -1 and l < z:
306         document.body.insert(z-2, "\\series default")
307       if m != -1 and m < z:
308         document.body.insert(z-2, "\\family default")
309       if n != -1 and n < z:
310         document.body.insert(z-2, "\\shape default")
311       if o != -1 and o < z:
312         document.body.insert(z-2, "\\color inherit")
313       if p != -1 and p < z:
314         document.body.insert(z-2, "\\size default")
315       if q != -1 and q < z:
316         document.body.insert(z-2, "\\bar default")
317       if r != -1 and r < z:
318         document.body.insert(z-2, "\\uuline default")
319       if s != -1 and s < z:
320         document.body.insert(z-2, "\\uwave default")
321       if t != -1 and t < z:
322         document.body.insert(z-2, "\\strikeout default")
323       document.body[i:i+4] = put_cmd_in_ert(LaTeXname + "{")
324     i += 1
325
326
327 def revert_charstyles(document, name, LaTeXname, changed):
328   " Reverts character styles to TeX code "
329   i = 0
330   while True:
331     i = find_token(document.body, name + ' on', i)
332     if i == -1:
333       return changed
334     else:
335       j = find_token(document.body, name + ' default', i)
336       k = find_token(document.body, name + ' on', i + 1)
337       # if there is no default set, the style ends with the layout
338       # assure hereby that we found the correct layout end
339       if j != -1 and (j < k or k ==-1):
340         document.body[j:j+1] = put_cmd_in_ert("}")
341       else:
342         j = find_token(document.body, '\\end_layout', i)
343         document.body[j:j] = put_cmd_in_ert("}")
344       document.body[i:i+1] = put_cmd_in_ert(LaTeXname + "{")
345       changed = True
346     i += 1
347
348
349 ####################################################################
350
351
352 def revert_swiss(document):
353     " Set language german-ch to ngerman "
354     i = 0
355     if document.language == "german-ch":
356         document.language = "ngerman"
357         i = find_token(document.header, "\\language", 0)
358         if i != -1:
359             document.header[i] = "\\language ngerman"
360     j = 0
361     while True:
362         j = find_token(document.body, "\\lang german-ch", j)
363         if j == -1:
364             return
365         document.body[j] = document.body[j].replace("\\lang german-ch", "\\lang ngerman")
366         j = j + 1
367
368
369 def revert_tabularvalign(document):
370    " Revert the tabular valign option "
371    i = 0
372    while True:
373        i = find_token(document.body, "\\begin_inset Tabular", i)
374        if i == -1:
375            return
376        j = find_token(document.body, "</cell>", i)
377        if j == -1:
378            document.warning("Malformed LyX document: Could not find end of tabular cell.")
379            i = j
380            continue
381        # don't set a box for longtables, only delete tabularvalignment
382        # the alignment is 2 lines below \\begin_inset Tabular
383        p = document.body[i+2].find("islongtable")
384        if p > -1:
385            q = document.body[i+2].find("tabularvalignment")
386            if q > -1:
387                document.body[i+2] = document.body[i+2][:q-1]
388                document.body[i+2] = document.body[i+2] + '>'
389            i = i + 1
390
391        # when no longtable
392        if p == -1:
393          tabularvalignment = 'c'
394          # which valignment is specified?
395          m = document.body[i+2].find('tabularvalignment="top"')
396          if m > -1:
397              tabularvalignment = 't'
398          m = document.body[i+2].find('tabularvalignment="bottom"')
399          if m > -1:
400              tabularvalignment = 'b'
401          # delete tabularvalignment
402          q = document.body[i+2].find("tabularvalignment")
403          if q > -1:
404              document.body[i+2] = document.body[i+2][:q-1]
405              document.body[i+2] = document.body[i+2] + '>'
406
407          # don't add a box when centered
408          if tabularvalignment == 'c':
409              i = j
410              continue
411          subst = ['\\end_layout', '\\end_inset']
412          document.body[j:j] = subst # just inserts those lines
413          subst = ['\\begin_inset Box Frameless',
414              'position "' + tabularvalignment +'"',
415              'hor_pos "c"',
416              'has_inner_box 1',
417              'inner_pos "c"',
418              'use_parbox 0',
419              # we don't know the width, assume 50%
420              'width "50col%"',
421              'special "none"',
422              'height "1in"',
423              'height_special "totalheight"',
424              'status open',
425              '',
426              '\\begin_layout Plain Layout']
427          document.body[i:i] = subst # this just inserts the array at i
428          i += len(subst) + 2 # adjust i to save a few cycles
429
430
431 def revert_phantom(document):
432     " Reverts phantom to ERT "
433     i = 0
434     j = 0
435     while True:
436       i = find_token(document.body, "\\begin_inset Phantom Phantom", i)
437       if i == -1:
438           return
439       substi = document.body[i].replace('\\begin_inset Phantom Phantom', \
440                 '\\begin_inset ERT\nstatus collapsed\n\n' \
441                 '\\begin_layout Plain Layout\n\n\n\\backslash\n' \
442                 'phantom{\n\\end_layout\n\n\\end_inset\n')
443       substi = substi.split('\n')
444       document.body[i : i+4] = substi
445       i += len(substi)
446       j = find_token(document.body, "\\end_layout", i)
447       if j == -1:
448           document.warning("Malformed LyX document: Could not find end of Phantom inset.")
449           return
450       substj = document.body[j].replace('\\end_layout', \
451                 '\\size default\n\n\\begin_inset ERT\nstatus collapsed\n\n' \
452                 '\\begin_layout Plain Layout\n\n' \
453                 '}\n\\end_layout\n\n\\end_inset\n')
454       substj = substj.split('\n')
455       document.body[j : j+4] = substj
456       i += len(substj)
457
458
459 def revert_hphantom(document):
460     " Reverts hphantom to ERT "
461     i = 0
462     j = 0
463     while True:
464       i = find_token(document.body, "\\begin_inset Phantom HPhantom", i)
465       if i == -1:
466           return
467       substi = document.body[i].replace('\\begin_inset Phantom HPhantom', \
468                 '\\begin_inset ERT\nstatus collapsed\n\n' \
469                 '\\begin_layout Plain Layout\n\n\n\\backslash\n' \
470                 'hphantom{\n\\end_layout\n\n\\end_inset\n')
471       substi = substi.split('\n')
472       document.body[i : i+4] = substi
473       i += len(substi)
474       j = find_token(document.body, "\\end_layout", i)
475       if j == -1:
476           document.warning("Malformed LyX document: Could not find end of HPhantom inset.")
477           return
478       substj = document.body[j].replace('\\end_layout', \
479                 '\\size default\n\n\\begin_inset ERT\nstatus collapsed\n\n' \
480                 '\\begin_layout Plain Layout\n\n' \
481                 '}\n\\end_layout\n\n\\end_inset\n')
482       substj = substj.split('\n')
483       document.body[j : j+4] = substj
484       i += len(substj)
485
486
487 def revert_vphantom(document):
488     " Reverts vphantom to ERT "
489     i = 0
490     j = 0
491     while True:
492       i = find_token(document.body, "\\begin_inset Phantom VPhantom", i)
493       if i == -1:
494           return
495       substi = document.body[i].replace('\\begin_inset Phantom VPhantom', \
496                 '\\begin_inset ERT\nstatus collapsed\n\n' \
497                 '\\begin_layout Plain Layout\n\n\n\\backslash\n' \
498                 'vphantom{\n\\end_layout\n\n\\end_inset\n')
499       substi = substi.split('\n')
500       document.body[i : i+4] = substi
501       i += len(substi)
502       j = find_token(document.body, "\\end_layout", i)
503       if j == -1:
504           document.warning("Malformed LyX document: Could not find end of VPhantom inset.")
505           return
506       substj = document.body[j].replace('\\end_layout', \
507                 '\\size default\n\n\\begin_inset ERT\nstatus collapsed\n\n' \
508                 '\\begin_layout Plain Layout\n\n' \
509                 '}\n\\end_layout\n\n\\end_inset\n')
510       substj = substj.split('\n')
511       document.body[j : j+4] = substj
512       i += len(substj)
513
514
515 def revert_xetex(document):
516     " Reverts documents that use XeTeX "
517     i = find_token(document.header, '\\use_xetex', 0)
518     if i == -1:
519         document.warning("Malformed LyX document: Missing \\use_xetex.")
520         return
521     if get_value(document.header, "\\use_xetex", i) == 'false':
522         del document.header[i]
523         return
524     del document.header[i]
525     # 1.) set doc encoding to utf8-plain
526     i = find_token(document.header, "\\inputencoding", 0)
527     if i == -1:
528         document.warning("Malformed LyX document: Missing \\inputencoding.")
529     document.header[i] = "\\inputencoding utf8-plain"
530     # 2.) check font settings
531     l = find_token(document.header, "\\font_roman", 0)
532     if l == -1:
533         document.warning("Malformed LyX document: Missing \\font_roman.")
534     line = document.header[l]
535     l = re.compile(r'\\font_roman (.*)$')
536     m = l.match(line)
537     roman = m.group(1)
538     l = find_token(document.header, "\\font_sans", 0)
539     if l == -1:
540         document.warning("Malformed LyX document: Missing \\font_sans.")
541     line = document.header[l]
542     l = re.compile(r'\\font_sans (.*)$')
543     m = l.match(line)
544     sans = m.group(1)
545     l = find_token(document.header, "\\font_typewriter", 0)
546     if l == -1:
547         document.warning("Malformed LyX document: Missing \\font_typewriter.")
548     line = document.header[l]
549     l = re.compile(r'\\font_typewriter (.*)$')
550     m = l.match(line)
551     typewriter = m.group(1)
552     osf = get_value(document.header, '\\font_osf', 0) == "true"
553     sf_scale = float(get_value(document.header, '\\font_sf_scale', 0))
554     tt_scale = float(get_value(document.header, '\\font_tt_scale', 0))
555     # 3.) set preamble stuff
556     pretext = '%% This document must be processed with xelatex!\n'
557     pretext += '\\usepackage{fontspec}\n'
558     if roman != "default":
559         pretext += '\\setmainfont[Mapping=tex-text]{' + roman + '}\n'
560     if sans != "default":
561         pretext += '\\setsansfont['
562         if sf_scale != 100:
563             pretext += 'Scale=' + str(sf_scale / 100) + ','
564         pretext += 'Mapping=tex-text]{' + sans + '}\n'
565     if typewriter != "default":
566         pretext += '\\setmonofont'
567         if tt_scale != 100:
568             pretext += '[Scale=' + str(tt_scale / 100) + ']'
569         pretext += '{' + typewriter + '}\n'
570     if osf:
571         pretext += '\\defaultfontfeatures{Numbers=OldStyle}\n'
572     pretext += '\usepackage{xunicode}\n'
573     pretext += '\usepackage{xltxtra}\n'
574     insert_to_preamble(0, document, pretext)
575     # 4.) reset font settings
576     i = find_token(document.header, "\\font_roman", 0)
577     if i == -1:
578         document.warning("Malformed LyX document: Missing \\font_roman.")
579     document.header[i] = "\\font_roman default"
580     i = find_token(document.header, "\\font_sans", 0)
581     if i == -1:
582         document.warning("Malformed LyX document: Missing \\font_sans.")
583     document.header[i] = "\\font_sans default"
584     i = find_token(document.header, "\\font_typewriter", 0)
585     if i == -1:
586         document.warning("Malformed LyX document: Missing \\font_typewriter.")
587     document.header[i] = "\\font_typewriter default"
588     i = find_token(document.header, "\\font_osf", 0)
589     if i == -1:
590         document.warning("Malformed LyX document: Missing \\font_osf.")
591     document.header[i] = "\\font_osf false"
592     i = find_token(document.header, "\\font_sc", 0)
593     if i == -1:
594         document.warning("Malformed LyX document: Missing \\font_sc.")
595     document.header[i] = "\\font_sc false"
596     i = find_token(document.header, "\\font_sf_scale", 0)
597     if i == -1:
598         document.warning("Malformed LyX document: Missing \\font_sf_scale.")
599     document.header[i] = "\\font_sf_scale 100"
600     i = find_token(document.header, "\\font_tt_scale", 0)
601     if i == -1:
602         document.warning("Malformed LyX document: Missing \\font_tt_scale.")
603     document.header[i] = "\\font_tt_scale 100"
604
605
606 def revert_outputformat(document):
607     " Remove default output format param "
608     i = find_token(document.header, '\\default_output_format', 0)
609     if i == -1:
610         document.warning("Malformed LyX document: Missing \\default_output_format.")
611         return
612     del document.header[i]
613
614
615 def revert_backgroundcolor(document):
616     " Reverts background color to preamble code "
617     i = 0
618     colorcode = ""
619     while True:
620       i = find_token(document.header, "\\backgroundcolor", i)
621       if i == -1:
622           return
623       colorcode = get_value(document.header, '\\backgroundcolor', 0)
624       del document.header[i]
625       # don't clutter the preamble if backgroundcolor is not set
626       if colorcode == "#ffffff":
627           continue
628       # the color code is in the form #rrggbb where every character denotes a hex number
629       # convert the string to an int
630       red = string.atoi(colorcode[1:3],16)
631       # we want the output "0.5" for the value "127" therefore add here
632       if red != 0:
633           red = red + 1
634       redout = float(red) / 256
635       green = string.atoi(colorcode[3:5],16)
636       if green != 0:
637           green = green + 1
638       greenout = float(green) / 256
639       blue = string.atoi(colorcode[5:7],16)
640       if blue != 0:
641           blue = blue + 1
642       blueout = float(blue) / 256
643       # write the preamble
644       insert_to_preamble(0, document,
645                            '% Commands inserted by lyx2lyx to set the background color\n'
646                            + '\\@ifundefined{definecolor}{\\usepackage{color}}{}\n'
647                            + '\\definecolor{page_backgroundcolor}{rgb}{'
648                            + str(redout) + ', ' + str(greenout)
649                            + ', ' + str(blueout) + '}\n'
650                            + '\\pagecolor{page_backgroundcolor}\n')
651
652
653 def revert_splitindex(document):
654     " Reverts splitindex-aware documents "
655     i = find_token(document.header, '\\use_indices', 0)
656     if i == -1:
657         document.warning("Malformed LyX document: Missing \\use_indices.")
658         return
659     indices = get_value(document.header, "\\use_indices", i)
660     preamble = ""
661     if indices == "true":
662          preamble += "\\usepackage{splitidx}\n"
663     del document.header[i]
664     i = 0
665     while True:
666         i = find_token(document.header, "\\index", i)
667         if i == -1:
668             break
669         k = find_token(document.header, "\\end_index", i)
670         if k == -1:
671             document.warning("Malformed LyX document: Missing \\end_index.")
672             return
673         line = document.header[i]
674         l = re.compile(r'\\index (.*)$')
675         m = l.match(line)
676         iname = m.group(1)
677         ishortcut = get_value(document.header, '\\shortcut', i, k)
678         if ishortcut != "" and indices == "true":
679             preamble += "\\newindex[" + iname + "]{" + ishortcut + "}\n"
680         del document.header[i:k+1]
681         i = 0
682     if preamble != "":
683         insert_to_preamble(0, document, preamble)
684     i = 0
685     while True:
686         i = find_token(document.body, "\\begin_inset Index", i)
687         if i == -1:
688             break
689         line = document.body[i]
690         l = re.compile(r'\\begin_inset Index (.*)$')
691         m = l.match(line)
692         itype = m.group(1)
693         if itype == "idx" or indices == "false":
694             document.body[i] = "\\begin_inset Index"
695         else:
696             k = find_end_of_inset(document.body, i)
697             if k == -1:
698                  return
699             content = lyx2latex(document, document.body[i:k])
700             # escape quotes
701             content = content.replace('"', r'\"')
702             subst = [old_put_cmd_in_ert("\\sindex[" + itype + "]{" + content + "}")]
703             document.body[i:k+1] = subst
704         i = i + 1
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         ptype = get_value(document.body, 'type', i, k).strip('"')
712         if ptype == "idx":
713             j = find_token(document.body, "type", i, k)
714             del document.body[j]
715         elif indices == "false":
716             del document.body[i:k+1]
717         else:
718             subst = [old_put_cmd_in_ert("\\printindex[" + ptype + "]{}")]
719             document.body[i:k+1] = subst
720         i = i + 1
721
722
723 def convert_splitindex(document):
724     " Converts index and printindex insets to splitindex-aware format "
725     i = 0
726     while True:
727         i = find_token(document.body, "\\begin_inset Index", i)
728         if i == -1:
729             break
730         document.body[i] = document.body[i].replace("\\begin_inset Index",
731             "\\begin_inset Index idx")
732         i = i + 1
733     i = 0
734     while True:
735         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
736         if i == -1:
737             return
738         if document.body[i + 1].find('LatexCommand printindex') == -1:
739             document.warning("Malformed LyX document: Incomplete printindex inset.")
740             return
741         subst = ["LatexCommand printindex", 
742             "type \"idx\""]
743         document.body[i + 1:i + 2] = subst
744         i = i + 1
745
746
747 def revert_subindex(document):
748     " Reverts \\printsubindex CommandInset types "
749     i = find_token(document.header, '\\use_indices', 0)
750     if i == -1:
751         document.warning("Malformed LyX document: Missing \\use_indices.")
752         return
753     indices = get_value(document.header, "\\use_indices", i)
754     i = 0
755     while True:
756         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
757         if i == -1:
758             return
759         k = find_end_of_inset(document.body, i)
760         ctype = get_value(document.body, 'LatexCommand', i, k)
761         if ctype != "printsubindex":
762             i = i + 1
763             continue
764         ptype = get_value(document.body, 'type', i, k).strip('"')
765         if indices == "false":
766             del document.body[i:k+1]
767         else:
768             subst = [old_put_cmd_in_ert("\\printsubindex[" + ptype + "]{}")]
769             document.body[i:k+1] = subst
770         i = i + 1
771
772
773 def revert_printindexall(document):
774     " Reverts \\print[sub]index* CommandInset types "
775     i = find_token(document.header, '\\use_indices', 0)
776     if i == -1:
777         document.warning("Malformed LyX document: Missing \\use_indices.")
778         return
779     indices = get_value(document.header, "\\use_indices", i)
780     i = 0
781     while True:
782         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
783         if i == -1:
784             return
785         k = find_end_of_inset(document.body, i)
786         ctype = get_value(document.body, 'LatexCommand', i, k)
787         if ctype != "printindex*" and ctype != "printsubindex*":
788             i = i + 1
789             continue
790         if indices == "false":
791             del document.body[i:k+1]
792         else:
793             subst = [old_put_cmd_in_ert("\\" + ctype + "{}")]
794             document.body[i:k+1] = subst
795         i = i + 1
796
797
798 def revert_strikeout(document):
799   " Reverts \\strikeout character style "
800   changed = False
801   changed = revert_charstyles(document, "\\uuline", "\\uuline", changed)
802   changed = revert_charstyles(document, "\\uwave", "\\uwave", changed)
803   changed = revert_charstyles(document, "\\strikeout", "\\sout", changed)
804   if changed == True:
805     insert_to_preamble(0, document,
806         '% Commands inserted by lyx2lyx for proper underlining\n'
807         + '\\PassOptionsToPackage{normalem}{ulem}\n'
808         + '\\usepackage{ulem}\n')
809
810
811 def revert_ulinelatex(document):
812     " Reverts \\uline character style "
813     i = find_token(document.body, '\\bar under', 0)
814     if i == -1:
815         return
816     insert_to_preamble(0, document,
817             '% Commands inserted by lyx2lyx for proper underlining\n'
818             + '\\PassOptionsToPackage{normalem}{ulem}\n'
819             + '\\usepackage{ulem}\n'
820             + '\\let\\cite@rig\\cite\n'
821             + '\\newcommand{\\b@xcite}[2][\\%]{\\def\\def@pt{\\%}\\def\\pas@pt{#1}\n'
822             + '  \\mbox{\\ifx\\def@pt\\pas@pt\\cite@rig{#2}\\else\\cite@rig[#1]{#2}\\fi}}\n'
823             + '\\renewcommand{\\underbar}[1]{{\\let\\cite\\b@xcite\\uline{#1}}}\n')
824
825
826 def revert_custom_processors(document):
827     " Remove bibtex_command and index_command params "
828     i = find_token(document.header, '\\bibtex_command', 0)
829     if i == -1:
830         document.warning("Malformed LyX document: Missing \\bibtex_command.")
831         return
832     del document.header[i]
833     i = find_token(document.header, '\\index_command', 0)
834     if i == -1:
835         document.warning("Malformed LyX document: Missing \\index_command.")
836         return
837     del document.header[i]
838
839
840 def convert_nomencl_width(document):
841     " Add set_width param to nomencl_print "
842     i = 0
843     while True:
844       i = find_token(document.body, "\\begin_inset CommandInset nomencl_print", i)
845       if i == -1:
846         break
847       document.body.insert(i + 2, "set_width \"none\"")
848       i = i + 1
849
850
851 def revert_nomencl_width(document):
852     " Remove set_width param from nomencl_print "
853     i = 0
854     while True:
855       i = find_token(document.body, "\\begin_inset CommandInset nomencl_print", i)
856       if i == -1:
857         break
858       j = find_end_of_inset(document.body, i)
859       l = find_token(document.body, "set_width", i, j)
860       if l == -1:
861             document.warning("Can't find set_width option for nomencl_print!")
862             i = j
863             continue
864       del document.body[l]
865       i = i + 1
866
867
868 def revert_nomencl_cwidth(document):
869     " Remove width param from nomencl_print "
870     i = 0
871     while True:
872       i = find_token(document.body, "\\begin_inset CommandInset nomencl_print", i)
873       if i == -1:
874         break
875       j = find_end_of_inset(document.body, i)
876       l = find_token(document.body, "width", i, j)
877       if l == -1:
878             #Can't find width option for nomencl_print
879             i = j
880             continue
881       width = get_value(document.body, "width", i, j).strip('"')
882       del document.body[l]
883       add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
884       add_to_preamble(document, ["\\setlength{\\nomlabelwidth}{" + width + "}"])
885       i = i + 1
886
887
888 def revert_applemac(document):
889     " Revert applemac encoding to auto "
890     i = 0
891     if document.encoding == "applemac":
892         document.encoding = "auto"
893         i = find_token(document.header, "\\encoding", 0)
894         if i != -1:
895             document.header[i] = "\\encoding auto"
896
897
898 def revert_longtable_align(document):
899     " Remove longtable alignment setting "
900     i = 0
901     j = 0
902     while True:
903       i = find_token(document.body, "\\begin_inset Tabular", i)
904       if i == -1:
905           break
906       # the alignment is 2 lines below \\begin_inset Tabular
907       j = document.body[i+2].find("longtabularalignment")
908       if j == -1:
909           break
910       document.body[i+2] = document.body[i+2][:j-1]
911       document.body[i+2] = document.body[i+2] + '>'
912       i = i + 1
913
914
915 def revert_branch_filename(document):
916     " Remove \\filename_suffix parameter from branches "
917     i = 0
918     while True:
919         i = find_token(document.header, "\\filename_suffix", i)
920         if i == -1:
921             return
922         del document.header[i]
923
924
925 def revert_paragraph_indentation(document):
926     " Revert custom paragraph indentation to preamble code "
927     i = 0
928     while True:
929       i = find_token(document.header, "\\paragraph_indentation", i)
930       if i == -1:
931           break
932       # only remove the preamble line if default
933       # otherwise also write the value to the preamble
934       length = get_value(document.header, "\\paragraph_indentation", i)
935       if length == "default":
936           del document.header[i]
937           break
938       else:
939           # handle percent lengths
940           # latex_length returns "bool,length"
941           length = latex_length(length).split(",")[1]
942           add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
943           add_to_preamble(document, ["\\setlength{\\parindent}{" + length + "}"])
944           del document.header[i]
945       i = i + 1
946
947
948 def revert_percent_skip_lengths(document):
949     " Revert relative lengths for paragraph skip separation to preamble code "
950     i = 0
951     while True:
952       i = find_token(document.header, "\\defskip", i)
953       if i == -1:
954           break
955       length = get_value(document.header, "\\defskip", i)
956       # only revert when a custom length was set and when
957       # it used a percent length
958       if length not in ('smallskip', 'medskip', 'bigskip'):
959           # handle percent lengths
960           length = latex_length(length)
961           # latex_length returns "bool,length"
962           percent = length.split(",")[0]
963           length = length.split(",")[1]
964           if percent == "True":
965               add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
966               add_to_preamble(document, ["\\setlength{\\parskip}{" + length + "}"])
967               # set defskip to medskip as default
968               document.header[i] = "\\defskip medskip"
969       i = i + 1
970
971
972 def revert_percent_vspace_lengths(document):
973     " Revert relative VSpace lengths to ERT "
974     i = 0
975     while True:
976       i = find_token(document.body, "\\begin_inset VSpace", i)
977       if i == -1:
978           break
979       # only revert if a custom length was set and if
980       # it used a percent length
981       line = document.body[i]
982       r = re.compile(r'\\begin_inset VSpace (.*)$')
983       m = r.match(line)
984       length = m.group(1)
985       if length not in ('defskip', 'smallskip', 'medskip', 'bigskip', 'vfill'):
986           # check if the space has a star (protected space)
987           protected = (document.body[i].rfind("*") != -1)
988           if protected:
989               length = length.rstrip('*')
990           # handle percent lengths
991           length = latex_length(length)
992           # latex_length returns "bool,length"
993           percent = length.split(",")[0]
994           length = length.split(",")[1]
995           # revert the VSpace inset to ERT
996           if percent == "True":
997               if protected:
998                   subst = [old_put_cmd_in_ert("\\vspace*{" + length + "}")]
999               else:
1000                   subst = [old_put_cmd_in_ert("\\vspace{" + length + "}")]
1001               document.body[i:i+2] = subst
1002       i = i + 1
1003
1004
1005 def revert_percent_hspace_lengths(document):
1006     " Revert relative HSpace lengths to ERT "
1007     i = 0
1008     while True:
1009       i = find_token(document.body, "\\begin_inset space \\hspace", i)
1010       if i == -1:
1011           break
1012       protected = (document.body[i].find("\\hspace*{}") != -1)
1013       # only revert if a custom length was set and if
1014       # it used a percent length
1015       length = get_value(document.body, '\\length', i+1)
1016       if length == '':
1017           document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
1018           return
1019       # handle percent lengths
1020       length = latex_length(length)
1021       # latex_length returns "bool,length"
1022       percent = length.split(",")[0]
1023       length = length.split(",")[1]
1024       # revert the HSpace inset to ERT
1025       if percent == "True":
1026           if protected:
1027               subst = [old_put_cmd_in_ert("\\hspace*{" + length + "}")]
1028           else:
1029               subst = [old_put_cmd_in_ert("\\hspace{" + length + "}")]
1030           document.body[i:i+3] = subst
1031       i = i + 2
1032
1033
1034 def revert_hspace_glue_lengths(document):
1035     " Revert HSpace glue lengths to ERT "
1036     i = 0
1037     while True:
1038       i = find_token(document.body, "\\begin_inset space \\hspace", i)
1039       if i == -1:
1040           break
1041       protected = (document.body[i].find("\\hspace*{}") != -1)
1042       length = get_value(document.body, '\\length', i+1)
1043       if length == '':
1044           document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
1045           return
1046       # only revert if the length contains a plus or minus at pos != 0
1047       glue  = re.compile(r'.+[\+-]')
1048       if glue.search(length):
1049           # handle percent lengths
1050           # latex_length returns "bool,length"
1051           length = latex_length(length).split(",")[1]
1052           # revert the HSpace inset to ERT
1053           if protected:
1054               subst = [old_put_cmd_in_ert("\\hspace*{" + length + "}")]
1055           else:
1056               subst = [old_put_cmd_in_ert("\\hspace{" + length + "}")]
1057           document.body[i:i+3] = subst
1058       i = i + 2
1059
1060 def convert_author_id(document):
1061     " Add the author_id to the \\author definition and make sure 0 is not used"
1062     i = 0
1063     j = 1
1064     while True:
1065         i = find_token(document.header, "\\author", i)
1066         if i == -1:
1067             break
1068         
1069         r = re.compile(r'(\\author) (\".*\")\s?(.*)$')
1070         m = r.match(document.header[i])
1071         if m != None:
1072             name = m.group(2)
1073             
1074             email = ''
1075             if m.lastindex == 3:
1076                 email = m.group(3)
1077             document.header[i] = "\\author %i %s %s" % (j, name, email)
1078         j = j + 1
1079         i = i + 1
1080         
1081     k = 0
1082     while True:
1083         k = find_token(document.body, "\\change_", k)
1084         if k == -1:
1085             break
1086
1087         change = document.body[k].split(' ');
1088         if len(change) == 3:
1089             type = change[0]
1090             author_id = int(change[1])
1091             time = change[2]
1092             document.body[k] = "%s %i %s" % (type, author_id + 1, time)
1093         k = k + 1
1094
1095 def revert_author_id(document):
1096     " Remove the author_id from the \\author definition "
1097     i = 0
1098     j = 0
1099     idmap = dict()
1100     while True:
1101         i = find_token(document.header, "\\author", i)
1102         if i == -1:
1103             break
1104         
1105         r = re.compile(r'(\\author) (\d+) (\".*\")\s?(.*)$')
1106         m = r.match(document.header[i])
1107         if m != None:
1108             author_id = int(m.group(2))
1109             idmap[author_id] = j
1110             name = m.group(3)
1111             
1112             email = ''
1113             if m.lastindex == 4:
1114                 email = m.group(4)
1115             document.header[i] = "\\author %s %s" % (name, email)
1116         i = i + 1
1117         j = j + 1
1118
1119     k = 0
1120     while True:
1121         k = find_token(document.body, "\\change_", k)
1122         if k == -1:
1123             break
1124
1125         change = document.body[k].split(' ');
1126         if len(change) == 3:
1127             type = change[0]
1128             author_id = int(change[1])
1129             time = change[2]
1130             document.body[k] = "%s %i %s" % (type, idmap[author_id], time)
1131         k = k + 1
1132
1133
1134 def revert_suppress_date(document):
1135     " Revert suppressing of default document date to preamble code "
1136     i = 0
1137     while True:
1138       i = find_token(document.header, "\\suppress_date", i)
1139       if i == -1:
1140           break
1141       # remove the preamble line and write to the preamble
1142       # when suppress_date was true
1143       date = get_value(document.header, "\\suppress_date", i)
1144       if date == "true":
1145           add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
1146           add_to_preamble(document, ["\\date{}"])
1147       del document.header[i]
1148       i = i + 1
1149
1150
1151 def revert_mhchem(document):
1152     "Revert mhchem loading to preamble code"
1153     i = 0
1154     j = 0
1155     k = 0
1156     mhchem = "off"
1157     i = find_token(document.header, "\\use_mhchem 1", 0)
1158     if i != -1:
1159         mhchem = "auto"
1160     else:
1161         i = find_token(document.header, "\\use_mhchem 2", 0)
1162         if i != -1:
1163             mhchem = "on"
1164     if mhchem == "auto":
1165         j = find_token(document.body, "\\cf{", 0)
1166         if j != -1:
1167             mhchem = "on"
1168         else:
1169             j = find_token(document.body, "\\ce{", 0)
1170             if j != -1:
1171                 mhchem = "on"
1172     if mhchem == "on":
1173         add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
1174         add_to_preamble(document, ["\\PassOptionsToPackage{version=3}{mhchem}"])
1175         add_to_preamble(document, ["\\usepackage{mhchem}"])
1176     k = find_token(document.header, "\\use_mhchem", 0)
1177     if k == -1:
1178         document.warning("Malformed LyX document: Could not find mhchem setting.")
1179         return
1180     del document.header[k]
1181
1182
1183 def revert_fontenc(document):
1184     " Remove fontencoding param "
1185     i = find_token(document.header, '\\fontencoding', 0)
1186     if i == -1:
1187         document.warning("Malformed LyX document: Missing \\fontencoding.")
1188         return
1189     del document.header[i]
1190
1191
1192 def merge_gbrief(document):
1193     " Merge g-brief-en and g-brief-de to one class "
1194
1195     if document.textclass != "g-brief-de":
1196         if document.textclass == "g-brief-en":
1197             document.textclass = "g-brief"
1198             document.set_textclass()
1199         return
1200
1201     obsoletedby = { "Brieftext":       "Letter",
1202                     "Unterschrift":    "Signature",
1203                     "Strasse":         "Street",
1204                     "Zusatz":          "Addition",
1205                     "Ort":             "Town",
1206                     "Land":            "State",
1207                     "RetourAdresse":   "ReturnAddress",
1208                     "MeinZeichen":     "MyRef",
1209                     "IhrZeichen":      "YourRef",
1210                     "IhrSchreiben":    "YourMail",
1211                     "Telefon":         "Phone",
1212                     "BLZ":             "BankCode",
1213                     "Konto":           "BankAccount",
1214                     "Postvermerk":     "PostalComment",
1215                     "Adresse":         "Address",
1216                     "Datum":           "Date",
1217                     "Betreff":         "Reference",
1218                     "Anrede":          "Opening",
1219                     "Anlagen":         "Encl.",
1220                     "Verteiler":       "cc",
1221                     "Gruss":           "Closing"}
1222     i = 0
1223     while 1:
1224         i = find_token(document.body, "\\begin_layout", i)
1225         if i == -1:
1226             break
1227
1228         layout = document.body[i][14:]
1229         if layout in obsoletedby:
1230             document.body[i] = "\\begin_layout " + obsoletedby[layout]
1231
1232         i += 1
1233         
1234     document.textclass = "g-brief"
1235     document.set_textclass()
1236
1237
1238 def revert_gbrief(document):
1239     " Revert g-brief to g-brief-en "
1240     if document.textclass == "g-brief":
1241         document.textclass = "g-brief-en"
1242         document.set_textclass()
1243
1244
1245 def revert_html_options(document):
1246     " Remove html options "
1247     i = find_token(document.header, '\\html_use_mathml', 0)
1248     if i != -1:
1249         del document.header[i]
1250     i = find_token(document.header, '\\html_be_strict', 0)
1251     if i != -1:
1252         del document.header[i]
1253
1254
1255 def revert_includeonly(document):
1256     i = 0
1257     while True:
1258         i = find_token(document.header, "\\begin_includeonly", i)
1259         if i == -1:
1260             return
1261         j = find_end_of(document.header, i, "\\begin_includeonly", "\\end_includeonly")
1262         if j == -1:
1263             # this should not happen
1264             break
1265         document.header[i : j + 1] = []
1266
1267
1268 def revert_includeall(document):
1269     " Remove maintain_unincluded_children param "
1270     i = find_token(document.header, '\\maintain_unincluded_children', 0)
1271     if i != -1:
1272         del document.header[i]
1273
1274
1275 def revert_multirow(document):
1276     " Revert multirow cells in tables "
1277     i = 0
1278     multirow = False
1279     while True:
1280       # cell type 3 is multirow begin cell
1281       i = find_token(document.body, '<cell multirow="3"', i)
1282       if i == -1:
1283           break
1284       # a multirow cell was found
1285       multirow = True
1286       # remove the multirow tag, set the valignment to top
1287       # and remove the bottom line
1288       document.body[i] = document.body[i].replace(' multirow="3" ', ' ')
1289       document.body[i] = document.body[i].replace('valignment="middle"', 'valignment="top"')
1290       document.body[i] = document.body[i].replace(' bottomline="true" ', ' ')
1291       # write ERT to create the multirow cell
1292       # use 2 rows and 2cm as default with because the multirow span
1293       # and the column width is only hardly accessible
1294       subst = [old_put_cmd_in_ert("\\multirow{2}{2cm}{")]
1295       document.body[i + 4:i + 4] = subst
1296       i = find_token(document.body, "</cell>", i)
1297       if i == -1:
1298            document.warning("Malformed LyX document: Could not find end of tabular cell.")
1299            break
1300       subst = [old_put_cmd_in_ert("}")]
1301       document.body[i - 3:i - 3] = subst
1302       # cell type 4 is multirow part cell
1303       i = find_token(document.body, '<cell multirow="4"', i)
1304       if i == -1:
1305           break
1306       # remove the multirow tag, set the valignment to top
1307       # and remove the top line
1308       document.body[i] = document.body[i].replace(' multirow="4" ', ' ')
1309       document.body[i] = document.body[i].replace('valignment="middle"', 'valignment="top"')
1310       document.body[i] = document.body[i].replace(' topline="true" ', ' ')
1311       i = i + 1
1312     if multirow == True:
1313         add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
1314         add_to_preamble(document, ["\\usepackage{multirow}"])
1315
1316
1317 def convert_math_output(document):
1318     " Convert \html_use_mathml to \html_math_output "
1319     i = find_token(document.header, "\\html_use_mathml", 0)
1320     if i == -1:
1321         return
1322     rgx = re.compile(r'\\html_use_mathml\s+(\w+)')
1323     m = rgx.match(document.header[i])
1324     if rgx:
1325         newval = "0" # MathML
1326         val = m.group(1)
1327         if val != "true":
1328             newval = "2" # Images
1329         document.header[i] = "\\html_math_output " + newval
1330
1331
1332 def revert_math_output(document):
1333     " Revert \html_math_output to \html_use_mathml "
1334     i = find_token(document.header, "\\html_math_output", 0)
1335     if i == -1:
1336         return
1337     rgx = re.compile(r'\\html_math_output\s+(\d)')
1338     m = rgx.match(document.header[i])
1339     newval = "true"
1340     if rgx:
1341         val = m.group(1)
1342         if val == "1" or val == "2":
1343             newval = "false"
1344     else:
1345         document.warning("Unable to match " + document.header[i])
1346     document.header[i] = "\\html_use_mathml " + newval
1347                 
1348
1349
1350 def revert_inset_preview(document):
1351     " Dissolves the preview inset "
1352     i = 0
1353     j = 0
1354     k = 0
1355     while True:
1356       i = find_token(document.body, "\\begin_inset Preview", i)
1357       if i == -1:
1358           return
1359       j = find_end_of_inset(document.body, i)
1360       if j == -1:
1361           document.warning("Malformed LyX document: Could not find end of Preview inset.")
1362           return
1363       #If the layout is Standard we need to remove it, otherwise there
1364       #will be paragraph breaks that shouldn't be there.
1365       k = find_token(document.body, "\\begin_layout Standard", i)
1366       if k == i+2:
1367           del document.body[i : i+3]
1368           del document.body[j-5 : j-2]
1369           i -= 6
1370       else:
1371           del document.body[i]
1372           del document.body[j-1]
1373           i -= 2
1374                 
1375
1376 def revert_equalspacing_xymatrix(document):
1377     " Revert a Formula with xymatrix@! to an ERT inset "
1378     i = 0
1379     j = 0
1380     has_preamble = False
1381     has_equal_spacing = False
1382     while True:
1383       found = -1
1384       i = find_token(document.body, "\\begin_inset Formula", i)
1385       if i == -1:
1386           break
1387       j = find_end_of_inset(document.body, i)
1388       if j == -1:
1389           document.warning("Malformed LyX document: Could not find end of Formula inset.")
1390           break
1391           
1392       for curline in range(i,j):
1393           found = document.body[curline].find("\\xymatrix@!")
1394           if found != -1:
1395               break
1396  
1397       if found != -1:
1398           has_equal_spacing = True
1399           content = [document.body[i][21:]]
1400           content += document.body[i+1:j]
1401           subst = put_cmd_in_ert(content)
1402           document.body[i:j+1] = subst
1403           i += len(subst)
1404       else:
1405           for curline in range(i,j):
1406               l = document.body[curline].find("\\xymatrix")
1407               if l != -1:
1408                   has_preamble = True;
1409                   break;
1410           i = j + 1
1411     if has_equal_spacing and not has_preamble:
1412         add_to_preamble(document, ['\\usepackage[all]{xy}'])
1413
1414
1415 def revert_notefontcolor(document):
1416     " Reverts greyed-out note font color to preamble code "
1417     i = 0
1418     colorcode = ""
1419     while True:
1420       i = find_token(document.header, "\\notefontcolor", i)
1421       if i == -1:
1422           return
1423       colorcode = get_value(document.header, '\\notefontcolor', 0)
1424       del document.header[i]
1425       # the color code is in the form #rrggbb where every character denotes a hex number
1426       # convert the string to an int
1427       red = string.atoi(colorcode[1:3],16)
1428       # we want the output "0.5" for the value "127" therefore increment here
1429       if red != 0:
1430           red = red + 1
1431       redout = float(red) / 256
1432       green = string.atoi(colorcode[3:5],16)
1433       if green != 0:
1434           green = green + 1
1435       greenout = float(green) / 256
1436       blue = string.atoi(colorcode[5:7],16)
1437       if blue != 0:
1438           blue = blue + 1
1439       blueout = float(blue) / 256
1440       # write the preamble
1441       insert_to_preamble(0, document,
1442                            '% Commands inserted by lyx2lyx to set the font color\n'
1443                            '% for greyed-out notes\n'
1444                            + '\\@ifundefined{definecolor}{\\usepackage{color}}{}\n'
1445                            + '\\definecolor{note_fontcolor}{rgb}{'
1446                            + str(redout) + ', ' + str(greenout)
1447                            + ', ' + str(blueout) + '}\n'
1448                            + '\\renewenvironment{lyxgreyedout}\n'
1449                            + ' {\\textcolor{note_fontcolor}\\bgroup}{\\egroup}\n')
1450
1451
1452 def revert_turkmen(document):
1453     "Set language Turkmen to English" 
1454     i = 0 
1455     if document.language == "turkmen": 
1456         document.language = "english" 
1457         i = find_token(document.header, "\\language", 0) 
1458         if i != -1: 
1459             document.header[i] = "\\language english" 
1460     j = 0 
1461     while True: 
1462         j = find_token(document.body, "\\lang turkmen", j) 
1463         if j == -1: 
1464             return 
1465         document.body[j] = document.body[j].replace("\\lang turkmen", "\\lang english") 
1466         j = j + 1 
1467
1468
1469 def revert_fontcolor(document):
1470     " Reverts font color to preamble code "
1471     i = 0
1472     colorcode = ""
1473     while True:
1474       i = find_token(document.header, "\\fontcolor", i)
1475       if i == -1:
1476           return
1477       colorcode = get_value(document.header, '\\fontcolor', 0)
1478       del document.header[i]
1479       # don't clutter the preamble if backgroundcolor is not set
1480       if colorcode == "#000000":
1481           continue
1482       # the color code is in the form #rrggbb where every character denotes a hex number
1483       # convert the string to an int
1484       red = string.atoi(colorcode[1:3],16)
1485       # we want the output "0.5" for the value "127" therefore add here
1486       if red != 0:
1487           red = red + 1
1488       redout = float(red) / 256
1489       green = string.atoi(colorcode[3:5],16)
1490       if green != 0:
1491           green = green + 1
1492       greenout = float(green) / 256
1493       blue = string.atoi(colorcode[5:7],16)
1494       if blue != 0:
1495           blue = blue + 1
1496       blueout = float(blue) / 256
1497       # write the preamble
1498       insert_to_preamble(0, document,
1499                            '% Commands inserted by lyx2lyx to set the font color\n'
1500                            + '\\@ifundefined{definecolor}{\\usepackage{color}}{}\n'
1501                            + '\\definecolor{document_fontcolor}{rgb}{'
1502                            + str(redout) + ', ' + str(greenout)
1503                            + ', ' + str(blueout) + '}\n'
1504                            + '\\color{document_fontcolor}\n')
1505
1506
1507 def revert_shadedboxcolor(document):
1508     " Reverts shaded box color to preamble code "
1509     i = 0
1510     colorcode = ""
1511     while True:
1512       i = find_token(document.header, "\\boxbgcolor", i)
1513       if i == -1:
1514           return
1515       colorcode = get_value(document.header, '\\boxbgcolor', 0)
1516       del document.header[i]
1517       # the color code is in the form #rrggbb where every character denotes a hex number
1518       # convert the string to an int
1519       red = string.atoi(colorcode[1:3],16)
1520       # we want the output "0.5" for the value "127" therefore increment here
1521       if red != 0:
1522           red = red + 1
1523       redout = float(red) / 256
1524       green = string.atoi(colorcode[3:5],16)
1525       if green != 0:
1526           green = green + 1
1527       greenout = float(green) / 256
1528       blue = string.atoi(colorcode[5:7],16)
1529       if blue != 0:
1530           blue = blue + 1
1531       blueout = float(blue) / 256
1532       # write the preamble
1533       insert_to_preamble(0, document,
1534                            '% Commands inserted by lyx2lyx to set the color\n'
1535                            '% of boxes with shaded background\n'
1536                            + '\\@ifundefined{definecolor}{\\usepackage{color}}{}\n'
1537                            + '\\definecolor{shadecolor}{rgb}{'
1538                            + str(redout) + ', ' + str(greenout)
1539                            + ', ' + str(blueout) + '}\n')
1540
1541
1542 def revert_lyx_version(document):
1543     " Reverts LyX Version information from Inset Info "
1544     version = "LyX version"
1545     try:
1546         import lyx2lyx_version
1547         version = lyx2lyx_version.version
1548     except:
1549         pass
1550
1551     i = 0
1552     while 1:
1553         i = find_token(document.body, '\\begin_inset Info', i)
1554         if i == -1:
1555             return
1556         j = find_end_of_inset(document.body, i + 1)
1557         if j == -1:
1558             # should not happen
1559             document.warning("Malformed LyX document: Could not find end of Info inset.")
1560         # We expect:
1561         # \begin_inset Info
1562         # type  "lyxinfo"
1563         # arg   "version"
1564         # \end_inset
1565         # but we shall try to be forgiving.
1566         arg = typ = ""
1567         for k in range(i, j):
1568             if document.body[k].startswith("arg"):
1569                 arg = document.body[k][3:].strip().strip('"')
1570             if document.body[k].startswith("type"):
1571                 typ = document.body[k][4:].strip().strip('"')
1572         if arg != "version" or typ != "lyxinfo":
1573             i = j+1
1574             continue
1575
1576         # We do not actually know the version of LyX used to produce the document.
1577         # But we can use our version, since we are reverting.
1578         s = [version]
1579         # Now we want to check if the line after "\end_inset" is empty. It normally
1580         # is, so we want to remove it, too.
1581         lastline = j+1
1582         if document.body[j+1].strip() == "":
1583             lastline = j+2
1584         document.body[i: lastline] = s
1585         i = i+1
1586
1587
1588 def revert_math_scale(document):
1589   " Remove math scaling and LaTeX options "
1590   i = find_token(document.header, '\\html_math_img_scale', 0)
1591   if i != -1:
1592     del document.header[i]
1593   i = find_token(document.header, '\\html_latex_start', 0)
1594   if i != -1:
1595     del document.header[i]
1596   i = find_token(document.header, '\\html_latex_end', 0)
1597   if i != -1:
1598     del document.header[i]
1599
1600
1601 def revert_pagesizes(document):
1602   i = 0
1603   " Revert page sizes to default "
1604   i = find_token(document.header, '\\papersize', 0)
1605   if i != -1:
1606     size = document.header[i][11:]
1607     if size == "a0paper" or size == "a1paper" or size == "a2paper" \
1608     or size == "a6paper" or size == "b0paper" or size == "b1paper" \
1609     or size == "b2paper" or size == "b6paper" or size == "b0j" \
1610     or size == "b1j" or size == "b2j" or size == "b3j" or size == "b4j" \
1611     or size == "b5j" or size == "b6j":
1612       del document.header[i]
1613
1614
1615 def convert_html_quotes(document):
1616   " Remove quotes around html_latex_start and html_latex_end "
1617
1618   i = find_token(document.header, '\\html_latex_start', 0)
1619   if i != -1:
1620     line = document.header[i]
1621     l = re.compile(r'\\html_latex_start\s+"(.*)"')
1622     m = l.match(line)
1623     if m != None:
1624       document.header[i] = "\\html_latex_start " + m.group(1)
1625       
1626   i = find_token(document.header, '\\html_latex_end', 0)
1627   if i != -1:
1628     line = document.header[i]
1629     l = re.compile(r'\\html_latex_end\s+"(.*)"')
1630     m = l.match(line)
1631     if m != None:
1632       document.header[i] = "\\html_latex_end " + m.group(1)
1633       
1634
1635 def revert_html_quotes(document):
1636   " Remove quotes around html_latex_start and html_latex_end "
1637   
1638   i = find_token(document.header, '\\html_latex_start', 0)
1639   if i != -1:
1640     line = document.header[i]
1641     l = re.compile(r'\\html_latex_start\s+(.*)')
1642     m = l.match(line)
1643     document.header[i] = "\\html_latex_start \"" + m.group(1) + "\""
1644       
1645   i = find_token(document.header, '\\html_latex_end', 0)
1646   if i != -1:
1647     line = document.header[i]
1648     l = re.compile(r'\\html_latex_end\s+(.*)')
1649     m = l.match(line)
1650     document.header[i] = "\\html_latex_end \"" + m.group(1) + "\""
1651
1652
1653 def revert_output_sync(document):
1654   " Remove forward search options "
1655   i = find_token(document.header, '\\output_sync_macro', 0)
1656   if i != -1:
1657     del document.header[i]
1658   i = find_token(document.header, '\\output_sync', 0)
1659   if i != -1:
1660     del document.header[i]
1661
1662
1663 def convert_beamer_args(document):
1664   " Convert ERT arguments in Beamer to InsetArguments "
1665
1666   if document.textclass != "beamer" and document.textclass != "article-beamer":
1667     return
1668   
1669   layouts = ("Block", "ExampleBlock", "AlertBlock")
1670   for layout in layouts:
1671     blay = 0
1672     while True:
1673       blay = find_token(document.body, '\\begin_layout ' + layout, blay)
1674       if blay == -1:
1675         break
1676       elay = find_end_of(document.body, blay, '\\begin_layout', '\\end_layout')
1677       if elay == -1:
1678         document.warning("Malformed LyX document: Can't find end of " + layout + " layout.")
1679         blay += 1
1680         continue
1681       bert = find_token(document.body, '\\begin_inset ERT', blay)
1682       if bert == -1:
1683         document.warning("Malformed Beamer LyX document: Can't find argument of " + layout + " layout.")
1684         blay = elay + 1
1685         continue
1686       eert = find_end_of_inset(document.body, bert)
1687       if eert == -1:
1688         document.warning("Malformed LyX document: Can't find end of ERT.")
1689         blay = elay + 1
1690         continue
1691       
1692       # So the ERT inset begins at line k and goes to line l. We now wrap it in 
1693       # an argument inset.
1694       # Do the end first, so as not to mess up the variables.
1695       document.body[eert + 1:eert + 1] = ['', '\\end_layout', '', '\\end_inset', '']
1696       document.body[bert:bert] = ['\\begin_inset OptArg', 'status open', '', 
1697           '\\begin_layout Plain Layout']
1698       blay = elay + 9
1699
1700
1701 def revert_beamer_args(document):
1702   " Revert Beamer arguments to ERT "
1703   
1704   if document.textclass != "beamer" and document.textclass != "article-beamer":
1705     return
1706     
1707   layouts = ("Block", "ExampleBlock", "AlertBlock")
1708   for layout in layouts:
1709     blay = 0
1710     while True:
1711       blay = find_token(document.body, '\\begin_layout ' + layout, blay)
1712       if blay == -1:
1713         break
1714       elay = find_end_of(document.body, blay, '\\begin_layout', '\\end_layout')
1715       if elay == -1:
1716         document.warning("Malformed LyX document: Can't find end of " + layout + " layout.")
1717         blay += 1
1718         continue
1719       bopt = find_token(document.body, '\\begin_inset OptArg', blay)
1720       if bopt == -1:
1721         # it is legal not to have one of these
1722         blay = elay + 1
1723         continue
1724       eopt = find_end_of_inset(document.body, bopt)
1725       if eopt == -1:
1726         document.warning("Malformed LyX document: Can't find end of argument.")
1727         blay = elay + 1
1728         continue
1729       bplay = find_token(document.body, '\\begin_layout Plain Layout', blay)
1730       if bplay == -1:
1731         document.warning("Malformed LyX document: Can't find plain layout.")
1732         blay = elay + 1
1733         continue
1734       eplay = find_end_of(document.body, bplay, '\\begin_layout', '\\end_layout')
1735       if eplay == -1:
1736         document.warning("Malformed LyX document: Can't find end of plain layout.")
1737         blay = elay + 1
1738         continue
1739       # So the content of the argument inset goes from bplay + 1 to eplay - 1
1740       bcont = bplay + 1
1741       if bcont >= eplay:
1742         # Hmm.
1743         document.warning(str(bcont) + " " + str(eplay))
1744         blay = blay + 1
1745         continue
1746       # we convert the content of the argument into pure LaTeX...
1747       content = lyx2latex(document, document.body[bcont:eplay])
1748       strlist = put_cmd_in_ert(["{" + content + "}"])
1749       
1750       # now replace the optional argument with the ERT
1751       document.body[bopt:eopt + 1] = strlist
1752       blay = blay + 1
1753
1754
1755 def revert_align_decimal(document):
1756   l = 0
1757   while True:
1758     l = document.body[l].find('alignment=decimal')
1759     if l == -1:
1760         break
1761     remove_option(document, l, 'decimal_point')
1762     document.body[l].replace('decimal', 'center')
1763
1764
1765 def convert_optarg(document):
1766   " Convert \\begin_inset OptArg to \\begin_inset Argument "
1767   i = 0
1768   while 1:
1769     i = find_token(document.body, '\\begin_inset OptArg', i)
1770     if i == -1:
1771       return
1772     document.body[i] = "\\begin_inset Argument"
1773     i += 1
1774
1775
1776 def revert_argument(document):
1777   " Convert \\begin_inset Argument to \\begin_inset OptArg "
1778   i = 0
1779   while 1:
1780     i = find_token(document.body, '\\begin_inset Argument', i)
1781     if i == -1:
1782       return
1783     document.body[i] = "\\begin_inset OptArg"
1784     i += 1
1785
1786
1787 def revert_makebox(document):
1788   " Convert \\makebox to TeX code "
1789   i = 0
1790   while 1:
1791     # only revert frameless boxes without an inner box
1792     i = find_token(document.body, '\\begin_inset Box Frameless', i)
1793     if i == -1:
1794       return
1795     else:
1796       z = find_end_of_inset(document.body, i)
1797       if z == -1:
1798         document.warning("Malformed LyX document: Can't find end of box inset.")
1799         return
1800       j = find_token(document.body, 'use_makebox 1', i)
1801       # assure we found the makebox of the current box
1802       if j > i + 7 or j == -1:
1803         return
1804       else:
1805         # remove the \end_inset
1806         document.body[z - 2:z + 1] = put_cmd_in_ert("}")
1807         # determine the alignment
1808         k = find_token(document.body, 'hor_pos', j - 4)
1809         align = document.body[k][9]
1810         # determine the width
1811         l = find_token(document.body, 'width "', j + 1)
1812         length = document.body[l][7:]
1813         # remove trailing '"'
1814         length = length[:-1]
1815         # latex_length returns "bool,length"
1816         length = latex_length(length).split(",")[1]
1817         subst = "\\makebox[" + length + "][" \
1818          + align + "]{"
1819         document.body[i:i+13] = put_cmd_in_ert(subst)
1820     i += 1
1821
1822
1823 def revert_IEEEtran(document):
1824   " Convert IEEEtran layouts and styles to TeX code "
1825   revert_flex_inset(document, "IEEE membership", "\\IEEEmembership", 0)
1826   revert_flex_inset(document, "Lowercase", "\\MakeLowercase", 0)
1827
1828
1829 ##
1830 # Conversion hub
1831 #
1832
1833 supported_versions = ["2.0.0","2.0"]
1834 convert = [[346, []],
1835            [347, []],
1836            [348, []],
1837            [349, []],
1838            [350, []],
1839            [351, []],
1840            [352, [convert_splitindex]],
1841            [353, []],
1842            [354, []],
1843            [355, []],
1844            [356, []],
1845            [357, []],
1846            [358, []],
1847            [359, [convert_nomencl_width]],
1848            [360, []],
1849            [361, []],
1850            [362, []],
1851            [363, []],
1852            [364, []],
1853            [365, []],
1854            [366, []],
1855            [367, []],
1856            [368, []],
1857            [369, [convert_author_id]],
1858            [370, []],
1859            [371, []],
1860            [372, []],
1861            [373, [merge_gbrief]],
1862            [374, []],
1863            [375, []],
1864            [376, []],
1865            [377, []],
1866            [378, []],
1867            [379, [convert_math_output]],
1868            [380, []],
1869            [381, []],
1870            [382, []],
1871            [383, []],
1872            [384, []],
1873            [385, []],
1874            [386, []],
1875            [387, []],
1876            [388, []],
1877            [389, [convert_html_quotes]],
1878            [390, []],
1879            [391, []],
1880            [392, [convert_beamer_args]],
1881            [393, [convert_optarg]],
1882            [394, []]
1883           ]
1884
1885 revert =  [[393, [revert_makebox]],
1886            [392, [revert_argument]],
1887            [391, [revert_beamer_args]],
1888            [390, [revert_align_decimal, revert_IEEEtran]],
1889            [389, [revert_output_sync]],
1890            [388, [revert_html_quotes]],
1891            [387, [revert_pagesizes]],
1892            [386, [revert_math_scale]],
1893            [385, [revert_lyx_version]],
1894            [384, [revert_shadedboxcolor]],
1895            [383, [revert_fontcolor]],
1896            [382, [revert_turkmen]],
1897            [381, [revert_notefontcolor]],
1898            [380, [revert_equalspacing_xymatrix]],
1899            [379, [revert_inset_preview]],
1900            [378, [revert_math_output]],
1901            [377, []],
1902            [376, [revert_multirow]],
1903            [375, [revert_includeall]],
1904            [374, [revert_includeonly]],
1905            [373, [revert_html_options]],
1906            [372, [revert_gbrief]],
1907            [371, [revert_fontenc]],
1908            [370, [revert_mhchem]],
1909            [369, [revert_suppress_date]],
1910            [368, [revert_author_id]],
1911            [367, [revert_hspace_glue_lengths]],
1912            [366, [revert_percent_vspace_lengths, revert_percent_hspace_lengths]],
1913            [365, [revert_percent_skip_lengths]],
1914            [364, [revert_paragraph_indentation]],
1915            [363, [revert_branch_filename]],
1916            [362, [revert_longtable_align]],
1917            [361, [revert_applemac]],
1918            [360, []],
1919            [359, [revert_nomencl_cwidth]],
1920            [358, [revert_nomencl_width]],
1921            [357, [revert_custom_processors]],
1922            [356, [revert_ulinelatex]],
1923            [355, []],
1924            [354, [revert_strikeout]],
1925            [353, [revert_printindexall]],
1926            [352, [revert_subindex]],
1927            [351, [revert_splitindex]],
1928            [350, [revert_backgroundcolor]],
1929            [349, [revert_outputformat]],
1930            [348, [revert_xetex]],
1931            [347, [revert_phantom, revert_hphantom, revert_vphantom]],
1932            [346, [revert_tabularvalign]],
1933            [345, [revert_swiss]]
1934           ]
1935
1936
1937 if __name__ == "__main__":
1938     pass