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