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