]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_2_0.py
7556609bc2a5487faa08f0f86d88f41765728865
[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) 2010 The LyX team
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, \
27   find_end_of_inset, find_end_of_layout, find_token_backwards, \
28   is_in_inset, get_value, get_quoted_value
29   
30 from lyx2lyx_tools import add_to_preamble, insert_to_preamble, \
31   put_cmd_in_ert, lyx2latex, latex_length, revert_flex_inset, \
32   revert_font_attrs, revert_layout_command, hex2ratio, str2bool
33
34 ####################################################################
35 # Private helper functions
36
37 def remove_option(lines, m, option):
38     ''' removes option from line m. returns whether we did anything '''
39     l = lines[m].find(option)
40     if l == -1:
41         return False
42     val = lines[m][l:].split('"')[1]
43     lines[m] = lines[m][:l - 1] + lines[m][l+len(option + '="' + val + '"'):]
44     return True
45
46
47 # DO NOT USE THIS ROUTINE ANY MORE. Better yet, replace the uses that
48 # have been made of it with uses of put_cmd_in_ert.
49 def old_put_cmd_in_ert(string):
50     for rep in unicode_reps:
51         string = string.replace(rep[1], rep[0].replace('\\\\', '\\'))
52     string = string.replace('\\', "\\backslash\n")
53     string = "\\begin_inset ERT\nstatus collapsed\n\\begin_layout Plain Layout\n" \
54       + string + "\n\\end_layout\n\\end_inset"
55     return string
56
57
58 ###############################################################################
59 ###
60 ### Conversion and reversion routines
61 ###
62 ###############################################################################
63
64 def revert_swiss(document):
65     " Set language german-ch to ngerman "
66     i = 0
67     if document.language == "german-ch":
68         document.language = "ngerman"
69         i = find_token(document.header, "\\language", 0)
70         if i != -1:
71             document.header[i] = "\\language ngerman"
72     j = 0
73     while True:
74         j = find_token(document.body, "\\lang german-ch", j)
75         if j == -1:
76             return
77         document.body[j] = document.body[j].replace("\\lang german-ch", "\\lang ngerman")
78         j = j + 1
79
80
81 def revert_tabularvalign(document):
82    " Revert the tabular valign option "
83    i = 0
84    while True:
85       i = find_token(document.body, "\\begin_inset Tabular", i)
86       if i == -1:
87           return
88       end = find_end_of_inset(document.body, i)
89       if end == -1:
90           document.warning("Can't find end of inset at line " + str(i))
91           i += 1
92           continue
93       fline = find_token(document.body, "<features", i, end)
94       if fline == -1:
95           document.warning("Can't find features for inset at line " + str(i))
96           i += 1
97           continue
98       p = document.body[fline].find("islongtable")
99       if p != -1:
100           q = document.body[fline].find("tabularvalignment")
101           if q != -1:
102               # FIXME
103               # This seems wrong: It removes everything after 
104               # tabularvalignment, too.
105               document.body[fline] = document.body[fline][:q - 1] + '>'
106           i += 1
107           continue
108
109        # no longtable
110       tabularvalignment = 'c'
111       # which valignment is specified?
112       m = document.body[fline].find('tabularvalignment="top"')
113       if m != -1:
114           tabularvalignment = 't'
115       m = document.body[fline].find('tabularvalignment="bottom"')
116       if m != -1:
117           tabularvalignment = 'b'
118       # delete tabularvalignment
119       q = document.body[fline].find("tabularvalignment")
120       if q != -1:
121           # FIXME
122           # This seems wrong: It removes everything after 
123           # tabularvalignment, too.
124           document.body[fline] = document.body[fline][:q - 1] + '>'
125
126       # don't add a box when centered
127       if tabularvalignment == 'c':
128           i = end
129           continue
130       subst = ['\\end_layout', '\\end_inset']
131       document.body[end:end] = subst # just inserts those lines
132       subst = ['\\begin_inset Box Frameless',
133           'position "' + tabularvalignment +'"',
134           'hor_pos "c"',
135           'has_inner_box 1',
136           'inner_pos "c"',
137           'use_parbox 0',
138           # we don't know the width, assume 50%
139           'width "50col%"',
140           'special "none"',
141           'height "1in"',
142           'height_special "totalheight"',
143           'status open',
144           '',
145           '\\begin_layout Plain Layout']
146       document.body[i:i] = subst # this just inserts the array at i
147       # since there could be a tabular inside a tabular, we cannot
148       # jump to end
149       i += len(subst)
150
151
152 def revert_phantom_types(document, ptype, cmd):
153     " Reverts phantom to ERT "
154     i = 0
155     while True:
156       i = find_token(document.body, "\\begin_inset Phantom " + ptype, i)
157       if i == -1:
158           return
159       end = find_end_of_inset(document.body, i)
160       if end == -1:
161           document.warning("Can't find end of inset at line " + str(i))
162           i += 1
163           continue
164       blay = find_token(document.body, "\\begin_layout Plain Layout", i, end)
165       if blay == -1:
166           document.warning("Can't find layout for inset at line " + str(i))
167           i = end
168           continue
169       bend = find_token(document.body, "\\end_layout", blay, end)
170       if bend == -1:
171           document.warning("Malformed LyX document: Could not find end of Phantom inset's layout.")
172           i = end
173           continue
174       substi = ["\\begin_inset ERT", "status collapsed", "",
175                 "\\begin_layout Plain Layout", "", "", "\\backslash", 
176                 cmd + "{", "\\end_layout", "", "\\end_inset"]
177       substj = ["\\size default", "", "\\begin_inset ERT", "status collapsed", "",
178                 "\\begin_layout Plain Layout", "", "}", "\\end_layout", "", "\\end_inset"]
179       # do the later one first so as not to mess up the numbering
180       document.body[bend:end + 1] = substj
181       document.body[i:blay + 1] = substi
182       i = end + len(substi) + len(substj) - (end - bend) - (blay - i) - 2
183
184
185 def revert_phantom(document):
186     revert_phantom_types(document, "Phantom", "phantom")
187     
188 def revert_hphantom(document):
189     revert_phantom_types(document, "HPhantom", "hphantom")
190
191 def revert_vphantom(document):
192     revert_phantom_types(document, "VPhantom", "vphantom")
193
194
195 def revert_xetex(document):
196     " Reverts documents that use XeTeX "
197
198     i = find_token(document.header, '\\use_xetex', 0)
199     if i == -1:
200         document.warning("Malformed LyX document: Missing \\use_xetex.")
201         return
202     if not str2bool(get_value(document.header, "\\use_xetex", i)):
203         del document.header[i]
204         return
205     del document.header[i]
206
207     # 1.) set doc encoding to utf8-plain
208     i = find_token(document.header, "\\inputencoding", 0)
209     if i == -1:
210         document.warning("Malformed LyX document: Missing \\inputencoding.")
211     else:
212         document.header[i] = "\\inputencoding utf8-plain"
213
214     # 2.) check font settings
215     # defaults
216     roman = sans = typew = default
217     osf = False
218     sf_scale = tt_scale = 100.0
219     
220     i = find_token(document.header, "\\font_roman", 0)
221     if i == -1:
222         document.warning("Malformed LyX document: Missing \\font_roman.")
223     else:
224         roman = get_value(document.header, "\\font_roman", i)
225         document.header[i] = "\\font_roman default"
226
227     i = find_token(document.header, "\\font_sans", 0)
228     if i == -1:
229         document.warning("Malformed LyX document: Missing \\font_sans.")
230     else:
231         sans = get_value(document.header, "\\font_sans", i)
232         document.header[i] = "\\font_sans default"
233     
234     i = find_token(document.header, "\\font_typewriter", 0)
235     if i == -1:
236         document.warning("Malformed LyX document: Missing \\font_typewriter.")
237     else:
238         typew = get_value(document.header, "\\font_typewriter", i)
239         document.header[i] = "\\font_typewriter default"
240
241     i = find_token(document.header, "\\font_osf", 0)
242     if i == -1:
243         document.warning("Malformed LyX document: Missing \\font_osf.")
244     else:
245         osf = str2bool(get_value(document.header, "\\font_osf", i))
246         document.header[i] = "\\font_osf false"
247
248     i = find_token(document.header, "\\font_sc", 0)
249     if i == -1:
250         document.warning("Malformed LyX document: Missing \\font_sc.")
251     else:
252         # we do not need this value.
253         document.header[i] = "\\font_sc false"
254     
255     i = find_token(document.header, "\\font_sf_scale", 0)
256     if i == -1:
257         document.warning("Malformed LyX document: Missing \\font_sf_scale.")
258     else:
259       val = get_value(document.header, '\\font_sf_scale', i)
260       try:
261         # float() can throw
262         sf_scale = float(val)
263       except:
264         document.warning("Invalid font_sf_scale value: " + val)
265       document.header[i] = "\\font_sf_scale 100"
266
267     i = find_token(document.header, "\\font_tt_scale", 0)
268     if i == -1:
269         document.warning("Malformed LyX document: Missing \\font_tt_scale.")
270     else:
271         val = get_value(document.header, '\\font_tt_scale', i)
272         try:
273           # float() can throw
274           tt_scale = float(val)
275         except:
276           document.warning("Invalid font_tt_scale value: " + val)
277         document.header[i] = "\\font_tt_scale 100"
278
279     # 3.) set preamble stuff
280     pretext = ['%% This document must be processed with xelatex!']
281     pretext.append('\\usepackage{fontspec}')
282     if roman != "default":
283         pretext.append('\\setmainfont[Mapping=tex-text]{' + roman + '}')
284     if sans != "default":
285         sf = '\\setsansfont['
286         if sf_scale != 100.0:
287             sf += 'Scale=' + str(sf_scale / 100.0) + ','
288         sf += 'Mapping=tex-text]{' + sans + '}'
289         pretext.append(sf)
290     if typewriter != "default":
291         tw = '\\setmonofont'
292         if tt_scale != 100.0:
293             tw += '[Scale=' + str(tt_scale / 100.0) + ']'
294         tw += '{' + typewriter + '}'
295         pretext.append(tw)
296     if osf:
297         pretext.append('\\defaultfontfeatures{Numbers=OldStyle}')
298     pretext.append('\usepackage{xunicode}')
299     pretext.append('\usepackage{xltxtra}')
300     insert_to_preamble(0, document, pretext)
301
302
303 def revert_outputformat(document):
304     " Remove default output format param "
305     i = find_token(document.header, '\\default_output_format', 0)
306     if i == -1:
307         document.warning("Malformed LyX document: Missing \\default_output_format.")
308         return
309     del document.header[i]
310
311
312 def revert_backgroundcolor(document):
313     " Reverts background color to preamble code "
314     i = find_token(document.header, "\\backgroundcolor", 0)
315     if i == -1:
316         return
317     colorcode = get_value(document.header, '\\backgroundcolor', i)
318     del document.header[i]
319     # don't clutter the preamble if backgroundcolor is not set
320     if colorcode == "#ffffff":
321         return
322     red   = hex2ratio(colorcode[1:3])
323     green = hex2ratio(colorcode[3:5])
324     blue  = hex2ratio(colorcode[5:7])
325     insert_to_preamble(0, document,
326                           '% Commands inserted by lyx2lyx to set the background color\n'
327                           + '\\@ifundefined{definecolor}{\\usepackage{color}}{}\n'
328                           + '\\definecolor{page_backgroundcolor}{rgb}{'
329                           + red + ',' + green + ',' + blue + '}\n'
330                           + '\\pagecolor{page_backgroundcolor}\n')
331
332
333 def revert_splitindex(document):
334     " Reverts splitindex-aware documents "
335     i = find_token(document.header, '\\use_indices', 0)
336     if i == -1:
337         document.warning("Malformed LyX document: Missing \\use_indices.")
338         return
339     useindices = str2bool(get_value(document.header, "\\use_indices", i))
340     del document.header[i]
341     preamble = []
342     if useindices:
343          preamble.append("\\usepackage{splitidx})")
344     
345     # deal with index declarations in the preamble
346     i = 0
347     while True:
348         i = find_token(document.header, "\\index", i)
349         if i == -1:
350             break
351         k = find_token(document.header, "\\end_index", i)
352         if k == -1:
353             document.warning("Malformed LyX document: Missing \\end_index.")
354             return
355         if useindices:    
356           line = document.header[i]
357           l = re.compile(r'\\index (.*)$')
358           m = l.match(line)
359           iname = m.group(1)
360           ishortcut = get_value(document.header, '\\shortcut', i, k)
361           if ishortcut != "":
362               preamble.append("\\newindex[" + iname + "]{" + ishortcut + "}")
363         del document.header[i:k + 1]
364     if preamble:
365         insert_to_preamble(0, document, preamble)
366         
367     # deal with index insets
368     # these need to have the argument removed
369     i = 0
370     while True:
371         i = find_token(document.body, "\\begin_inset Index", i)
372         if i == -1:
373             break
374         line = document.body[i]
375         l = re.compile(r'\\begin_inset Index (.*)$')
376         m = l.match(line)
377         itype = m.group(1)
378         if itype == "idx" or indices == "false":
379             document.body[i] = "\\begin_inset Index"
380         else:
381             k = find_end_of_inset(document.body, i)
382             if k == -1:
383                 document.warning("Can't find end of index inset!")
384                 i += 1
385                 continue
386             content = lyx2latex(document, document.body[i:k])
387             # escape quotes
388             content = content.replace('"', r'\"')
389             subst = put_cmd_in_ert("\\sindex[" + itype + "]{" + content + "}")
390             document.body[i:k + 1] = subst
391         i = i + 1
392         
393     # deal with index_print insets
394     i = 0
395     while True:
396         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
397         if i == -1:
398             return
399         k = find_end_of_inset(document.body, i)
400         ptype = get_quoted_value(document.body, 'type', i, k)
401         if ptype == "idx":
402             j = find_token(document.body, "type", i, k)
403             del document.body[j]
404         elif not useindices:
405             del document.body[i:k + 1]
406         else:
407             subst = put_cmd_in_ert("\\printindex[" + ptype + "]{}")
408             document.body[i:k + 1] = subst
409         i = i + 1
410
411
412 def convert_splitindex(document):
413     " Converts index and printindex insets to splitindex-aware format "
414     i = 0
415     while True:
416         i = find_token(document.body, "\\begin_inset Index", i)
417         if i == -1:
418             break
419         document.body[i] = document.body[i].replace("\\begin_inset Index",
420             "\\begin_inset Index idx")
421         i = i + 1
422     i = 0
423     while True:
424         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
425         if i == -1:
426             return
427         if document.body[i + 1].find('LatexCommand printindex') == -1:
428             document.warning("Malformed LyX document: Incomplete printindex inset.")
429             return
430         subst = ["LatexCommand printindex", 
431             "type \"idx\""]
432         document.body[i + 1:i + 2] = subst
433         i = i + 1
434
435
436 def revert_subindex(document):
437     " Reverts \\printsubindex CommandInset types "
438     i = find_token(document.header, '\\use_indices', 0)
439     if i == -1:
440         document.warning("Malformed LyX document: Missing \\use_indices.")
441         return
442     useindices = str2bool(get_value(document.header, "\\use_indices", i))
443     i = 0
444     while True:
445         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
446         if i == -1:
447             return
448         k = find_end_of_inset(document.body, i)
449         ctype = get_value(document.body, 'LatexCommand', i, k)
450         if ctype != "printsubindex":
451             i = k + 1
452             continue
453         ptype = get_quoted_value(document.body, 'type', i, k)
454         if not useindices:
455             del document.body[i:k + 1]
456         else:
457             subst = put_cmd_in_ert("\\printsubindex[" + ptype + "]{}")
458             document.body[i:k + 1] = subst
459         i = i + 1
460
461
462 def revert_printindexall(document):
463     " Reverts \\print[sub]index* CommandInset types "
464     i = find_token(document.header, '\\use_indices', 0)
465     if i == -1:
466         document.warning("Malformed LyX document: Missing \\use_indices.")
467         return
468     useindices = str2bool(get_value(document.header, "\\use_indices", i))
469     i = 0
470     while True:
471         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
472         if i == -1:
473             return
474         k = find_end_of_inset(document.body, i)
475         ctype = get_value(document.body, 'LatexCommand', i, k)
476         if ctype != "printindex*" and ctype != "printsubindex*":
477             i = k
478             continue
479         if not useindices:
480             del document.body[i:k + 1]
481         else:
482             subst = put_cmd_in_ert("\\" + ctype + "{}")
483             document.body[i:k + 1] = subst
484         i = i + 1
485
486
487 def revert_strikeout(document):
488   " Reverts \\strikeout font attribute "
489   changed = revert_font_attrs(document, "\\uuline", "\\uuline")
490   changed = revert_font_attrs(document, "\\uwave", "\\uwave") or changed
491   changed = revert_font_attrs(document, "\\strikeout", "\\sout")  or changed
492   if changed == True:
493     insert_to_preamble(0, document,
494         '% Commands inserted by lyx2lyx for proper underlining\n'
495         + '\\PassOptionsToPackage{normalem}{ulem}\n'
496         + '\\usepackage{ulem}\n')
497
498
499 def revert_ulinelatex(document):
500     " Reverts \\uline font attribute "
501     i = find_token(document.body, '\\bar under', 0)
502     if i == -1:
503         return
504     insert_to_preamble(0, document,
505             '% Commands inserted by lyx2lyx for proper underlining\n'
506             + '\\PassOptionsToPackage{normalem}{ulem}\n'
507             + '\\usepackage{ulem}\n'
508             + '\\let\\cite@rig\\cite\n'
509             + '\\newcommand{\\b@xcite}[2][\\%]{\\def\\def@pt{\\%}\\def\\pas@pt{#1}\n'
510             + '  \\mbox{\\ifx\\def@pt\\pas@pt\\cite@rig{#2}\\else\\cite@rig[#1]{#2}\\fi}}\n'
511             + '\\renewcommand{\\underbar}[1]{{\\let\\cite\\b@xcite\\uline{#1}}}\n')
512
513
514 def revert_custom_processors(document):
515     " Remove bibtex_command and index_command params "
516     i = find_token(document.header, '\\bibtex_command', 0)
517     if i == -1:
518         document.warning("Malformed LyX document: Missing \\bibtex_command.")
519     else:
520         del document.header[i]
521     i = find_token(document.header, '\\index_command', 0)
522     if i == -1:
523         document.warning("Malformed LyX document: Missing \\index_command.")
524     else:
525         del document.header[i]
526
527
528 def convert_nomencl_width(document):
529     " Add set_width param to nomencl_print "
530     i = 0
531     while True:
532       i = find_token(document.body, "\\begin_inset CommandInset nomencl_print", i)
533       if i == -1:
534         break
535       document.body.insert(i + 2, "set_width \"none\"")
536       i = i + 1
537
538
539 def revert_nomencl_width(document):
540     " Remove set_width param from nomencl_print "
541     i = 0
542     while True:
543       i = find_token(document.body, "\\begin_inset CommandInset nomencl_print", i)
544       if i == -1:
545         break
546       j = find_end_of_inset(document.body, i)
547       l = find_token(document.body, "set_width", i, j)
548       if l == -1:
549             document.warning("Can't find set_width option for nomencl_print!")
550             i = j
551             continue
552       del document.body[l]
553       i = j - 1
554
555
556 def revert_nomencl_cwidth(document):
557     " Remove width param from nomencl_print "
558     i = 0
559     while True:
560       i = find_token(document.body, "\\begin_inset CommandInset nomencl_print", i)
561       if i == -1:
562         break
563       j = find_end_of_inset(document.body, i)
564       l = find_token(document.body, "width", i, j)
565       if l == -1:
566         document.warning("Can't find width option for nomencl_print!")
567         i = j
568         continue
569       width = get_quoted_value(document.body, "width", i, j)
570       del document.body[l]
571       add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
572       add_to_preamble(document, ["\\setlength{\\nomlabelwidth}{" + width + "}"])
573       i = j - 1
574
575
576 def revert_applemac(document):
577     " Revert applemac encoding to auto "
578     if document.encoding != "applemac":
579       return
580     document.encoding = "auto"
581     i = find_token(document.header, "\\encoding", 0)
582     if i != -1:
583         document.header[i] = "\\encoding auto"
584
585
586 def revert_longtable_align(document):
587     " Remove longtable alignment setting "
588     i = 0
589     while True:
590       i = find_token(document.body, "\\begin_inset Tabular", i)
591       if i == -1:
592           break
593       end = find_end_of_inset(document.body, i)
594       if end == -1:
595           document.warning("Can't find end of inset at line " + str(i))
596           i += 1
597           continue
598       fline = find_token(document.body, "<features", i, end)
599       if fline == -1:
600           document.warning("Can't find features for inset at line " + str(i))
601           i += 1
602           continue
603       j = document.body[fline].find("longtabularalignment")
604       if j == -1:
605           i += 1
606           continue
607       # FIXME Is this correct? It wipes out everything after the 
608       # one we found.
609       document.body[fline] = document.body[fline][:j - 1] + '>'
610       # since there could be a tabular inside this one, we 
611       # cannot jump to end.
612       i += 1
613
614
615 def revert_branch_filename(document):
616     " Remove \\filename_suffix parameter from branches "
617     i = 0
618     while True:
619         i = find_token(document.header, "\\filename_suffix", i)
620         if i == -1:
621             return
622         del document.header[i]
623
624
625 def revert_paragraph_indentation(document):
626     " Revert custom paragraph indentation to preamble code "
627     i = find_token(document.header, "\\paragraph_indentation", 0)
628     if i == -1:
629       return
630     length = get_value(document.header, "\\paragraph_indentation", i)
631     # we need only remove the line if indentation is default
632     if length != "default":
633       # handle percent lengths
634       length = latex_length(length)[1]
635       add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
636       add_to_preamble(document, ["\\setlength{\\parindent}{" + length + "}"])
637     del document.header[i]
638
639
640 def revert_percent_skip_lengths(document):
641     " Revert relative lengths for paragraph skip separation to preamble code "
642     i = find_token(document.header, "\\defskip", 0)
643     if i == -1:
644         return
645     length = get_value(document.header, "\\defskip", i)
646     # only revert when a custom length was set and when
647     # it used a percent length
648     if length in ('smallskip', 'medskip', 'bigskip'):
649         return
650     # handle percent lengths
651     percent, length = latex_length(length)
652     if percent:
653         add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
654         add_to_preamble(document, ["\\setlength{\\parskip}{" + length + "}"])
655         # set defskip to medskip as default
656         document.header[i] = "\\defskip medskip"
657
658
659 def revert_percent_vspace_lengths(document):
660     " Revert relative VSpace lengths to ERT "
661     i = 0
662     while True:
663       i = find_token(document.body, "\\begin_inset VSpace", i)
664       if i == -1:
665           break
666       # only revert if a custom length was set and if
667       # it used a percent length
668       r = re.compile(r'\\begin_inset VSpace (.*)$')
669       m = r.match(document.body[i])
670       length = m.group(1)
671       if length in ('defskip', 'smallskip', 'medskip', 'bigskip', 'vfill'):
672          i += 1
673          continue
674       # check if the space has a star (protected space)
675       protected = (document.body[i].rfind("*") != -1)
676       if protected:
677           length = length.rstrip('*')
678       # handle percent lengths
679       percent, length = latex_length(length)
680       # revert the VSpace inset to ERT
681       if percent:
682           if protected:
683               subst = put_cmd_in_ert("\\vspace*{" + length + "}")
684           else:
685               subst = put_cmd_in_ert("\\vspace{" + length + "}")
686           document.body[i:i + 2] = subst
687       i += 1
688
689
690 def revert_percent_hspace_lengths(document):
691     " Revert relative HSpace lengths to ERT "
692     i = 0
693     while True:
694       i = find_token(document.body, "\\begin_inset space \\hspace", i)
695       if i == -1:
696           break
697       j = find_end_of_inset(document.body, i)
698       if j == -1:
699           document.warning("Can't find end of inset at line " + str(i))
700           i += 1
701           continue
702       # only revert if a custom length was set...
703       length = get_value(document.body, '\\length', i + 1, j)
704       if length == '':
705           document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
706           i = j
707           continue
708       protected = ""
709       if document.body[i].find("\\hspace*{}") != -1:
710           protected = "*"
711       # ...and if it used a percent length
712       percent, length = latex_length(length)
713       # revert the HSpace inset to ERT
714       if percent:
715           subst = put_cmd_in_ert("\\hspace" + protected + "{" + length + "}")
716           document.body[i:j + 1] = subst
717       # if we did a substitution, this will still be ok
718       i = j
719
720
721 def revert_hspace_glue_lengths(document):
722     " Revert HSpace glue lengths to ERT "
723     i = 0
724     while True:
725       i = find_token(document.body, "\\begin_inset space \\hspace", i)
726       if i == -1:
727           break
728       j = find_end_of_inset(document.body, i)
729       if j == -1:
730           document.warning("Can't find end of inset at line " + str(i))
731           i += 1
732           continue
733       length = get_value(document.body, '\\length', i + 1, j)
734       if length == '':
735           document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
736           i = j
737           continue
738       protected = ""
739       if document.body[i].find("\\hspace*{}") != -1:
740           protected = "*"
741       # only revert if the length contains a plus or minus at pos != 0
742       if length.find('-',1) != -1 or length.find('+',1) != -1:
743           # handle percent lengths
744           length = latex_length(length)[1]
745           # revert the HSpace inset to ERT
746           subst = put_cmd_in_ert("\\hspace" + protected + "{" + length + "}")
747           document.body[i:j+1] = subst
748       i = j
749
750
751 def convert_author_id(document):
752     " Add the author_id to the \\author definition and make sure 0 is not used"
753     i = 0
754     anum = 1
755     re_author = re.compile(r'(\\author) (\".*\")\s*(.*)$')
756     
757     while True:
758         i = find_token(document.header, "\\author", i)
759         if i == -1:
760             break
761         m = re_author.match(document.header[i])
762         if m:
763             name = m.group(2)
764             email = m.group(3)
765             document.header[i] = "\\author %i %s %s" % (anum, name, email)
766         # FIXME Should this really be incremented if we didn't match?
767         anum += 1
768         i += 1
769         
770     i = 0
771     while True:
772         i = find_token(document.body, "\\change_", i)
773         if i == -1:
774             break
775         change = document.body[i].split(' ');
776         if len(change) == 3:
777             type = change[0]
778             author_id = int(change[1])
779             time = change[2]
780             document.body[i] = "%s %i %s" % (type, author_id + 1, time)
781         i += 1
782
783
784 def revert_author_id(document):
785     " Remove the author_id from the \\author definition "
786     i = 0
787     anum = 0
788     rx = re.compile(r'(\\author)\s+(\d+)\s+(\".*\")\s*(.*)$')
789     idmap = dict()
790
791     while True:
792         i = find_token(document.header, "\\author", i)
793         if i == -1:
794             break
795         m = rx.match(document.header[i])
796         if m:
797             author_id = int(m.group(2))
798             idmap[author_id] = anum
799             name = m.group(3)
800             email = m.group(4)
801             document.header[i] = "\\author %s %s" % (name, email)
802         i += 1
803         # FIXME Should this be incremented if we didn't match?
804         anum += 1
805
806     i = 0
807     while True:
808         i = find_token(document.body, "\\change_", i)
809         if i == -1:
810             break
811         change = document.body[i].split(' ');
812         if len(change) == 3:
813             type = change[0]
814             author_id = int(change[1])
815             time = change[2]
816             document.body[i] = "%s %i %s" % (type, idmap[author_id], time)
817         i += 1
818
819
820 def revert_suppress_date(document):
821     " Revert suppressing of default document date to preamble code "
822     i = find_token(document.header, "\\suppress_date", 0)
823     if i == -1:
824         return
825     # remove the preamble line and write to the preamble
826     # when suppress_date was true
827     date = str2bool(get_value(document.header, "\\suppress_date", i))
828     if date:
829         add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
830         add_to_preamble(document, ["\\date{}"])
831     del document.header[i]
832
833
834 def revert_mhchem(document):
835     "Revert mhchem loading to preamble code"
836
837     mhchem = "off"
838     i = find_token(document.header, "\\use_mhchem", 0)
839     if i == -1:
840         document.warning("Malformed LyX document: Could not find mhchem setting.")
841         mhchem = "auto"
842     else:
843         val = get_value(document.header, "\\use_mhchem", i)
844         if val == "1":
845             mhchem = "auto"
846         elif val == "2":
847             mhchem = "on"
848         del document.header[i]
849
850     if mhchem == "off":
851       # don't load case
852       return 
853
854     if mhchem == "auto":
855         i = 0
856         while True:
857             i = find_token(document.body, "\\begin_inset Formula", i)
858             if i == -1:
859                break
860             line = document.body[i]
861             if line.find("\\ce{") != -1 or line.find("\\cf{") != -1:
862               mhchem = "on"
863               break
864             i += 1
865
866     if mhchem == "on":
867         pre = ["% lyx2lyx mhchem commands", 
868           "\\PassOptionsToPackage{version=3}{mhchem}", 
869           "\\usepackage{mhchem}"]
870         add_to_preamble(document, pre) 
871
872
873 def revert_fontenc(document):
874     " Remove fontencoding param "
875     i = find_token(document.header, '\\fontencoding', 0)
876     if i == -1:
877         document.warning("Malformed LyX document: Missing \\fontencoding.")
878         return
879     del document.header[i]
880
881
882 def merge_gbrief(document):
883     " Merge g-brief-en and g-brief-de to one class "
884
885     if document.textclass != "g-brief-de":
886         if document.textclass == "g-brief-en":
887             document.textclass = "g-brief"
888             document.set_textclass()
889         return
890
891     obsoletedby = { "Brieftext":       "Letter",
892                     "Unterschrift":    "Signature",
893                     "Strasse":         "Street",
894                     "Zusatz":          "Addition",
895                     "Ort":             "Town",
896                     "Land":            "State",
897                     "RetourAdresse":   "ReturnAddress",
898                     "MeinZeichen":     "MyRef",
899                     "IhrZeichen":      "YourRef",
900                     "IhrSchreiben":    "YourMail",
901                     "Telefon":         "Phone",
902                     "BLZ":             "BankCode",
903                     "Konto":           "BankAccount",
904                     "Postvermerk":     "PostalComment",
905                     "Adresse":         "Address",
906                     "Datum":           "Date",
907                     "Betreff":         "Reference",
908                     "Anrede":          "Opening",
909                     "Anlagen":         "Encl.",
910                     "Verteiler":       "cc",
911                     "Gruss":           "Closing"}
912     i = 0
913     while 1:
914         i = find_token(document.body, "\\begin_layout", i)
915         if i == -1:
916             break
917
918         layout = document.body[i][14:]
919         if layout in obsoletedby:
920             document.body[i] = "\\begin_layout " + obsoletedby[layout]
921
922         i += 1
923         
924     document.textclass = "g-brief"
925     document.set_textclass()
926
927
928 def revert_gbrief(document):
929     " Revert g-brief to g-brief-en "
930     if document.textclass == "g-brief":
931         document.textclass = "g-brief-en"
932         document.set_textclass()
933
934
935 def revert_html_options(document):
936     " Remove html options "
937     i = find_token(document.header, '\\html_use_mathml', 0)
938     if i != -1:
939         del document.header[i]
940     i = find_token(document.header, '\\html_be_strict', 0)
941     if i != -1:
942         del document.header[i]
943
944
945 def revert_includeonly(document):
946     i = 0
947     while True:
948         i = find_token(document.header, "\\begin_includeonly", i)
949         if i == -1:
950             return
951         j = find_end_of(document.header, i, "\\begin_includeonly", "\\end_includeonly")
952         if j == -1:
953             document.warning("Unable to find end of includeonly section!!")
954             break
955         document.header[i : j + 1] = []
956
957
958 def revert_includeall(document):
959     " Remove maintain_unincluded_children param "
960     i = find_token(document.header, '\\maintain_unincluded_children', 0)
961     if i != -1:
962         del document.header[i]
963
964
965 def revert_multirow(document):
966     " Revert multirow cells in tables to TeX-code"
967     i = 0
968     multirow = False
969     while True:
970       # cell type 3 is multirow begin cell
971       i = find_token(document.body, '<cell multirow="3"', i)
972       if i == -1:
973           break
974       # a multirow cell was found
975       multirow = True
976       # remove the multirow tag, set the valignment to top
977       # and remove the bottom line
978       # FIXME Are we sure these always have space around them?
979       document.body[i] = document.body[i].replace(' multirow="3" ', ' ')
980       document.body[i] = document.body[i].replace('valignment="middle"', 'valignment="top"')
981       document.body[i] = document.body[i].replace(' bottomline="true" ', ' ')
982       # write ERT to create the multirow cell
983       # use 2 rows and 2cm as default with because the multirow span
984       # and the column width is only hardly accessible
985       cend = find_token(document.body, "</cell>", i)
986       if cend == -1:
987           document.warning("Malformed LyX document: Could not find end of tabular cell.")
988           i += 1
989           continue
990       blay = find_token(document.body, "\\begin_layout", i, cend)
991       if blay == -1:
992           document.warning("Can't find layout for cell!")
993           i = j
994           continue
995       bend = find_end_of_layout(document.body, blay)
996       if blay == -1:
997           document.warning("Can't find end of layout for cell!")
998           i = cend
999           continue
1000
1001       # do the later one first, so as not to mess up the numbering
1002       # we are wrapping the whole cell in this ert
1003       # so before the end of the layout...
1004       document.body[bend:bend] = put_cmd_in_ert("}")
1005       # ...and after the beginning
1006       document.body[blay+1:blay+1] = put_cmd_in_ert("\\multirow{2}{2cm}{")
1007
1008       while True:
1009           # cell type 4 is multirow part cell
1010           k = find_token(document.body, '<cell multirow="4"', cend)
1011           if k == -1:
1012               break
1013           # remove the multirow tag, set the valignment to top
1014           # and remove the top line
1015           # FIXME Are we sure these always have space around them?
1016           document.body[k] = document.body[k].replace(' multirow="4" ', ' ')
1017           document.body[k] = document.body[k].replace('valignment="middle"', 'valignment="top"')
1018           document.body[k] = document.body[k].replace(' topline="true" ', ' ')
1019           k += 1
1020       # this will always be ok
1021       i = cend
1022
1023     if multirow == True:
1024         add_to_preamble(document, 
1025           ["% lyx2lyx multirow additions ", "\\usepackage{multirow}"])
1026
1027
1028 def convert_math_output(document):
1029     " Convert \html_use_mathml to \html_math_output "
1030     i = find_token(document.header, "\\html_use_mathml", 0)
1031     if i == -1:
1032         return
1033     rgx = re.compile(r'\\html_use_mathml\s+(\w+)')
1034     m = rgx.match(document.header[i])
1035     newval = "0" # MathML
1036     if m:
1037       val = str2bool(m.group(1))
1038       if not val:
1039         newval = "2" # Images
1040     else:
1041       document.warning("Can't match " + document.header[i])
1042     document.header[i] = "\\html_math_output " + newval
1043
1044
1045 def revert_math_output(document):
1046     " Revert \html_math_output to \html_use_mathml "
1047     i = find_token(document.header, "\\html_math_output", 0)
1048     if i == -1:
1049         return
1050     rgx = re.compile(r'\\html_math_output\s+(\d)')
1051     m = rgx.match(document.header[i])
1052     newval = "true"
1053     if m:
1054         val = m.group(1)
1055         if val == "1" or val == "2":
1056             newval = "false"
1057     else:
1058         document.warning("Unable to match " + document.header[i])
1059     document.header[i] = "\\html_use_mathml " + newval
1060                 
1061
1062
1063 def revert_inset_preview(document):
1064     " Dissolves the preview inset "
1065     i = 0
1066     while True:
1067       i = find_token(document.body, "\\begin_inset Preview", i)
1068       if i == -1:
1069           return
1070       iend = find_end_of_inset(document.body, i)
1071       if iend == -1:
1072           document.warning("Malformed LyX document: Could not find end of Preview inset.")
1073           i += 1
1074           continue
1075       
1076       # This has several issues.
1077       # We need to do something about the layouts inside InsetPreview.
1078       # If we just leave the first one, then we have something like:
1079       # \begin_layout Standard
1080       # ...
1081       # \begin_layout Standard
1082       # and we get a "no \end_layout" error. So something has to be done.
1083       # Ideally, we would check if it is the same as the layout we are in.
1084       # If so, we just remove it; if not, we end the active one. But it is 
1085       # not easy to know what layout we are in, due to depth changes, etc,
1086       # and it is not clear to me how much work it is worth doing. In most
1087       # cases, the layout will probably be the same.
1088       # 
1089       # For the same reason, we have to remove the \end_layout tag at the
1090       # end of the last layout in the inset. Again, that will sometimes be
1091       # wrong, but it will usually be right. To know what to do, we would
1092       # again have to know what layout the inset is in.
1093       
1094       blay = find_token(document.body, "\\begin_layout", i, iend)
1095       if blay == -1:
1096           document.warning("Can't find layout for preview inset!")
1097           # always do the later one first...
1098           del document.body[iend]
1099           del document.body[i]
1100           # deletions mean we do not need to reset i
1101           continue
1102
1103       # This is where we would check what layout we are in.
1104       # The check for Standard is definitely wrong.
1105       # 
1106       # lay = document.body[blay].split(None, 1)[1]
1107       # if lay != oldlayout:
1108       #     # record a boolean to tell us what to do later....
1109       #     # better to do it later, since (a) it won't mess up
1110       #     # the numbering and (b) we only modify at the end.
1111         
1112       # we want to delete the last \\end_layout in this inset, too.
1113       # note that this may not be the \\end_layout that goes with blay!!
1114       bend = find_end_of_layout(document.body, blay)
1115       while True:
1116           tmp = find_token(document.body, "\\end_layout", bend + 1, iend)
1117           if tmp == -1:
1118               break
1119           bend = tmp
1120       if bend == blay:
1121           document.warning("Unable to find last layout in preview inset!")
1122           del document.body[iend]
1123           del document.body[i]
1124           # deletions mean we do not need to reset i
1125           continue
1126       # always do the later one first...
1127       del document.body[iend]
1128       del document.body[bend]
1129       del document.body[i:blay + 1]
1130       # we do not need to reset i
1131                 
1132
1133 def revert_equalspacing_xymatrix(document):
1134     " Revert a Formula with xymatrix@! to an ERT inset "
1135     i = 0
1136     has_preamble = False
1137     has_equal_spacing = False
1138
1139     while True:
1140       i = find_token(document.body, "\\begin_inset Formula", i)
1141       if i == -1:
1142           break
1143       j = find_end_of_inset(document.body, i)
1144       if j == -1:
1145           document.warning("Malformed LyX document: Could not find end of Formula inset.")
1146           i += 1
1147           continue
1148       
1149       for curline in range(i,j):
1150           found = document.body[curline].find("\\xymatrix@!")
1151           if found != -1:
1152               break
1153  
1154       if found != -1:
1155           has_equal_spacing = True
1156           content = [document.body[i][21:]]
1157           content += document.body[i + 1:j]
1158           subst = put_cmd_in_ert(content)
1159           document.body[i:j + 1] = subst
1160           i += len(subst) - (j - i) + 1
1161       else:
1162           for curline in range(i,j):
1163               l = document.body[curline].find("\\xymatrix")
1164               if l != -1:
1165                   has_preamble = True;
1166                   break;
1167           i = j + 1
1168   
1169     if has_equal_spacing and not has_preamble:
1170         add_to_preamble(document, ['% lyx2lyx xymatrix addition', '\\usepackage[all]{xy}'])
1171
1172
1173 def revert_notefontcolor(document):
1174     " Reverts greyed-out note font color to preamble code "
1175
1176     i = find_token(document.header, "\\notefontcolor", 0)
1177     if i == -1:
1178         return
1179
1180     # are there any grey notes?
1181     if find_token(document.body, "\\begin_inset Note Greyedout", 0) == -1:
1182         # no need to do anything, and \renewcommand will throw an error
1183         # since lyxgreyedout will not exist.
1184         return
1185
1186     colorcode = get_value(document.header, '\\notefontcolor', i)
1187     del document.header[i]
1188     # the color code is in the form #rrggbb where every character denotes a hex number
1189     red = hex2ratio(colorcode[1:3])
1190     green = hex2ratio(colorcode[3:5])
1191     blue = hex2ratio(colorcode[5:7])
1192     # write the preamble
1193     insert_to_preamble(0, document,
1194       ['% Commands inserted by lyx2lyx to set the font color',
1195         '% for greyed-out notes',
1196         '\\@ifundefined{definecolor}{\\usepackage{color}}{}'
1197         '\\definecolor{note_fontcolor}{rgb}{%s,%s,%s}' % (red, green, blue),
1198         '\\renewenvironment{lyxgreyedout}',
1199         ' {\\textcolor{note_fontcolor}\\bgroup}{\\egroup}'])
1200
1201
1202 def revert_turkmen(document):
1203     "Set language Turkmen to English" 
1204
1205     if document.language == "turkmen": 
1206         document.language = "english" 
1207         i = find_token(document.header, "\\language", 0) 
1208         if i != -1: 
1209             document.header[i] = "\\language english" 
1210
1211     j = 0 
1212     while True: 
1213         j = find_token(document.body, "\\lang turkmen", j) 
1214         if j == -1: 
1215             return 
1216         document.body[j] = document.body[j].replace("\\lang turkmen", "\\lang english") 
1217         j += 1 
1218
1219
1220 def revert_fontcolor(document):
1221     " Reverts font color to preamble code "
1222     i = find_token(document.header, "\\fontcolor", 0)
1223     if i == -1:
1224         return
1225     colorcode = get_value(document.header, '\\fontcolor', i)
1226     del document.header[i]
1227     # don't clutter the preamble if font color is not set
1228     if colorcode == "#000000":
1229         return
1230     # the color code is in the form #rrggbb where every character denotes a hex number
1231     red = hex2ratio(colorcode[1:3])
1232     green = hex2ratio(colorcode[3:5])
1233     blue = hex2ratio(colorcode[5:7])
1234     # write the preamble
1235     insert_to_preamble(0, document,
1236       ['% Commands inserted by lyx2lyx to set the font color',
1237       '\\@ifundefined{definecolor}{\\usepackage{color}}{}',
1238       '\\definecolor{document_fontcolor}{rgb}{%s,%s,%s}' % (red, green, blue),
1239       '\\color{document_fontcolor}'])
1240
1241
1242 def revert_shadedboxcolor(document):
1243     " Reverts shaded box color to preamble code "
1244     i = find_token(document.header, "\\boxbgcolor", 0)
1245     if i == -1:
1246         return
1247     colorcode = get_value(document.header, '\\boxbgcolor', i)
1248     del document.header[i]
1249     # the color code is in the form #rrggbb
1250     red = hex2ratio(colorcode[1:3])
1251     green = hex2ratio(colorcode[3:5])
1252     blue = hex2ratio(colorcode[5:7])
1253     # write the preamble
1254     insert_to_preamble(0, document,
1255       ['% Commands inserted by lyx2lyx to set the color of boxes with shaded background',
1256       '\\@ifundefined{definecolor}{\\usepackage{color}}{}',
1257       "\\definecolor{shadecolor}{rgb}{%s,%s,%s}" % (red, green, blue)])
1258
1259
1260 def revert_lyx_version(document):
1261     " Reverts LyX Version information from Inset Info "
1262     version = "LyX version"
1263     try:
1264         import lyx2lyx_version
1265         version = lyx2lyx_version.version
1266     except:
1267         pass
1268
1269     i = 0
1270     while 1:
1271         i = find_token(document.body, '\\begin_inset Info', i)
1272         if i == -1:
1273             return
1274         j = find_end_of_inset(document.body, i + 1)
1275         if j == -1:
1276             document.warning("Malformed LyX document: Could not find end of Info inset.")
1277             i += 1
1278             continue
1279
1280         # We expect:
1281         # \begin_inset Info
1282         # type  "lyxinfo"
1283         # arg   "version"
1284         # \end_inset
1285         typ = get_quoted_value(document.body, "type", i, j)
1286         arg = get_quoted_value(document.body, "arg", i, j)
1287         if arg != "version" or typ != "lyxinfo":
1288             i = j + 1
1289             continue
1290
1291         # We do not actually know the version of LyX used to produce the document.
1292         # But we can use our version, since we are reverting.
1293         s = [version]
1294         # Now we want to check if the line after "\end_inset" is empty. It normally
1295         # is, so we want to remove it, too.
1296         lastline = j + 1
1297         if document.body[j + 1].strip() == "":
1298             lastline = j + 2
1299         document.body[i: lastline] = s
1300         i = i + 1
1301
1302
1303 def revert_math_scale(document):
1304   " Remove math scaling and LaTeX options "
1305   i = find_token(document.header, '\\html_math_img_scale', 0)
1306   if i != -1:
1307     del document.header[i]
1308   i = find_token(document.header, '\\html_latex_start', 0)
1309   if i != -1:
1310     del document.header[i]
1311   i = find_token(document.header, '\\html_latex_end', 0)
1312   if i != -1:
1313     del document.header[i]
1314
1315
1316 def revert_pagesizes(document):
1317   " Revert page sizes to default "
1318   i = find_token(document.header, '\\papersize', 0)
1319   if i != -1:
1320     size = document.header[i][11:]
1321     if size == "a0paper" or size == "a1paper" or size == "a2paper" \
1322     or size == "a6paper" or size == "b0paper" or size == "b1paper" \
1323     or size == "b2paper" or size == "b6paper" or size == "b0j" \
1324     or size == "b1j" or size == "b2j" or size == "b3j" or size == "b4j" \
1325     or size == "b5j" or size == "b6j":
1326       del document.header[i]
1327
1328
1329 def revert_DIN_C_pagesizes(document):
1330   " Revert DIN C page sizes to default "
1331   i = find_token(document.header, '\\papersize', 0)
1332   if i != -1:
1333     size = document.header[i][11:]
1334     if size == "c0paper" or size == "c1paper" or size == "c2paper" \
1335     or size == "c3paper" or size == "c4paper" or size == "c5paper" \
1336     or size == "c6paper":
1337       del document.header[i]
1338
1339
1340 def convert_html_quotes(document):
1341   " Remove quotes around html_latex_start and html_latex_end "
1342
1343   i = find_token(document.header, '\\html_latex_start', 0)
1344   if i != -1:
1345     line = document.header[i]
1346     l = re.compile(r'\\html_latex_start\s+"(.*)"')
1347     m = l.match(line)
1348     if m:
1349       document.header[i] = "\\html_latex_start " + m.group(1)
1350       
1351   i = find_token(document.header, '\\html_latex_end', 0)
1352   if i != -1:
1353     line = document.header[i]
1354     l = re.compile(r'\\html_latex_end\s+"(.*)"')
1355     m = l.match(line)
1356     if m:
1357       document.header[i] = "\\html_latex_end " + m.group(1)
1358       
1359
1360 def revert_html_quotes(document):
1361   " Remove quotes around html_latex_start and html_latex_end "
1362   
1363   i = find_token(document.header, '\\html_latex_start', 0)
1364   if i != -1:
1365     line = document.header[i]
1366     l = re.compile(r'\\html_latex_start\s+(.*)')
1367     m = l.match(line)
1368     if not m:
1369         document.warning("Weird html_latex_start line: " + line)
1370         del document.header[i]
1371     else:
1372         document.header[i] = "\\html_latex_start \"" + m.group(1) + "\""
1373       
1374   i = find_token(document.header, '\\html_latex_end', 0)
1375   if i != -1:
1376     line = document.header[i]
1377     l = re.compile(r'\\html_latex_end\s+(.*)')
1378     m = l.match(line)
1379     if not m:
1380         document.warning("Weird html_latex_end line: " + line)
1381         del document.header[i]
1382     else:
1383         document.header[i] = "\\html_latex_end \"" + m.group(1) + "\""
1384
1385
1386 def revert_output_sync(document):
1387   " Remove forward search options "
1388   i = find_token(document.header, '\\output_sync_macro', 0)
1389   if i != -1:
1390     del document.header[i]
1391   i = find_token(document.header, '\\output_sync', 0)
1392   if i != -1:
1393     del document.header[i]
1394
1395
1396 def revert_align_decimal(document):
1397   i = 0
1398   while True:
1399     i = find_token(document.body, "\\begin_inset Tabular", i)
1400     if i == -1:
1401       return
1402     j = find_end_of_inset(document.body, i)
1403     if j == -1:
1404       document.warning("Unable to find end of Tabular inset at line " + str(i))
1405       i += 1
1406       continue
1407     cell = find_token(document.body, "<cell", i, j)
1408     if cell == -1:
1409       document.warning("Can't find any cells in Tabular inset at line " + str(i))
1410       i = j
1411       continue
1412     k = i + 1
1413     while True:
1414       k = find_token(document.body, "<column", k, cell)
1415       if k == -1:
1416         return
1417       if document.body[k].find('alignment="decimal"') == -1:
1418         k += 1
1419         continue
1420       remove_option(document.body, k, 'decimal_point')
1421       document.body[k] = \
1422         document.body[k].replace('alignment="decimal"', 'alignment="center"')
1423       k += 1
1424
1425
1426 def convert_optarg(document):
1427   " Convert \\begin_inset OptArg to \\begin_inset Argument "
1428   i = 0
1429   while 1:
1430     i = find_token(document.body, '\\begin_inset OptArg', i)
1431     if i == -1:
1432       return
1433     document.body[i] = "\\begin_inset Argument"
1434     i += 1
1435
1436
1437 def revert_argument(document):
1438   " Convert \\begin_inset Argument to \\begin_inset OptArg "
1439   i = 0
1440   while 1:
1441     i = find_token(document.body, '\\begin_inset Argument', i)
1442     if i == -1:
1443       return
1444     document.body[i] = "\\begin_inset OptArg"
1445     i += 1
1446
1447
1448 def revert_makebox(document):
1449   " Convert \\makebox to TeX code "
1450   i = 0
1451   while 1:
1452     # only revert frameless boxes without an inner box
1453     i = find_token(document.body, '\\begin_inset Box Frameless', i)
1454     if i == -1:
1455       return
1456     z = find_end_of_inset(document.body, i)
1457     if z == -1:
1458       document.warning("Malformed LyX document: Can't find end of box inset.")
1459       i += 1
1460       continue
1461     blay = find_token(document.body, "\\begin_layout", i, z)
1462     if blay == -1:
1463       document.warning("Malformed LyX document: Can't find layout in box.")
1464       i = z
1465       continue
1466     # by looking before the layout we make sure we're actually finding
1467     # an option, not text.
1468     j = find_token(document.body, 'use_makebox', i, blay)
1469     if j == -1:
1470         i = z
1471         continue
1472     val = get_value(document.body, 'use_makebox', j)
1473     if val != "1":
1474         del document.body[j]
1475         i = z
1476         continue
1477     bend = find_end_of_layout(document.body, blay)
1478     if bend == -1 or bend > z:
1479         document.warning("Malformed LyX document: Can't find end of layout in box.")
1480         i = z
1481         continue
1482     # determine the alignment
1483     align = get_quoted_value(document.body, 'hor_pos', i, blay, "c")
1484     # determine the width
1485     length = get_quoted_value(document.body, 'width', i, blay, "50col%")
1486     length = latex_length(length)[1]
1487     # remove the \end_layout \end_inset pair
1488     document.body[bend:z + 1] = put_cmd_in_ert("}")
1489     subst = "\\makebox[" + length + "][" \
1490       + align + "]{"
1491     document.body[i:blay + 1] = put_cmd_in_ert(subst)
1492     i += 1
1493
1494
1495 def convert_use_makebox(document):
1496   " Adds use_makebox option for boxes "
1497   i = 0
1498   while 1:
1499     i = find_token(document.body, '\\begin_inset Box', i)
1500     if i == -1:
1501       return
1502     # all of this is to make sure we actually find the use_parbox
1503     # that is an option for this box, not some text elsewhere.
1504     z = find_end_of_inset(document.body, i)
1505     if z == -1:
1506       document.warning("Can't find end of box inset!!")
1507       i += 1
1508       continue
1509     blay = find_token(document.body, "\\begin_layout", i, z)
1510     if blay == -1:
1511       document.warning("Can't find layout in box inset!!")
1512       i = z
1513       continue
1514     # so now we are looking for use_parbox before the box's layout
1515     k = find_token(document.body, 'use_parbox', i, blay)
1516     if k == -1:
1517       document.warning("Malformed LyX document: Can't find use_parbox statement in box.")
1518       i = z
1519       continue
1520     document.body.insert(k + 1, "use_makebox 0")
1521     i = z + 1
1522
1523
1524 def revert_IEEEtran(document):
1525   " Convert IEEEtran layouts and styles to TeX code "
1526   if document.textclass != "IEEEtran":
1527     return
1528   revert_flex_inset(document, "IEEE membership", "\\IEEEmembership", 0)
1529   revert_flex_inset(document, "Lowercase", "\\MakeLowercase", 0)
1530   layouts = ("Special Paper Notice", "After Title Text", "Publication ID",
1531              "Page headings", "Biography without photo")
1532   latexcmd = {"Special Paper Notice": "\\IEEEspecialpapernotice",
1533               "After Title Text":     "\\IEEEaftertitletext",
1534               "Publication ID":       "\\IEEEpubid"}
1535   obsoletedby = {"Page headings":            "MarkBoth",
1536                  "Biography without photo":  "BiographyNoPhoto"}
1537   for layout in layouts:
1538     i = 0
1539     while True:
1540         i = find_token(document.body, '\\begin_layout ' + layout, i)
1541         if i == -1:
1542           break
1543         j = find_end_of_layout(document.body, i)
1544         if j == -1:
1545           document.warning("Malformed LyX document: Can't find end of " + layout + " layout.")
1546           i += 1
1547           continue
1548         if layout in obsoletedby:
1549           document.body[i] = "\\begin_layout " + obsoletedby[layout]
1550           i = j
1551           continue
1552         content = lyx2latex(document, document.body[i:j + 1])
1553         add_to_preamble(document, [latexcmd[layout] + "{" + content + "}"])
1554         del document.body[i:j + 1]
1555         # no need to reset i
1556
1557
1558 def convert_prettyref(document):
1559         " Converts prettyref references to neutral formatted refs "
1560         re_ref = re.compile("^\s*reference\s+\"(\w+):(\S+)\"")
1561         nm_ref = re.compile("^\s*name\s+\"(\w+):(\S+)\"")
1562
1563         i = 0
1564         while True:
1565                 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1566                 if i == -1:
1567                         break
1568                 j = find_end_of_inset(document.body, i)
1569                 if j == -1:
1570                         document.warning("Malformed LyX document: No end of InsetRef!")
1571                         i += 1
1572                         continue
1573                 k = find_token(document.body, "LatexCommand prettyref", i, j)
1574                 if k != -1:
1575                         document.body[k] = "LatexCommand formatted"
1576                 i = j + 1
1577         document.header.insert(-1, "\\use_refstyle 0")
1578                 
1579  
1580 def revert_refstyle(document):
1581         " Reverts neutral formatted refs to prettyref "
1582         re_ref = re.compile("^reference\s+\"(\w+):(\S+)\"")
1583         nm_ref = re.compile("^\s*name\s+\"(\w+):(\S+)\"")
1584
1585         i = 0
1586         while True:
1587                 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1588                 if i == -1:
1589                         break
1590                 j = find_end_of_inset(document.body, i)
1591                 if j == -1:
1592                         document.warning("Malformed LyX document: No end of InsetRef")
1593                         i += 1
1594                         continue
1595                 k = find_token(document.body, "LatexCommand formatted", i, j)
1596                 if k != -1:
1597                         document.body[k] = "LatexCommand prettyref"
1598                 i = j + 1
1599         i = find_token(document.header, "\\use_refstyle", 0)
1600         if i != -1:
1601                 document.header.pop(i)
1602  
1603
1604 def revert_nameref(document):
1605   " Convert namerefs to regular references "
1606   cmds = ["Nameref", "nameref"]
1607   foundone = False
1608   rx = re.compile(r'reference "(.*)"')
1609   for cmd in cmds:
1610     i = 0
1611     oldcmd = "LatexCommand " + cmd
1612     while 1:
1613       # It seems better to look for this, as most of the reference
1614       # insets won't be ones we care about.
1615       i = find_token(document.body, oldcmd, i)
1616       if i == -1:
1617         break
1618       cmdloc = i
1619       i += 1
1620       # Make sure it is actually in an inset!
1621       # A normal line could begin with "LatexCommand nameref"!
1622       val = is_in_inset(document.body, cmdloc, \
1623           "\\begin_inset CommandInset ref")
1624       if not val:
1625           continue
1626       stins, endins = val
1627
1628       # ok, so it is in an InsetRef
1629       refline = find_token(document.body, "reference", stins, endins)
1630       if refline == -1:
1631         document.warning("Can't find reference for inset at line " + stinst + "!!")
1632         continue
1633       m = rx.match(document.body[refline])
1634       if not m:
1635         document.warning("Can't match reference line: " + document.body[ref])
1636         continue
1637       foundone = True
1638       ref = m.group(1)
1639       newcontent = put_cmd_in_ert('\\' + cmd + '{' + ref + '}')
1640       document.body[stins:endins + 1] = newcontent
1641
1642   if foundone:
1643     add_to_preamble(document, "\usepackage{nameref}")
1644
1645
1646 def remove_Nameref(document):
1647   " Convert Nameref commands to nameref commands "
1648   i = 0
1649   while 1:
1650     # It seems better to look for this, as most of the reference
1651     # insets won't be ones we care about.
1652     i = find_token(document.body, "LatexCommand Nameref" , i)
1653     if i == -1:
1654       break
1655     cmdloc = i
1656     i += 1
1657     
1658     # Make sure it is actually in an inset!
1659     val = is_in_inset(document.body, cmdloc, \
1660         "\\begin_inset CommandInset ref")
1661     if not val:
1662       continue
1663     document.body[cmdloc] = "LatexCommand nameref"
1664
1665
1666 def revert_mathrsfs(document):
1667     " Load mathrsfs if \mathrsfs us use in the document "
1668     i = 0
1669     for line in document.body:
1670       if line.find("\\mathscr{") != -1:
1671         add_to_preamble(document, ["% lyx2lyx mathrsfs addition", "\\usepackage{mathrsfs}"])
1672         return
1673
1674
1675 def convert_flexnames(document):
1676     "Convert \\begin_inset Flex Custom:Style to \\begin_inset Flex Style and similarly for CharStyle and Element."
1677     
1678     i = 0
1679     rx = re.compile(r'^\\begin_inset Flex (?:Custom|CharStyle|Element):(.+)$')
1680     while True:
1681       i = find_token(document.body, "\\begin_inset Flex", i)
1682       if i == -1:
1683         return
1684       m = rx.match(document.body[i])
1685       if m:
1686         document.body[i] = "\\begin_inset Flex " + m.group(1)
1687       i += 1
1688
1689
1690 flex_insets = {
1691   "Alert" : "CharStyle:Alert",
1692   "Code" : "CharStyle:Code",
1693   "Concepts" : "CharStyle:Concepts",
1694   "E-Mail" : "CharStyle:E-Mail",
1695   "Emph" : "CharStyle:Emph",
1696   "Expression" : "CharStyle:Expression",
1697   "Initial" : "CharStyle:Initial",
1698   "Institute" : "CharStyle:Institute",
1699   "Meaning" : "CharStyle:Meaning",
1700   "Noun" : "CharStyle:Noun",
1701   "Strong" : "CharStyle:Strong",
1702   "Structure" : "CharStyle:Structure",
1703   "ArticleMode" : "Custom:ArticleMode",
1704   "Endnote" : "Custom:Endnote",
1705   "Glosse" : "Custom:Glosse",
1706   "PresentationMode" : "Custom:PresentationMode",
1707   "Tri-Glosse" : "Custom:Tri-Glosse"
1708 }
1709
1710 flex_elements = {
1711   "Abbrev" : "Element:Abbrev",
1712   "CCC-Code" : "Element:CCC-Code",
1713   "Citation-number" : "Element:Citation-number",
1714   "City" : "Element:City",
1715   "Code" : "Element:Code",
1716   "CODEN" : "Element:CODEN",
1717   "Country" : "Element:Country",
1718   "Day" : "Element:Day",
1719   "Directory" : "Element:Directory",
1720   "Dscr" : "Element:Dscr",
1721   "Email" : "Element:Email",
1722   "Emph" : "Element:Emph",
1723   "Filename" : "Element:Filename",
1724   "Firstname" : "Element:Firstname",
1725   "Fname" : "Element:Fname",
1726   "GuiButton" : "Element:GuiButton",
1727   "GuiMenu" : "Element:GuiMenu",
1728   "GuiMenuItem" : "Element:GuiMenuItem",
1729   "ISSN" : "Element:ISSN",
1730   "Issue-day" : "Element:Issue-day",
1731   "Issue-months" : "Element:Issue-months",
1732   "Issue-number" : "Element:Issue-number",
1733   "KeyCap" : "Element:KeyCap",
1734   "KeyCombo" : "Element:KeyCombo",
1735   "Keyword" : "Element:Keyword",
1736   "Literal" : "Element:Literal",
1737   "MenuChoice" : "Element:MenuChoice",
1738   "Month" : "Element:Month",
1739   "Orgdiv" : "Element:Orgdiv",
1740   "Orgname" : "Element:Orgname",
1741   "Postcode" : "Element:Postcode",
1742   "SS-Code" : "Element:SS-Code",
1743   "SS-Title" : "Element:SS-Title",
1744   "State" : "Element:State",
1745   "Street" : "Element:Street",
1746   "Surname" : "Element:Surname",
1747   "Volume" : "Element:Volume",
1748   "Year" : "Element:Year"
1749 }
1750
1751
1752 def revert_flexnames(document):
1753   if document.backend == "latex":
1754     flexlist = flex_insets
1755   else:
1756     flexlist = flex_elements
1757   
1758   rx = re.compile(r'^\\begin_inset Flex\s+(.+)$')
1759   i = 0
1760   while True:
1761     i = find_token(document.body, "\\begin_inset Flex", i)
1762     if i == -1:
1763       return
1764     m = rx.match(document.body[i])
1765     if not m:
1766       document.warning("Illegal flex inset: " + document.body[i])
1767       i += 1
1768       continue
1769     style = m.group(1)
1770     if style in flexlist:
1771       document.body[i] = "\\begin_inset Flex " + flexlist[style]
1772     i += 1
1773
1774
1775 def convert_mathdots(document):
1776     " Load mathdots automatically "
1777     i = find_token(document.header, "\\use_esint" , 0)
1778     if i != -1:
1779       document.header.insert(i + 1, "\\use_mathdots 1")
1780
1781
1782 def revert_mathdots(document):
1783     " Load mathdots if used in the document "
1784
1785     mathdots = find_token(document.header, "\\use_mathdots" , 0)
1786     if mathdots == -1:
1787       document.warning("No \\usemathdots line. Assuming auto.")
1788     else:
1789       val = get_value(document.header, "\\use_mathdots", mathdots)
1790       del document.header[mathdots]
1791       try:
1792         usedots = int(val)
1793       except:
1794         document.warning("Invalid \\use_mathdots value: " + val + ". Assuming auto.")
1795         # probably usedots has not been changed, but be safe.
1796         usedots = 1
1797
1798       if usedots == 0:
1799         # do not load case
1800         return
1801       if usedots == 2:
1802         # force load case
1803         add_to_preamble(["% lyx2lyx mathdots addition", "\\usepackage{mathdots}"])
1804         return
1805     
1806     # so we are in the auto case. we want to load mathdots if \iddots is used.
1807     i = 0
1808     while True:
1809       i = find_token(document.body, '\\begin_inset Formula', i)
1810       if i == -1:
1811         return
1812       j = find_end_of_inset(document.body, i)
1813       if j == -1:
1814         document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1815         i += 1
1816         continue
1817       code = "\n".join(document.body[i:j])
1818       if code.find("\\iddots") != -1:
1819         add_to_preamble(document, ["% lyx2lyx mathdots addition", 
1820         "\\@ifundefined{iddots}{\\usepackage{mathdots}}"])
1821         return
1822       i = j
1823
1824
1825 def convert_rule(document):
1826     " Convert \\lyxline to CommandInset line. "
1827     i = 0
1828     
1829     inset = ['\\begin_inset CommandInset line',
1830       'LatexCommand rule',
1831       'offset "0.5ex"',
1832       'width "100line%"',
1833       'height "1pt"', '',
1834       '\\end_inset', '', '']
1835
1836     # if paragraphs are indented, we may have to unindent to get the
1837     # line to be full-width.
1838     indent = get_value(document.header, "\\paragraph_separation", 0)
1839     have_indent = (indent == "indent")
1840
1841     while True:
1842       i = find_token(document.body, "\\lyxline" , i)
1843       if i == -1:
1844         return
1845
1846       # we need to find out if this line follows other content
1847       # in its paragraph. find its layout....
1848       lastlay = find_token_backwards(document.body, "\\begin_layout", i)
1849       if lastlay == -1:
1850         document.warning("Can't find layout for line at " + str(i))
1851         # do the best we can.
1852         document.body[i:i+1] = inset
1853         i += len(inset)
1854         continue
1855
1856       # ...and look for other content before it.
1857       lineisfirst = True
1858       for line in document.body[lastlay + 1:i]:
1859         # is it empty or a paragraph option?
1860         if not line or line[0] == '\\':
1861           continue
1862         lineisfirst = False
1863         break
1864
1865       if lineisfirst:
1866         document.body[i:i+1] = inset
1867         if indent:
1868           # we need to unindent, lest the line be too long
1869           document.body.insert(lastlay + 1, "\\noindent")
1870         i += len(inset)
1871       else:
1872         # so our line is in the middle of a paragraph
1873         # we need to add a new line, lest this line follow the
1874         # other content on that line and run off the side of the page
1875         document.body[i:i+1] = inset
1876         document.body[i:i] = ["\\begin_inset Newline newline", "\\end_inset", ""]
1877       i += len(inset)
1878
1879
1880 def revert_rule(document):
1881     " Revert line insets to Tex code "
1882     i = 0
1883     while 1:
1884       i = find_token(document.body, "\\begin_inset CommandInset line" , i)
1885       if i == -1:
1886         return
1887       # find end of inset
1888       j = find_token(document.body, "\\end_inset" , i)
1889       if j == -1:
1890         document.warning("Malformed LyX document: Can't find end of line inset.")
1891         return
1892       # determine the optional offset
1893       offset = get_quoted_value(document.body, 'offset', i, j)
1894       if offset:
1895         offset = '[' + offset + ']'
1896       # determine the width
1897       width = get_quoted_value(document.body, 'width', i, j, "100col%")
1898       width = latex_length(width)[1]
1899       # determine the height
1900       height = get_quoted_value(document.body, 'height', i, j, "1pt")
1901       height = latex_length(height)[1]
1902       # output the \rule command
1903       subst = "\\rule[" + offset + "]{" + width + "}{" + height + "}"
1904       document.body[i:j + 1] = put_cmd_in_ert(subst)
1905       i += len(subst) - (j - i)
1906
1907
1908 def revert_diagram(document):
1909   " Add the feyn package if \\Diagram is used in math "
1910   i = 0
1911   while True:
1912     i = find_token(document.body, '\\begin_inset Formula', i)
1913     if i == -1:
1914       return
1915     j = find_end_of_inset(document.body, i)
1916     if j == -1:
1917         document.warning("Malformed LyX document: Can't find end of Formula inset.")
1918         return 
1919     lines = "\n".join(document.body[i:j])
1920     if lines.find("\\Diagram") == -1:
1921       i = j
1922       continue
1923     add_to_preamble(document, ["% lyx2lyx feyn package insertion ", "\\usepackage{feyn}"])
1924     # only need to do it once!
1925     return
1926
1927
1928 def convert_bibtex_clearpage(document):
1929   " insert a clear(double)page bibliographystyle if bibtotoc option is used "
1930
1931   i = find_token(document.header, '\\papersides', 0)
1932   sides = 0
1933   if i == -1:
1934     document.warning("Malformed LyX document: Can't find papersides definition.")
1935     document.warning("Assuming single sided.")
1936     sides = 1
1937   else:
1938     val = get_value(document.header, "\\papersides", i)
1939     try:
1940       sides = int(val)
1941     except:
1942       pass
1943     if sides != 1 and sides != 2:
1944       document.warning("Invalid papersides value: " + val)
1945       document.warning("Assuming single sided.")
1946       sides = 1
1947
1948   j = 0
1949   while True:
1950     j = find_token(document.body, "\\begin_inset CommandInset bibtex", j)
1951     if j == -1:
1952       return
1953
1954     k = find_end_of_inset(document.body, j)
1955     if k == -1:
1956       document.warning("Can't find end of Bibliography inset at line " + str(j))
1957       j += 1
1958       continue
1959
1960     # only act if there is the option "bibtotoc"
1961     val = get_value(document.body, 'options', j, k)
1962     if not val:
1963       document.warning("Can't find options for bibliography inset at line " + str(j))
1964       j = k
1965       continue
1966     
1967     if val.find("bibtotoc") == -1:
1968       j = k
1969       continue
1970     
1971     # so we want to insert a new page right before the paragraph that
1972     # this bibliography thing is in. 
1973     lay = find_token_backwards(document.body, "\\begin_layout", j)
1974     if lay == -1:
1975       document.warning("Can't find layout containing bibliography inset at line " + str(j))
1976       j = k
1977       continue
1978
1979     if sides == 1:
1980       cmd = "clearpage"
1981     else:
1982       cmd = "cleardoublepage"
1983     subst = ['\\begin_layout Standard',
1984         '\\begin_inset Newpage ' + cmd,
1985         '\\end_inset', '', '',
1986         '\\end_layout', '']
1987     document.body[lay:lay] = subst
1988     j = k + len(subst)
1989
1990
1991 ##
1992 # Conversion hub
1993 #
1994
1995 supported_versions = ["2.0.0","2.0"]
1996 convert = [[346, []],
1997            [347, []],
1998            [348, []],
1999            [349, []],
2000            [350, []],
2001            [351, []],
2002            [352, [convert_splitindex]],
2003            [353, []],
2004            [354, []],
2005            [355, []],
2006            [356, []],
2007            [357, []],
2008            [358, []],
2009            [359, [convert_nomencl_width]],
2010            [360, []],
2011            [361, []],
2012            [362, []],
2013            [363, []],
2014            [364, []],
2015            [365, []],
2016            [366, []],
2017            [367, []],
2018            [368, []],
2019            [369, [convert_author_id]],
2020            [370, []],
2021            [371, []],
2022            [372, []],
2023            [373, [merge_gbrief]],
2024            [374, []],
2025            [375, []],
2026            [376, []],
2027            [377, []],
2028            [378, []],
2029            [379, [convert_math_output]],
2030            [380, []],
2031            [381, []],
2032            [382, []],
2033            [383, []],
2034            [384, []],
2035            [385, []],
2036            [386, []],
2037            [387, []],
2038            [388, []],
2039            [389, [convert_html_quotes]],
2040            [390, []],
2041            [391, []],
2042            [392, []],
2043            [393, [convert_optarg]],
2044            [394, [convert_use_makebox]],
2045            [395, []],
2046            [396, []],
2047            [397, [remove_Nameref]],
2048            [398, []],
2049            [399, [convert_mathdots]],
2050            [400, [convert_rule]],
2051            [401, []],
2052            [402, [convert_bibtex_clearpage]],
2053            [403, [convert_flexnames]],
2054            [404, [convert_prettyref]]
2055 ]
2056
2057 revert =  [[403, [revert_refstyle]],
2058            [402, [revert_flexnames]],
2059            [401, []],
2060            [400, [revert_diagram]],
2061            [399, [revert_rule]],
2062            [398, [revert_mathdots]],
2063            [397, [revert_mathrsfs]],
2064            [396, []],
2065            [395, [revert_nameref]],
2066            [394, [revert_DIN_C_pagesizes]],
2067            [393, [revert_makebox]],
2068            [392, [revert_argument]],
2069            [391, []],
2070            [390, [revert_align_decimal, revert_IEEEtran]],
2071            [389, [revert_output_sync]],
2072            [388, [revert_html_quotes]],
2073            [387, [revert_pagesizes]],
2074            [386, [revert_math_scale]],
2075            [385, [revert_lyx_version]],
2076            [384, [revert_shadedboxcolor]],
2077            [383, [revert_fontcolor]],
2078            [382, [revert_turkmen]],
2079            [381, [revert_notefontcolor]],
2080            [380, [revert_equalspacing_xymatrix]],
2081            [379, [revert_inset_preview]],
2082            [378, [revert_math_output]],
2083            [377, []],
2084            [376, [revert_multirow]],
2085            [375, [revert_includeall]],
2086            [374, [revert_includeonly]],
2087            [373, [revert_html_options]],
2088            [372, [revert_gbrief]],
2089            [371, [revert_fontenc]],
2090            [370, [revert_mhchem]],
2091            [369, [revert_suppress_date]],
2092            [368, [revert_author_id]],
2093            [367, [revert_hspace_glue_lengths]],
2094            [366, [revert_percent_vspace_lengths, revert_percent_hspace_lengths]],
2095            [365, [revert_percent_skip_lengths]],
2096            [364, [revert_paragraph_indentation]],
2097            [363, [revert_branch_filename]],
2098            [362, [revert_longtable_align]],
2099            [361, [revert_applemac]],
2100            [360, []],
2101            [359, [revert_nomencl_cwidth]],
2102            [358, [revert_nomencl_width]],
2103            [357, [revert_custom_processors]],
2104            [356, [revert_ulinelatex]],
2105            [355, []],
2106            [354, [revert_strikeout]],
2107            [353, [revert_printindexall]],
2108            [352, [revert_subindex]],
2109            [351, [revert_splitindex]],
2110            [350, [revert_backgroundcolor]],
2111            [349, [revert_outputformat]],
2112            [348, [revert_xetex]],
2113            [347, [revert_phantom, revert_hphantom, revert_vphantom]],
2114            [346, [revert_tabularvalign]],
2115            [345, [revert_swiss]]
2116           ]
2117
2118
2119 if __name__ == "__main__":
2120     pass