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