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