]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_2_1.py
Also add support for URW Garamond math fonts (via newtx)
[lyx.git] / lib / lyx2lyx / lyx_2_1.py
1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # -*- coding: utf-8 -*-
4 # Copyright (C) 2011 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19
20 """ Convert files to the file format generated by lyx 2.1"""
21
22 import re, string
23 import unicodedata
24 import sys, os
25
26 # Uncomment only what you need to import, please.
27
28 from parser_tools import del_token, find_token, find_token_backwards, find_end_of, \
29     find_end_of_inset, find_end_of_layout, find_re, get_option_value, get_containing_layout, \
30     get_value, get_quoted_value, set_option_value
31
32 #from parser_tools import find_token, find_end_of, find_tokens, \
33   #find_token_exact, find_end_of_inset, find_end_of_layout, \
34   #is_in_inset, del_token, check_token
35
36 from lyx2lyx_tools import add_to_preamble, put_cmd_in_ert, get_ert
37
38 #from lyx2lyx_tools import insert_to_preamble, \
39 #  lyx2latex, latex_length, revert_flex_inset, \
40 #  revert_font_attrs, hex2ratio, str2bool
41
42 ####################################################################
43 # Private helper functions
44
45 #def remove_option(lines, m, option):
46     #''' removes option from line m. returns whether we did anything '''
47     #l = lines[m].find(option)
48     #if l == -1:
49         #return False
50     #val = lines[m][l:].split('"')[1]
51     #lines[m] = lines[m][:l - 1] + lines[m][l+len(option + '="' + val + '"'):]
52     #return True
53
54
55 ###############################################################################
56 ###
57 ### Conversion and reversion routines
58 ###
59 ###############################################################################
60
61 def revert_visible_space(document):
62     "Revert InsetSpace visible into its ERT counterpart"
63     i = 0
64     while True:
65       i = find_token(document.body, "\\begin_inset space \\textvisiblespace{}", i)
66       if i == -1:
67         return
68       end = find_end_of_inset(document.body, i)
69       subst = put_cmd_in_ert("\\textvisiblespace{}")
70       document.body[i:end + 1] = subst
71
72
73 def convert_undertilde(document):
74     " Load undertilde automatically "
75     i = find_token(document.header, "\\use_mathdots" , 0)
76     if i == -1:
77         i = find_token(document.header, "\\use_mhchem" , 0)
78     if i == -1:
79         i = find_token(document.header, "\\use_esint" , 0)
80     if i == -1:
81         document.warning("Malformed LyX document: Can't find \\use_mathdots.")
82         return;
83     j = find_token(document.preamble, "\\usepackage{undertilde}", 0)
84     if j == -1:
85         document.header.insert(i + 1, "\\use_undertilde 0")
86     else:
87         document.header.insert(i + 1, "\\use_undertilde 2")
88         del document.preamble[j]
89
90
91 def revert_undertilde(document):
92     " Load undertilde if used in the document "
93     undertilde = find_token(document.header, "\\use_undertilde" , 0)
94     if undertilde == -1:
95       document.warning("No \\use_undertilde line. Assuming auto.")
96     else:
97       val = get_value(document.header, "\\use_undertilde", undertilde)
98       del document.header[undertilde]
99       try:
100         usetilde = int(val)
101       except:
102         document.warning("Invalid \\use_undertilde value: " + val + ". Assuming auto.")
103         # probably usedots has not been changed, but be safe.
104         usetilde = 1
105
106       if usetilde == 0:
107         # do not load case
108         return
109       if usetilde == 2:
110         # force load case
111         add_to_preamble(document, ["\\usepackage{undertilde}"])
112         return
113
114     # so we are in the auto case. we want to load undertilde if \utilde is used.
115     i = 0
116     while True:
117       i = find_token(document.body, '\\begin_inset Formula', i)
118       if i == -1:
119         return
120       j = find_end_of_inset(document.body, i)
121       if j == -1:
122         document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
123         i += 1
124         continue
125       code = "\n".join(document.body[i:j])
126       if code.find("\\utilde") != -1:
127         add_to_preamble(document, ["\\@ifundefined{utilde}{\\usepackage{undertilde}}"])
128         return
129       i = j
130
131
132 def revert_negative_space(document):
133     "Revert InsetSpace negmedspace and negthickspace into its TeX-code counterpart"
134     i = 0
135     j = 0
136     reverted = False
137     while True:
138       i = find_token(document.body, "\\begin_inset space \\negmedspace{}", i)
139       if i == -1:
140         j = find_token(document.body, "\\begin_inset space \\negthickspace{}", j)
141         if j == -1:
142           # load amsmath in the preamble if not already loaded if we are at the end of checking
143           if reverted == True:
144             i = find_token(document.header, "\\use_amsmath 2", 0)
145             if i == -1:
146               add_to_preamble(document, ["\\@ifundefined{negthickspace}{\\usepackage{amsmath}}"])
147           return
148       if i == -1:
149         return
150       end = find_end_of_inset(document.body, i)
151       subst = put_cmd_in_ert("\\negmedspace{}")
152       document.body[i:end + 1] = subst
153       j = find_token(document.body, "\\begin_inset space \\negthickspace{}", j)
154       if j == -1:
155         return
156       end = find_end_of_inset(document.body, j)
157       subst = put_cmd_in_ert("\\negthickspace{}")
158       document.body[j:end + 1] = subst
159       reverted = True
160
161
162 def revert_math_spaces(document):
163     "Revert formulas with protected custom space and protected hfills to TeX-code"
164     i = 0
165     while True:
166       i = find_token(document.body, "\\begin_inset Formula", i)
167       if i == -1:
168         return
169       j = document.body[i].find("\\hspace*")
170       if j != -1:
171         end = find_end_of_inset(document.body, i)
172         subst = put_cmd_in_ert(document.body[i][21:])
173         document.body[i:end + 1] = subst
174       i = i + 1
175
176
177 def convert_japanese_encodings(document):
178     " Rename the japanese encodings to names understood by platex "
179     jap_enc_dict = {
180         "EUC-JP-pLaTeX": "euc",
181         "JIS-pLaTeX":    "jis",
182         "SJIS-pLaTeX":   "sjis"
183     }
184     i = find_token(document.header, "\\inputencoding" , 0)
185     if i == -1:
186         return
187     val = get_value(document.header, "\\inputencoding", i)
188     if val in jap_enc_dict.keys():
189         document.header[i] = "\\inputencoding %s" % jap_enc_dict[val]
190
191
192 def revert_japanese_encodings(document):
193     " Revert the japanese encodings name changes "
194     jap_enc_dict = {
195         "euc":  "EUC-JP-pLaTeX",
196         "jis":  "JIS-pLaTeX",
197         "sjis": "SJIS-pLaTeX"
198     }
199     i = find_token(document.header, "\\inputencoding" , 0)
200     if i == -1:
201         return
202     val = get_value(document.header, "\\inputencoding", i)
203     if val in jap_enc_dict.keys():
204         document.header[i] = "\\inputencoding %s" % jap_enc_dict[val]
205
206
207 def revert_justification(document):
208     " Revert the \\justification buffer param"
209     if not del_token(document.header, '\\justification', 0):
210         document.warning("Malformed LyX document: Missing \\justification.")
211
212
213 def revert_australian(document):
214     "Set English language variants Australian and Newzealand to English" 
215
216     if document.language == "australian" or document.language == "newzealand": 
217         document.language = "english"
218         i = find_token(document.header, "\\language", 0) 
219         if i != -1: 
220             document.header[i] = "\\language english" 
221     j = 0 
222     while True: 
223         j = find_token(document.body, "\\lang australian", j) 
224         if j == -1:
225             j = find_token(document.body, "\\lang newzealand", 0)
226             if j == -1:
227                 return
228             else:
229                 document.body[j] = document.body[j].replace("\\lang newzealand", "\\lang english")
230         else:
231             document.body[j] = document.body[j].replace("\\lang australian", "\\lang english") 
232         j += 1
233
234
235 def convert_biblio_style(document):
236     "Add a sensible default for \\biblio_style based on the citation engine."
237     i = find_token(document.header, "\\cite_engine", 0)
238     if i != -1:
239         engine = get_value(document.header, "\\cite_engine", i).split("_")[0]
240         style = {"basic": "plain", "natbib": "plainnat", "jurabib": "jurabib"}
241         document.header.insert(i + 1, "\\biblio_style " + style[engine])
242
243
244 def revert_biblio_style(document):
245     "BibTeX insets with default option use the style defined by \\biblio_style."
246     i = find_token(document.header, "\\biblio_style" , 0)
247     if i == -1:
248         document.warning("No \\biblio_style line. Nothing to do.")
249         return
250
251     default_style = get_value(document.header, "\\biblio_style", i)
252     del document.header[i]
253
254     # We are looking for bibtex insets having the default option
255     i = 0
256     while True:
257         i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
258         if i == -1:
259             return
260         j = find_end_of_inset(document.body, i)
261         if j == -1:
262             document.warning("Malformed LyX document: Can't find end of bibtex inset at line " + str(i))
263             i += 1
264             return
265         k = find_token(document.body, "options", i, j)
266         if k != -1:
267             options = get_quoted_value(document.body, "options", k)
268             if "default" in options.split(","):
269                 document.body[k] = 'options "%s"' \
270                     % options.replace("default", default_style)
271         i = j
272
273
274 def handle_longtable_captions(document, forward):
275     begin_table = 0
276     while True:
277         begin_table = find_token(document.body, '<lyxtabular version=', begin_table)
278         if begin_table == -1:
279             break
280         end_table = find_end_of(document.body, begin_table, '<lyxtabular', '</lyxtabular>')
281         if end_table == -1:
282             document.warning("Malformed LyX document: Could not find end of table.")
283             begin_table += 1
284             continue
285         fline = find_token(document.body, "<features", begin_table, end_table)
286         if fline == -1:
287             document.warning("Can't find features for inset at line " + str(begin_table))
288             begin_table += 1
289             continue
290         p = document.body[fline].find("islongtable")
291         if p == -1:
292             # no longtable
293             begin_table += 1
294             continue
295         numrows = get_option_value(document.body[begin_table], "rows")
296         try:
297             numrows = int(numrows)
298         except:
299             document.warning(document.body[begin_table])
300             document.warning("Unable to determine rows!")
301             begin_table = end_table
302             continue
303         begin_row = begin_table
304         for row in range(numrows):
305             begin_row = find_token(document.body, '<row', begin_row, end_table)
306             if begin_row == -1:
307                 document.warning("Can't find row " + str(row + 1))
308                 break
309             end_row = find_end_of(document.body, begin_row, '<row', '</row>')
310             if end_row == -1:
311                 document.warning("Can't find end of row " + str(row + 1))
312                 break
313             if forward:
314                 if (get_option_value(document.body[begin_row], 'caption') == 'true' and
315                     get_option_value(document.body[begin_row], 'endfirsthead') != 'true' and
316                     get_option_value(document.body[begin_row], 'endhead') != 'true' and
317                     get_option_value(document.body[begin_row], 'endfoot') != 'true' and
318                     get_option_value(document.body[begin_row], 'endlastfoot') != 'true'):
319                     document.body[begin_row] = set_option_value(document.body[begin_row], 'caption', 'true", endfirsthead="true')
320             elif get_option_value(document.body[begin_row], 'caption') == 'true':
321                 if get_option_value(document.body[begin_row], 'endfirsthead') == 'true':
322                     document.body[begin_row] = set_option_value(document.body[begin_row], 'endfirsthead', 'false')
323                 if get_option_value(document.body[begin_row], 'endhead') == 'true':
324                     document.body[begin_row] = set_option_value(document.body[begin_row], 'endhead', 'false')
325                 if get_option_value(document.body[begin_row], 'endfoot') == 'true':
326                     document.body[begin_row] = set_option_value(document.body[begin_row], 'endfoot', 'false')
327                 if get_option_value(document.body[begin_row], 'endlastfoot') == 'true':
328                     document.body[begin_row] = set_option_value(document.body[begin_row], 'endlastfoot', 'false')
329             begin_row = end_row
330         # since there could be a tabular inside this one, we 
331         # cannot jump to end.
332         begin_table += 1
333
334
335 def convert_longtable_captions(document):
336     "Add a firsthead flag to caption rows"
337     handle_longtable_captions(document, True)
338
339
340 def revert_longtable_captions(document):
341     "remove head/foot flag from caption rows"
342     handle_longtable_captions(document, False)
343
344
345 def convert_use_packages(document):
346     "use_xxx yyy => use_package xxx yyy"
347     packages = ["amsmath", "esint", "mathdots", "mhchem", "undertilde"]
348     for p in packages:
349         i = find_token(document.header, "\\use_%s" % p, 0)
350         if i != -1:
351             value = get_value(document.header, "\\use_%s" % p, i)
352             document.header[i] = "\\use_package %s %s" % (p, value)
353
354
355 def revert_use_packages(document):
356     "use_package xxx yyy => use_xxx yyy"
357     packages = ["amsmath", "esint", "mathdots", "mhchem", "undertilde"]
358     # the order is arbitrary for the use_package version, and not all packages need to be given.
359     # Ensure a complete list and correct order (important for older LyX versions and especially lyx2lyx)
360     j = 0
361     for p in packages:
362         regexp = re.compile(r'(\\use_package\s+%s)' % p)
363         i = find_re(document.header, regexp, j)
364         if i != -1:
365             value = get_value(document.header, "\\use_package %s" % p, i).split()[1]
366             del document.header[i]
367             j = i
368             document.header.insert(j, "\\use_%s %s"  % (p, value))
369         j = j + 1
370
371
372 def convert_use_mathtools(document):
373     "insert use_package mathtools"
374     i = find_token(document.header, "\\use_package", 0)
375     if i == -1:
376         document.warning("Malformed LyX document: Can't find \\use_package.")
377         return;
378     j = find_token(document.preamble, "\\usepackage{mathtools}", 0)
379     if j == -1:
380         document.header.insert(i + 1, "\\use_package mathtools 0")
381     else:
382         document.header.insert(i + 1, "\\use_package mathtools 2")
383         del document.preamble[j]
384
385
386 def revert_use_mathtools(document):
387     "remove use_package mathtools"
388     regexp = re.compile(r'(\\use_package\s+mathtools)')
389     i = find_re(document.header, regexp, 0)
390     value = "1" # default is auto
391     if i != -1:
392         value = get_value(document.header, "\\use_package" , i).split()[1]
393         del document.header[i]
394     if value == "2": # on
395         add_to_preamble(document, ["\\usepackage{mathtools}"])
396     elif value == "1": # auto
397         commands = ["mathclap", "mathllap", "mathrlap", \
398                     "lgathered", "rgathered", "vcentcolon", "dblcolon", \
399                     "coloneqq", "Coloneqq", "coloneq", "Coloneq", "eqqcolon", \
400                     "Eqqcolon", "eqcolon", "Eqcolon", "colonapprox", \
401                     "Colonapprox", "colonsim", "Colonsim"]
402         i = 0
403         while True:
404             i = find_token(document.body, '\\begin_inset Formula', i)
405             if i == -1:
406                 return
407             j = find_end_of_inset(document.body, i)
408             if j == -1:
409                 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
410                 i += 1
411                 continue
412             code = "\n".join(document.body[i:j])
413             for c in commands:
414                 if code.find("\\%s" % c) != -1:
415                     add_to_preamble(document, ["\\usepackage{mathtools}"])
416                     return
417             i = j
418
419
420 def convert_cite_engine_type(document):
421     "Determine the \\cite_engine_type from the citation engine."
422     i = find_token(document.header, "\\cite_engine", 0)
423     if i == -1:
424         return
425     engine = get_value(document.header, "\\cite_engine", i)
426     if "_" in engine:
427         engine, type = engine.split("_")
428     else:
429         type = {"basic": "numerical", "jurabib": "authoryear"}[engine]
430     document.header[i] = "\\cite_engine " + engine
431     document.header.insert(i + 1, "\\cite_engine_type " + type)
432
433
434 def revert_cite_engine_type(document):
435     "Natbib had the type appended with an underscore."
436     engine_type = "numerical"
437     i = find_token(document.header, "\\cite_engine_type" , 0)
438     if i == -1:
439         document.warning("No \\cite_engine_type line. Assuming numerical.")
440     else:
441         engine_type = get_value(document.header, "\\cite_engine_type", i)
442         del document.header[i]
443
444     # We are looking for the natbib citation engine
445     i = find_token(document.header, "\\cite_engine natbib", 0)
446     if i == -1:
447         return
448     document.header[i] = "\\cite_engine natbib_" + engine_type
449
450
451 def revert_cancel(document):
452     "add cancel to the preamble if necessary"
453     commands = ["cancelto", "cancel", "bcancel", "xcancel"]
454     i = 0
455     while True:
456         i = find_token(document.body, '\\begin_inset Formula', i)
457         if i == -1:
458             return
459         j = find_end_of_inset(document.body, i)
460         if j == -1:
461             document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
462             i += 1
463             continue
464         code = "\n".join(document.body[i:j])
465         for c in commands:
466             if code.find("\\%s" % c) != -1:
467                 add_to_preamble(document, ["\\usepackage{cancel}"])
468                 return
469         i = j
470
471
472 def revert_verbatim(document):
473     " Revert verbatim einvironments completely to TeX-code. "
474     i = 0
475     consecutive = False
476     subst_end = ['\end_layout', '', '\\begin_layout Plain Layout',
477                  '\end_layout', '',
478                  '\\begin_layout Plain Layout', '', '',
479                  '\\backslash', '',
480                  'end{verbatim}',
481                  '\\end_layout', '', '\\end_inset',
482                  '', '', '\\end_layout']
483     subst_begin = ['\\begin_layout Standard', '\\noindent',
484                    '\\begin_inset ERT', 'status collapsed', '',
485                    '\\begin_layout Plain Layout', '', '', '\\backslash',
486                    'begin{verbatim}',
487                    '\\end_layout', '', '\\begin_layout Plain Layout', '']
488     while 1:
489         i = find_token(document.body, "\\begin_layout Verbatim", i)
490         if i == -1:
491             return
492         j = find_end_of_layout(document.body, i)
493         if j == -1:
494             document.warning("Malformed lyx document: Can't find end of Verbatim layout")
495             i += 1
496             continue
497         # delete all line breaks insets (there are no other insets)
498         l = i
499         while 1:
500             n = find_token(document.body, "\\begin_inset Newline newline", l)
501             if n == -1:
502                 n = find_token(document.body, "\\begin_inset Newline linebreak", l)
503                 if n == -1:
504                     break
505             m = find_end_of_inset(document.body, n)
506             del(document.body[m:m+1])
507             document.body[n:n+1] = ['\end_layout', '', '\\begin_layout Plain Layout']
508             l += 1
509             j += 1
510         # consecutive verbatim environments need to be connected
511         k = find_token(document.body, "\\begin_layout Verbatim", j)
512         if k == j + 2 and consecutive == False:
513             consecutive = True
514             document.body[j:j+1] = ['\end_layout', '', '\\begin_layout Plain Layout']
515             document.body[i:i+1] = subst_begin
516             continue
517         if k == j + 2 and consecutive == True:
518             document.body[j:j+1] = ['\end_layout', '', '\\begin_layout Plain Layout']
519             del(document.body[i:i+1])
520             continue
521         if k != j + 2 and consecutive == True:
522             document.body[j:j+1] = subst_end
523             # the next paragraph must not be indented
524             document.body[j+19:j+19] = ['\\noindent']
525             del(document.body[i:i+1])
526             consecutive = False
527             continue
528         else:
529             document.body[j:j+1] = subst_end
530             # the next paragraph must not be indented
531             document.body[j+19:j+19] = ['\\noindent']
532             document.body[i:i+1] = subst_begin
533
534
535 def revert_tipa(document):
536     " Revert native TIPA insets to mathed or ERT. "
537     i = 0
538     while 1:
539         i = find_token(document.body, "\\begin_inset IPA", i)
540         if i == -1:
541             return
542         j = find_end_of_inset(document.body, i)
543         if j == -1:
544             document.warning("Malformed lyx document: Can't find end of IPA inset")
545             i += 1
546             continue
547         Multipar = False
548         n = find_token(document.body, "\\begin_layout", i, j)
549         if n == -1:
550             document.warning("Malformed lyx document: IPA inset has no embedded layout")
551             i += 1
552             continue
553         m = find_end_of_layout(document.body, n)
554         if m == -1:
555             document.warning("Malformed lyx document: Can't find end of embedded layout")
556             i += 1
557             continue
558         content = document.body[n+1:m]
559         p = find_token(document.body, "\\begin_layout", m, j)
560         if p != -1 or len(content) > 1:
561             Multipar = True
562             content = document.body[i+1:j]
563         if Multipar:
564             # IPA insets with multiple pars need to be wrapped by \begin{IPA}...\end{IPA}
565             document.body[i:j+1] = ['\\end_layout', '', '\\begin_layout Standard'] + put_cmd_in_ert("\\begin{IPA}") + ['\\end_layout'] + content + ['\\begin_layout Standard'] + put_cmd_in_ert("\\end{IPA}")
566             add_to_preamble(document, ["\\usepackage{tipa,tipx}"])
567         else:
568             # single-par IPA insets can be reverted to mathed
569             document.body[i:j+1] = ["\\begin_inset Formula $\\text{\\textipa{" + content[0] + "}}$", "\\end_inset"]
570         i = j
571
572
573 def revert_cell_rotation(document):
574   "Revert cell rotations to TeX-code"
575
576   load_rotating = False
577   i = 0
578   try:
579     while True:
580       # first, let's find out if we need to do anything
581       i = find_token(document.body, '<cell ', i)
582       if i == -1:
583         return
584       j = document.body[i].find('rotate="')
585       if j != -1:
586         k = document.body[i].find('"', j + 8)
587         value = document.body[i][j + 8 : k]
588         if value == "0":
589           rgx = re.compile(r' rotate="[^"]+?"')
590           # remove rotate option
591           document.body[i] = rgx.sub('', document.body[i])
592         elif value == "90":
593           rgx = re.compile(r' rotate="[^"]+?"')
594           document.body[i] = rgx.sub('rotate="true"', document.body[i])
595         else:
596           rgx = re.compile(r' rotate="[^"]+?"')
597           load_rotating = True
598           # remove rotate option
599           document.body[i] = rgx.sub('', document.body[i])
600           # write ERT
601           document.body[i + 5 : i + 5] = \
602             put_cmd_in_ert("\\end{turn}")
603           document.body[i + 4 : i + 4] = \
604             put_cmd_in_ert("\\begin{turn}{" + value + "}")
605         
606       i += 1
607         
608   finally:
609     if load_rotating:
610       add_to_preamble(document, ["\\@ifundefined{turnbox}{\usepackage{rotating}}{}"])
611
612
613 def convert_cell_rotation(document):
614     'Convert cell rotation statements from "true" to "90"'
615
616     i = 0
617     while True:
618       # first, let's find out if we need to do anything
619       i = find_token(document.body, '<cell ', i)
620       if i == -1:
621         return
622       j = document.body[i].find('rotate="true"')
623       if j != -1:
624         rgx = re.compile(r'rotate="[^"]+?"')
625         # convert "true" to "90"
626         document.body[i] = rgx.sub('rotate="90"', document.body[i])
627         
628       i += 1
629
630
631 def revert_table_rotation(document):
632   "Revert table rotations to TeX-code"
633
634   load_rotating = False
635   i = 0
636   try:
637     while True:
638       # first, let's find out if we need to do anything
639       i = find_token(document.body, '<features ', i)
640       if i == -1:
641         return
642       j = document.body[i].find('rotate="')
643       if j != -1:
644         end_table = find_token(document.body, '</lyxtabular>', j)
645         k = document.body[i].find('"', j + 8)
646         value = document.body[i][j + 8 : k]
647         if value == "0":
648           rgx = re.compile(r' rotate="[^"]+?"')
649           # remove rotate option
650           document.body[i] = rgx.sub('', document.body[i])
651         elif value == "90":
652           rgx = re.compile(r'rotate="[^"]+?"')
653           document.body[i] = rgx.sub('rotate="true"', document.body[i])
654         else:
655           rgx = re.compile(r' rotate="[^"]+?"')
656           load_rotating = True
657           # remove rotate option
658           document.body[i] = rgx.sub('', document.body[i])
659           # write ERT
660           document.body[end_table + 3 : end_table + 3] = \
661             put_cmd_in_ert("\\end{turn}")
662           document.body[i - 2 : i - 2] = \
663             put_cmd_in_ert("\\begin{turn}{" + value + "}")
664         
665       i += 1
666         
667   finally:
668     if load_rotating:
669       add_to_preamble(document, ["\\@ifundefined{turnbox}{\usepackage{rotating}}{}"])
670
671
672 def convert_table_rotation(document):
673     'Convert table rotation statements from "true" to "90"'
674
675     i = 0
676     while True:
677       # first, let's find out if we need to do anything
678       i = find_token(document.body, '<features ', i)
679       if i == -1:
680         return
681       j = document.body[i].find('rotate="true"')
682       if j != -1:
683         rgx = re.compile(r'rotate="[^"]+?"')
684         # convert "true" to "90"
685         document.body[i] = rgx.sub('rotate="90"', document.body[i])
686         
687       i += 1
688
689
690 def convert_listoflistings(document):
691     'Convert ERT \lstlistoflistings to TOC lstlistoflistings inset'
692     # We can support roundtrip because the command is so simple
693     i = 0
694     while True:
695         i = find_token(document.body, "\\begin_inset ERT", i)
696         if i == -1:
697             return
698         j = find_end_of_inset(document.body, i)
699         if j == -1:
700             document.warning("Malformed lyx document: Can't find end of ERT inset")
701             i += 1
702             continue
703         ert = get_ert(document.body, i)
704         if ert == "\\lstlistoflistings{}":
705             document.body[i:j] = ["\\begin_inset CommandInset toc", "LatexCommand lstlistoflistings", ""]
706             i = i + 4
707         else:
708             i = j + 1
709
710
711 def revert_listoflistings(document):
712     'Convert TOC lstlistoflistings inset to ERT lstlistoflistings'
713     i = 0
714     while True:
715         i = find_token(document.body, "\\begin_inset CommandInset toc", i)
716         if i == -1:
717             return
718         if document.body[i+1] == "LatexCommand lstlistoflistings":
719             j = find_end_of_inset(document.body, i)
720             if j == -1:
721                 document.warning("Malformed lyx document: Can't find end of TOC inset")
722                 i += 1
723                 continue
724             subst = put_cmd_in_ert("\\lstlistoflistings{}")
725             document.body[i:j+1] = subst
726             add_to_preamble(document, ["\\usepackage{listings}"])
727         i = i + 1
728
729
730 def convert_use_amssymb(document):
731     "insert use_package amssymb"
732     regexp = re.compile(r'(\\use_package\s+amsmath)')
733     i = find_re(document.header, regexp, 0)
734     if i == -1:
735         document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
736         return;
737     value = get_value(document.header, "\\use_package" , i).split()[1]
738     useamsmath = 0
739     try:
740         useamsmath = int(value)
741     except:
742         document.warning("Invalid \\use_package amsmath: " + value + ". Assuming auto.")
743         useamsmath = 1
744     j = find_token(document.preamble, "\\usepackage{amssymb}", 0)
745     if j == -1:
746         document.header.insert(i + 1, "\\use_package amssymb %d" % useamsmath)
747     else:
748         document.header.insert(i + 1, "\\use_package amssymb 2")
749         del document.preamble[j]
750
751
752 def revert_use_amssymb(document):
753     "remove use_package amssymb"
754     regexp1 = re.compile(r'(\\use_package\s+amsmath)')
755     regexp2 = re.compile(r'(\\use_package\s+amssymb)')
756     i = find_re(document.header, regexp1, 0)
757     j = find_re(document.header, regexp2, 0)
758     value1 = "1" # default is auto
759     value2 = "1" # default is auto
760     if i != -1:
761         value1 = get_value(document.header, "\\use_package" , i).split()[1]
762     if j != -1:
763         value2 = get_value(document.header, "\\use_package" , j).split()[1]
764         del document.header[j]
765     if value1 != value2 and value2 == "2": # on
766         add_to_preamble(document, ["\\usepackage{amssymb}"])
767
768
769 def revert_ancientgreek(document):
770     "Set the document language for ancientgreek to greek" 
771
772     if document.language == "ancientgreek": 
773         document.language = "greek"
774         i = find_token(document.header, "\\language", 0) 
775         if i != -1: 
776             document.header[i] = "\\language greek" 
777     j = 0 
778     while True: 
779         j = find_token(document.body, "\\lang ancientgreek", j) 
780         if j == -1:
781             return
782         else:
783             document.body[j] = document.body[j].replace("\\lang ancientgreek", "\\lang greek") 
784         j += 1
785
786
787 def revert_languages(document):
788     "Set the document language for new supported languages to English" 
789
790     languages = [
791                  "coptic", "divehi", "hindi", "kurmanji", "lao", "marathi", "occitan", "sanskrit",
792                  "syriac", "tamil", "telugu", "urdu"
793                 ]
794     for n in range(len(languages)):
795         if document.language == languages[n]:
796             document.language = "english"
797             i = find_token(document.header, "\\language", 0) 
798             if i != -1: 
799                 document.header[i] = "\\language english" 
800         j = 0
801         while j < len(document.body): 
802             j = find_token(document.body, "\\lang " + languages[n], j)
803             if j != -1:
804                 document.body[j] = document.body[j].replace("\\lang " + languages[n], "\\lang english")
805                 j += 1
806             else:
807                 j = len(document.body)
808
809
810 def convert_armenian(document):
811     "Use polyglossia and thus non-TeX fonts for Armenian" 
812
813     if document.language == "armenian": 
814         i = find_token(document.header, "\\use_non_tex_fonts", 0) 
815         if i != -1: 
816             document.header[i] = "\\use_non_tex_fonts true" 
817
818
819 def revert_armenian(document):
820     "Use ArmTeX and thus TeX fonts for Armenian" 
821
822     if document.language == "armenian": 
823         i = find_token(document.header, "\\use_non_tex_fonts", 0) 
824         if i != -1: 
825             document.header[i] = "\\use_non_tex_fonts false" 
826
827
828 def revert_libertine(document):
829     " Revert native libertine font definition to LaTeX " 
830
831     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
832         i = find_token(document.header, "\\font_roman libertine", 0)
833         if i != -1:
834             osf = False
835             j = find_token(document.header, "\\font_osf true", 0)
836             if j != -1:
837                 osf = True
838             preamble = "\\usepackage"
839             if osf:
840                 document.header[j] = "\\font_osf false"
841             else:
842                 preamble += "[lining]"
843             preamble += "{libertine-type1}"
844             add_to_preamble(document, [preamble])
845             document.header[i] = "\\font_roman default"
846
847
848 def revert_txtt(document):
849     " Revert native txtt font definition to LaTeX " 
850
851     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
852         i = find_token(document.header, "\\font_typewriter txtt", 0)
853         if i != -1:
854             preamble = "\\renewcommand{\\ttdefault}{txtt}"
855             add_to_preamble(document, [preamble])
856             document.header[i] = "\\font_typewriter default"
857
858
859 def revert_mathdesign(document):
860     " Revert native mathdesign font definition to LaTeX " 
861
862     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
863         mathdesign_dict = {
864         "mdbch":  "charter",
865         "mdput":  "utopia",
866         "mdugm":  "garamond"
867         }
868         i = find_token(document.header, "\\font_roman", 0)
869         if i == -1:
870             return
871         val = get_value(document.header, "\\font_roman", i)
872         if val in mathdesign_dict.keys():
873             preamble = "\\usepackage[%s" % mathdesign_dict[val]
874             expert = False
875             j = find_token(document.header, "\\font_osf true", 0)
876             if j != -1:
877                 expert = True
878                 document.header[j] = "\\font_osf false"
879             l = find_token(document.header, "\\font_sc true", 0)
880             if l != -1:
881                 expert = True
882                 document.header[l] = "\\font_sc false"
883             if expert:
884                 preamble += ",expert"
885             preamble += "]{mathdesign}"
886             add_to_preamble(document, [preamble])
887             document.header[i] = "\\font_roman default"
888
889
890 def revert_texgyre(document):
891     " Revert native TeXGyre font definition to LaTeX " 
892
893     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
894         texgyre_fonts = ["tgadventor", "tgbonum", "tgchorus", "tgcursor", \
895                          "tgheros", "tgpagella", "tgschola", "tgtermes"]
896         i = find_token(document.header, "\\font_roman", 0)
897         if i != -1:
898             val = get_value(document.header, "\\font_roman", i)
899             if val in texgyre_fonts:
900                 preamble = "\\usepackage{%s}" % val
901                 add_to_preamble(document, [preamble])
902                 document.header[i] = "\\font_roman default"
903         i = find_token(document.header, "\\font_sans", 0)
904         if i != -1:
905             val = get_value(document.header, "\\font_sans", i)
906             if val in texgyre_fonts:
907                 preamble = "\\usepackage{%s}" % val
908                 add_to_preamble(document, [preamble])
909                 document.header[i] = "\\font_sans default"
910         i = find_token(document.header, "\\font_typewriter", 0)
911         if i != -1:
912             val = get_value(document.header, "\\font_typewriter", i)
913             if val in texgyre_fonts:
914                 preamble = "\\usepackage{%s}" % val
915                 add_to_preamble(document, [preamble])
916                 document.header[i] = "\\font_typewriter default"
917
918
919 def revert_ipadeco(document):
920     " Revert IPA decorations to ERT "
921     i = 0
922     while True:
923       i = find_token(document.body, "\\begin_inset IPADeco", i)
924       if i == -1:
925           return
926       end = find_end_of_inset(document.body, i)
927       if end == -1:
928           document.warning("Can't find end of inset at line " + str(i))
929           i += 1
930           continue
931       line = document.body[i]
932       rx = re.compile(r'\\begin_inset IPADeco (.*)$')
933       m = rx.match(line)
934       decotype = m.group(1)
935       if decotype != "toptiebar" and decotype != "bottomtiebar":
936           document.warning("Invalid IPADeco type: " + decotype)
937           i = end
938           continue
939       blay = find_token(document.body, "\\begin_layout Plain Layout", i, end)
940       if blay == -1:
941           document.warning("Can't find layout for inset at line " + str(i))
942           i = end
943           continue
944       bend = find_end_of_layout(document.body, blay)
945       if bend == -1:
946           document.warning("Malformed LyX document: Could not find end of IPADeco inset's layout.")
947           i = end
948           continue
949       substi = ["\\begin_inset ERT", "status collapsed", "",
950                 "\\begin_layout Plain Layout", "", "", "\\backslash", 
951                 decotype + "{", "\\end_layout", "", "\\end_inset"]
952       substj = ["\\size default", "", "\\begin_inset ERT", "status collapsed", "",
953                 "\\begin_layout Plain Layout", "", "}", "\\end_layout", "", "\\end_inset"]
954       # do the later one first so as not to mess up the numbering
955       document.body[bend:end + 1] = substj
956       document.body[i:blay + 1] = substi
957       i = end + len(substi) + len(substj) - (end - bend) - (blay - i) - 2
958       add_to_preamble(document, "\\usepackage{tipa}")
959
960
961 def revert_ipachar(document):
962     ' Revert \\IPAChar to ERT '
963     i = 0
964     found = False
965     while i < len(document.body):
966         m = re.match(r'(.*)\\IPAChar \\(\w+\{\w+\})(.*)', document.body[i])
967         if m:
968             found = True
969             before = m.group(1)
970             ipachar = m.group(2)
971             after = m.group(3)
972             subst = [before,
973                      '\\begin_inset ERT',
974                      'status collapsed', '',
975                      '\\begin_layout Standard',
976                      '', '', '\\backslash',
977                      ipachar,
978                      '\\end_layout', '',
979                      '\\end_inset', '',
980                      after]
981             document.body[i: i+1] = subst
982             i = i + len(subst)
983         else:
984             i = i + 1
985     if found:
986         add_to_preamble(document, "\\usepackage{tone}")
987
988
989 def revert_minionpro(document):
990     " Revert native MinionPro font definition to LaTeX " 
991
992     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
993         i = find_token(document.header, "\\font_roman minionpro", 0)
994         if i != -1:
995             osf = False
996             j = find_token(document.header, "\\font_osf true", 0)
997             if j != -1:
998                 osf = True
999             preamble = "\\usepackage"
1000             if osf:
1001                 document.header[j] = "\\font_osf false"
1002             else:
1003                 preamble += "[lf]"
1004             preamble += "{MinionPro}"
1005             add_to_preamble(document, [preamble])
1006             document.header[i] = "\\font_roman default"
1007
1008
1009 def revert_mathfonts(document):
1010     " Revert native math font definitions to LaTeX " 
1011
1012     i = find_token(document.header, "\\font_math", 0)
1013     if i == -1:
1014        return
1015     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1016         val = get_value(document.header, "\\font_math", i)
1017         if val == "eulervm":
1018             add_to_preamble(document, "\\usepackage{eulervm}")
1019         elif val == "default":
1020             mathfont_dict = {
1021             "lmodern":  "\\renewcommand{\\rmdefault}{lmr}",
1022             "minionpro":  "\\usepackage[onlytext,lf]{MinionPro}",
1023             "minionpro-osf":  "\\usepackage[onlytext]{MinionPro}",
1024             "palatino":  "\\renewcommand{\\rmdefault}{ppl}",
1025             "palatino-osf":  "\\renewcommand{\\rmdefault}{pplj}",
1026             "times":  "\\renewcommand{\\rmdefault}{ptm}",
1027             "utopia":  "\\renewcommand{\\rmdefault}{futs}",
1028             "utopia-osf":  "\\renewcommand{\\rmdefault}{futj}",
1029             }
1030             j = find_token(document.header, "\\font_roman", 0)
1031             if j != -1:
1032                 rm = get_value(document.header, "\\font_roman", j)
1033                 k = find_token(document.header, "\\font_osf true", 0)
1034                 if k != -1:
1035                     rm += "-osf"
1036                 if rm in mathfont_dict.keys():
1037                     add_to_preamble(document, mathfont_dict[rm])
1038                     document.header[j] = "\\font_roman default"
1039                     if k != -1:
1040                         document.header[k] = "\\font_osf false"
1041     del document.header[i]
1042
1043
1044 def revert_mdnomath(document):
1045     " Revert mathdesign and fourier without math " 
1046
1047     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1048         mathdesign_dict = {
1049         "md-charter": "mdbch",
1050         "md-utopia": "mdput",
1051         "md-garamond": "mdugm"
1052         }
1053         i = find_token(document.header, "\\font_roman", 0)
1054         if i == -1:
1055             return
1056         val = get_value(document.header, "\\font_roman", i)
1057         if val in mathdesign_dict.keys():
1058             j = find_token(document.header, "\\font_math", 0)
1059             if j == -1:
1060                 document.header[i] = "\\font_roman %s" % mathdesign_dict[val]
1061             mval = get_value(document.header, "\\font_math", j)
1062             if mval == "default":
1063                 document.header[i] = "\\font_roman default"
1064                 add_to_preamble(document, "\\renewcommand{\\rmdefault}{%s}" % mathdesign_dict[val])
1065             else:
1066                 document.header[i] = "\\font_roman %s" % mathdesign_dict[val]
1067
1068
1069 def convert_mdnomath(document):
1070     " Change mathdesign font name " 
1071
1072     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1073         mathdesign_dict = {
1074         "mdbch":  "md-charter",
1075         "mdput":  "md-utopia",
1076         "mdugm":  "md-garamond"
1077         }
1078         i = find_token(document.header, "\\font_roman", 0)
1079         if i == -1:
1080             return
1081         val = get_value(document.header, "\\font_roman", i)
1082         if val in mathdesign_dict.keys():
1083              document.header[i] = "\\font_roman %s" % mathdesign_dict[val]
1084
1085
1086 def revert_newtxmath(document):
1087     " Revert native newtxmath definitions to LaTeX " 
1088
1089     i = find_token(document.header, "\\font_math", 0)
1090     if i == -1:
1091        return
1092     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1093         val = get_value(document.header, "\\font_math", i)
1094         mathfont_dict = {
1095         "libertine-ntxm":  "\\usepackage[libertine]{newtxmath}",
1096         "minion-ntxm":  "\\usepackage[minion]{newtxmath}",
1097         "newtxmath":  "\\usepackage{newtxmath}",
1098         }
1099         if val in mathfont_dict.keys():
1100             add_to_preamble(document, mathfont_dict[val])
1101             document.header[i] = "\\font_math auto"
1102
1103
1104 def revert_biolinum(document):
1105     " Revert native biolinum font definition to LaTeX " 
1106
1107     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1108         i = find_token(document.header, "\\font_sans biolinum", 0)
1109         if i != -1:
1110             osf = False
1111             j = find_token(document.header, "\\font_osf true", 0)
1112             if j != -1:
1113                 osf = True
1114             preamble = "\\usepackage"
1115             if not osf:
1116                 preamble += "[lf]"
1117             preamble += "{biolinum-type1}"
1118             add_to_preamble(document, [preamble])
1119             document.header[i] = "\\font_sans default"
1120
1121
1122 def revert_uop(document):
1123     " Revert native URW Classico (Optima) font definition to LaTeX "
1124
1125     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1126         i = find_token(document.header, "\\font_sans uop", 0)
1127         if i != -1:
1128                 preamble = "\\renewcommand{\\sfdefault}{uop}"
1129                 add_to_preamble(document, [preamble])
1130                 document.header[i] = "\\font_sans default"
1131
1132
1133 def convert_latexargs(document):
1134     " Convert InsetArgument to new syntax "
1135
1136     if find_token(document.body, "\\begin_inset Argument", 0) == -1:
1137         # nothing to do.
1138         return
1139
1140     # A list of layouts (document classes) with only optional or no arguments.
1141     # These can be safely converted to the new syntax
1142     # (I took the liberty to add some of my personal layouts/modules here; JSP)
1143     safe_layouts = ["aa", "aapaper", "aastex", "achemso", "acmsiggraph", "AEA",
1144                     "agu-dtd", "agums", "agutex", "amsart", "amsbook", "apa",
1145                     "arab-article", "armenian-article", "article-beamer", "article",
1146                     "beamer", "book", "broadway", "chess", "cl2emult", "ctex-article",
1147                     "ctex-book", "ctex-report", "dinbrief", "docbook-book", "docbook-chapter",
1148                     "docbook", "docbook-section", "doublecol-new", "dtk", "ectaart", "egs",
1149                     "elsarticle", "elsart", "entcs", "europecv", "extarticle", "extbook",
1150                     "extletter", "extreport", "foils", "frletter", "g-brief2", "g-brief",
1151                     "heb-article", "heb-letter", "hollywood", "IEEEtran", "ijmpc", "ijmpd",
1152                     "iopart", "isprs", "jarticle", "jasatex", "jbook", "jgrga", "jreport",
1153                     "jsarticle", "jsbeamer", "jsbook", "jss", "kluwer", "latex8", "letter", "lettre",
1154                     "literate-article", "literate-book", "literate-report", "llncs", "ltugboat",
1155                     "memoir", "moderncv", "mwart", "mwbk", "mwrep", "paper", "powerdot",
1156                     "recipebook", "report", "revtex4", "revtex", "scrartcl", "scrarticle-beamer",
1157                     "scrbook", "scrlettr", "scrlttr2", "scrreprt", "seminar", "siamltex",
1158                     "sigplanconf", "simplecv", "singlecol", "singlecol-new", "slides", "spie",
1159                     "svglobal3", "svglobal", "svjog", "svmono", "svmult", "svprobth", "tarticle",
1160                     "tbook", "treport", "tufte-book", "tufte-handout"]
1161     # A list of "safe" modules, same as above
1162     safe_modules = ["biblatex", "beameraddons", "beamersession", "braille", "customHeadersFooters",
1163                     "endnotes", "enumitem", "eqs-within-sections", "figs-within-sections", "fix-cm",
1164                     "fixltx2e", "foottoend", "hanging", "jscharstyles", "knitr", "lilypond",
1165                     "linguistics", "linguisticx", "logicalmkup", "minimalistic", "nomindex", "noweb",
1166                     "pdfcomment", "sweave", "tabs-within-sections", "theorems-ams-bytype",
1167                     "theorems-ams-extended-bytype", "theorems-ams-extended", "theorems-ams", "theorems-bytype",
1168                     "theorems-chap-bytype", "theorems-chap", "theorems-named", "theorems-sec-bytype",
1169                     "theorems-sec", "theorems-starred", "theorems-std", "todonotes"]
1170     # Modules we need to take care of
1171     caveat_modules = ["initials"]
1172     # information about the relevant styles in caveat_modules (number of opt and req args)
1173     # use this if we get more caveat_modules. For now, use hard coding (see below).
1174     # initials = [{'Layout' : 'Initial', 'opt' : 1, 'req' : 1}]
1175
1176     # Is this a known safe layout?
1177     safe_layout = document.textclass in safe_layouts
1178     if not safe_layout:
1179         document.warning("Lyx2lyx knows nothing about textclass '%s'. "
1180                          "Please check if short title insets have been converted correctly."
1181                          % document.textclass)
1182     # Do we use unsafe or unknown modules
1183     mods = document.get_module_list()
1184     unknown_modules = False
1185     used_caveat_modules = list()
1186     for mod in mods:
1187         if mod in safe_modules:
1188             continue
1189         if mod in caveat_modules:
1190             used_caveat_modules.append(mod)
1191             continue
1192         unknown_modules = True
1193         document.warning("Lyx2lyx knows nothing about module '%s'. "
1194                          "Please check if short title insets have been converted correctly."
1195                          % mod)
1196
1197     i = 0
1198     while True:
1199         i = find_token(document.body, "\\begin_inset Argument", i)
1200         if i == -1:
1201             return
1202
1203         if not safe_layout or unknown_modules:
1204             # We cannot do more here since we have no access to this layout.
1205             # InsetArgument itself will do the real work
1206             # (see InsetArgument::updateBuffer())
1207             document.body[i] = "\\begin_inset Argument 999"
1208             i = i + 1
1209             continue
1210         
1211         # Find containing paragraph layout
1212         parent = get_containing_layout(document.body, i)
1213         if parent == False:
1214             document.warning("Malformed lyx document: Can't find parent paragraph layout")
1215             i = i + 1
1216             continue
1217         parbeg = parent[1]
1218         parend = parent[2]
1219         allowed_opts = -1
1220         first_req = -1
1221         if len(used_caveat_modules) > 0:
1222             # We know for now that this must be the initials module with the Initial layout
1223             # If we get more such modules, we need some automating.
1224             if parent[0] == "Initial":
1225                 # Layout has 1 opt and 1 req arg.
1226                 # Count the actual arguments
1227                 actualargs = 0
1228                 for p in range(parbeg, parend):
1229                     if document.body[p] == "\\begin_inset Argument":
1230                         actualargs += 1
1231                 if actualargs == 1:
1232                     allowed_opts = 0
1233                     first_req = 2
1234         # Collect all arguments in this paragraph
1235         argnr = 0
1236         for p in range(parbeg, parend):
1237             if document.body[p] == "\\begin_inset Argument":
1238                 argnr += 1
1239                 if allowed_opts != -1:
1240                     # We have less arguments than opt + required.
1241                     # required must take precedence.
1242                     if argnr > allowed_opts and argnr < first_req:
1243                         argnr = first_req
1244                 document.body[p] = "\\begin_inset Argument %d" % argnr
1245         i = i + 1
1246
1247
1248 def revert_latexargs(document):
1249     " Revert InsetArgument to old syntax "
1250
1251     i = 0
1252     rx = re.compile(r'^\\begin_inset Argument (\d+)$')
1253     args = dict()
1254     while True:
1255         # Search for Argument insets
1256         i = find_token(document.body, "\\begin_inset Argument", i)
1257         if i == -1:
1258             return
1259         m = rx.match(document.body[i])
1260         if not m:
1261             # No ID: inset already reverted
1262             i = i + 1
1263             continue
1264         # Find containing paragraph layout
1265         parent = get_containing_layout(document.body, i)
1266         if parent == False:
1267             document.warning("Malformed lyx document: Can't find parent paragraph layout")
1268             i = i + 1
1269             continue
1270         parbeg = parent[1]
1271         parend = parent[2]
1272         # Collect all arguments in this paragraph 
1273         realparend = parend
1274         for p in range(parbeg, parend):
1275             m = rx.match(document.body[p])
1276             if m:
1277                 val = int(m.group(1))
1278                 j = find_end_of_inset(document.body, p)
1279                 # Revert to old syntax
1280                 document.body[p] = "\\begin_inset Argument"
1281                 if j == -1:
1282                     document.warning("Malformed lyx document: Can't find end of Argument inset")
1283                     continue
1284                 if val > 0:
1285                     args[val] = document.body[p : j + 1]
1286                 # Adjust range end
1287                 realparend = realparend - len(document.body[p : j + 1])
1288                 # Remove arg inset at this position
1289                 del document.body[p : j + 1]
1290             if p >= realparend:
1291                 break
1292         # Now sort the arg insets
1293         subst = [""]
1294         for f in sorted(args):
1295             subst += args[f]
1296             del args[f]
1297         # Insert the sorted arg insets at paragraph begin
1298         document.body[parbeg + 1:parbeg + 1] = subst
1299
1300         i = parbeg + 1 + len(subst)
1301
1302
1303 def revert_Argument_to_TeX_brace(document, line, n, nmax, environment):
1304     '''
1305     Reverts an InsetArgument to TeX-code
1306     usage:
1307     revert_Argument_to_TeX_brace(document, LineOfBeginLayout, StartArgument, EndArgument, isEnvironment)
1308     LineOfBeginLayout is the line  of the \begin_layout statement
1309     StartArgument is the number of the first argument that needs to be converted
1310     EndArgument is the number of the last argument that needs to be converted or the last defined one
1311     isEnvironment must be true, if the layout id for a LaTeX environment
1312     '''
1313     lineArg = 0
1314     while lineArg != -1 and n < nmax + 1:
1315       lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
1316       if lineArg != -1:
1317         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1318         # we have to assure that no other inset is in the Argument
1319         beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1320         endInset = find_token(document.body, "\\end_inset", beginPlain)
1321         k = beginPlain + 1
1322         l = k
1323         while beginInset < endInset and beginInset != -1:
1324           beginInset = find_token(document.body, "\\begin_inset", k)
1325           endInset = find_token(document.body, "\\end_inset", l)
1326           k = beginInset + 1
1327           l = endInset + 1
1328         if environment == False:
1329           document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
1330           del(document.body[lineArg : beginPlain + 1])
1331         else:
1332           document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
1333           document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
1334         n = n + 1
1335
1336
1337 def revert_IEEEtran(document):
1338   '''
1339   Reverts InsetArgument of
1340   Page headings
1341   Biography
1342   Biography without photo
1343   to TeX-code
1344   '''
1345   if document.textclass == "IEEEtran":
1346     i = 0
1347     j = 0
1348     k = 0
1349     while True:
1350       if i != -1:
1351         i = find_token(document.body, "\\begin_layout Page headings", i)
1352       if i != -1:
1353         revert_Argument_to_TeX_brace(document, i, 1, 1, False)
1354         i = i + 1
1355       if j != -1:
1356         j = find_token(document.body, "\\begin_layout Biography without photo", j)
1357       if j != -1:
1358         revert_Argument_to_TeX_brace(document, j, 1, 1, True)
1359         j = j + 1
1360       if k != -1:
1361         k = find_token(document.body, "\\begin_layout Biography", k)
1362         kA = find_token(document.body, "\\begin_layout Biography without photo", k)
1363         if k == kA and k != -1:
1364           k = k + 1
1365           continue
1366       if k != -1:
1367         # start with the second argument, therefore 2
1368         revert_Argument_to_TeX_brace(document, k, 2, 2, True)
1369         k = k + 1
1370       if i == -1 and j == -1 and k == -1:
1371         return
1372
1373
1374 def convert_TeX_brace_to_Argument(document, line, n, nmax, inset, environment):
1375     '''
1376     Converts TeX code for mandatory arguments to an InsetArgument
1377     The conversion of TeX code for optional arguments must be done with another routine
1378     !!! Be careful if the braces are different in your case as expected here:
1379     - "}{" separates mandatory arguments of commands
1380     - "}" + "{" separates mandatory arguments of commands
1381     - "}" + " " + "{" separates mandatory arguments of commands
1382     - { and } surround a mandatory argument of an environment
1383     usage:
1384     convert_TeX_brace_to_Argument(document, LineOfBeginLayout/Inset, StartArgument, EndArgument, isInset, isEnvironment)
1385     LineOfBeginLayout/Inset is the line  of the \begin_layout or \begin_inset statement
1386     StartArgument is the number of the first ERT that needs to be converted
1387     EndArgument is the number of the last ERT that needs to be converted
1388     isInset must be true, if braces inside an InsetLayout needs to be converted
1389     isEnvironment must be true, if the layout is for a LaTeX environment
1390     
1391     Todo: this routine can currently handle only one mandatory argument of environments
1392     '''
1393     lineERT = line
1394     endn = line
1395     loop = 1
1396     while lineERT != -1 and n < nmax + 1:
1397       lineERT = find_token(document.body, "\\begin_inset ERT", lineERT)
1398       if environment == False and lineERT != -1:
1399         bracePair = find_token(document.body, "}{", lineERT)
1400         # assure that the "}{" is in this ERT
1401         if bracePair == lineERT + 5:
1402           end = find_token(document.body, "\\end_inset", bracePair)
1403           document.body[lineERT : end + 1] = ["\\end_layout", "", "\\end_inset"]
1404           if loop == 1:
1405             # in the case that n > 1 we have optional arguments before
1406             # therefore detect them if any
1407             if n > 1:
1408               # first check if there is an argument
1409               lineArg = find_token(document.body, "\\begin_inset Argument", line)
1410               if lineArg < lineERT and lineArg != -1:
1411                 # we have an argument, so now search backwards for its end
1412                 # we must now assure that we don't find other insets like e.g. a newline
1413                 endInsetArg = lineERT
1414                 endLayoutArg = endInsetArg
1415                 while endInsetArg != endLayoutArg + 2 and endInsetArg != -1:
1416                   endInsetArg = endInsetArg - 1
1417                   endLayoutArg = endInsetArg
1418                   endInsetArg = find_token_backwards(document.body, "\\end_inset", endInsetArg)
1419                   endLayoutArg = find_token_backwards(document.body, "\\end_layout", endLayoutArg)
1420                 line = endInsetArg + 1
1421             if inset == False:
1422               document.body[line + 1 : line + 1] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
1423             else:
1424               document.body[line + 4 : line + 4] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
1425           else:
1426             document.body[endn : endn] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
1427           n = n + 1
1428           endn = end
1429           loop = loop + 1
1430         # now check the case that we have "}" + "{" in two ERTs
1431         else:
1432           endBrace = find_token(document.body, "}", lineERT)
1433           if endBrace == lineERT + 5:
1434             beginBrace = find_token(document.body, "{", endBrace)
1435             # assure that the ERTs are consecutive (11 or 12 depending if there is a space between the ERTs or not)
1436             if beginBrace == endBrace + 11 or beginBrace == endBrace + 12:
1437               end = find_token(document.body, "\\end_inset", beginBrace)
1438               document.body[lineERT : end + 1] = ["\\end_layout", "", "\\end_inset"]
1439               if loop == 1:
1440                 # in the case that n > 1 we have optional arguments before
1441                 # therefore detect them if any
1442                 if n > 1:
1443                   # first check if there is an argument
1444                   lineArg = find_token(document.body, "\\begin_inset Argument", line)
1445                   if lineArg < lineERT and lineArg != -1:
1446                     # we have an argument, so now search backwards for its end
1447                     # we must now assure that we don't find other insets like e.g. a newline
1448                     endInsetArg = lineERT
1449                     endLayoutArg = endInsetArg
1450                     while endInsetArg != endLayoutArg + 2 and endInsetArg != -1:
1451                       endInsetArg = endInsetArg - 1
1452                       endLayoutArg = endInsetArg
1453                       endInsetArg = find_token_backwards(document.body, "\\end_inset", endInsetArg)
1454                       endLayoutArg = find_token_backwards(document.body, "\\end_layout", endLayoutArg)
1455                     line = endInsetArg + 1
1456                 if inset == False:
1457                   document.body[line + 1 : line + 1] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
1458                 else:
1459                   document.body[line + 4 : line + 4] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
1460               else:
1461                 document.body[endn : endn] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
1462               n = n + 1
1463               loop = loop + 1
1464               # set the line where the next argument will be inserted
1465               if beginBrace == endBrace + 11:
1466                 endn = end - 11
1467               else:
1468                 endn = end - 12
1469           else:
1470             lineERT = lineERT + 1
1471       if environment == True and lineERT != -1:
1472         opening = find_token(document.body, "{", lineERT)
1473         if opening == lineERT + 5: # assure that the "{" is in this ERT
1474           end = find_token(document.body, "\\end_inset", opening)
1475           document.body[lineERT : end + 1] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
1476           n = n + 1
1477           lineERT2 = find_token(document.body, "\\begin_inset ERT", lineERT)
1478           closing = find_token(document.body, "}", lineERT2)
1479           if closing == lineERT2 + 5: # assure that the "}" is in this ERT
1480             end2 = find_token(document.body, "\\end_inset", closing)
1481             document.body[lineERT2 : end2 + 1] = ["\\end_layout", "", "\\end_inset"]
1482         else:
1483           lineERT = lineERT + 1
1484
1485
1486 def convert_IEEEtran(document):
1487   '''
1488   Converts ERT of
1489   Page headings
1490   Biography
1491   Biography without photo
1492   to InsetArgument
1493   '''
1494   if document.textclass == "IEEEtran":
1495     i = 0
1496     j = 0
1497     k = 0
1498     while True:
1499       if i != -1:
1500         i = find_token(document.body, "\\begin_layout Page headings", i)
1501       if i != -1:
1502         convert_TeX_brace_to_Argument(document, i, 1, 1, False, False)
1503         i = i + 1
1504       if j != -1:
1505         j = find_token(document.body, "\\begin_layout Biography without photo", j)
1506       if j != -1:
1507         convert_TeX_brace_to_Argument(document, j, 1, 1, False, True)
1508         j = j + 1
1509       if k != -1:
1510         # assure that we don't handle Biography Biography without photo
1511         k = find_token(document.body, "\\begin_layout Biography", k)
1512         kA = find_token(document.body, "\\begin_layout Biography without photo", k - 1)
1513       if k == kA and k != -1:
1514         k = k + 1
1515         continue
1516       if k != -1:
1517         # the argument we want to convert is the second one
1518         convert_TeX_brace_to_Argument(document, k, 2, 2, False, True)
1519         k = k + 1
1520       if i == -1 and j == -1 and k == -1:
1521         return
1522
1523
1524 def revert_AASTeX(document):
1525   " Reverts InsetArgument of Altaffilation to TeX-code "
1526   if document.textclass == "aastex":
1527     i = 0
1528     while True:
1529       if i != -1:
1530         i = find_token(document.body, "\\begin_layout Altaffilation", i)
1531       if i != -1:
1532         revert_Argument_to_TeX_brace(document, i, 1, 1, False)
1533         i = i + 1
1534       if i == -1:
1535         return
1536
1537
1538 def convert_AASTeX(document):
1539   " Converts ERT of Altaffilation to InsetArgument "
1540   if document.textclass == "aastex":
1541     i = 0
1542     while True:
1543       if i != -1:
1544         i = find_token(document.body, "\\begin_layout Altaffilation", i)
1545       if i != -1:
1546         convert_TeX_brace_to_Argument(document, i, 1, 1, False, False)
1547         i = i + 1
1548       if i == -1:
1549         return
1550
1551
1552 def revert_AGUTeX(document):
1553   " Reverts InsetArgument of Author affiliation to TeX-code "
1554   if document.textclass == "agutex":
1555     i = 0
1556     while True:
1557       if i != -1:
1558         i = find_token(document.body, "\\begin_layout Author affiliation", i)
1559       if i != -1:
1560         revert_Argument_to_TeX_brace(document, i, 1, 1, False)
1561         i = i + 1
1562       if i == -1:
1563         return
1564
1565
1566 def convert_AGUTeX(document):
1567   " Converts ERT of Author affiliation to InsetArgument "
1568   if document.textclass == "agutex":
1569     i = 0
1570     while True:
1571       if i != -1:
1572         i = find_token(document.body, "\\begin_layout Author affiliation", i)
1573       if i != -1:
1574         convert_TeX_brace_to_Argument(document, i, 1, 1, False, False)
1575         i = i + 1
1576       if i == -1:
1577         return
1578
1579
1580 def revert_IJMP(document):
1581   " Reverts InsetArgument of MarkBoth to TeX-code "
1582   if document.textclass == "ijmpc" or document.textclass == "ijmpd":
1583     i = 0
1584     while True:
1585       if i != -1:
1586         i = find_token(document.body, "\\begin_layout MarkBoth", i)
1587       if i != -1:
1588         revert_Argument_to_TeX_brace(document, i, 1, 1, False)
1589         i = i + 1
1590       if i == -1:
1591         return
1592
1593
1594 def convert_IJMP(document):
1595   " Converts ERT of MarkBoth to InsetArgument "
1596   if document.textclass == "ijmpc" or document.textclass == "ijmpd":
1597     i = 0
1598     while True:
1599       if i != -1:
1600         i = find_token(document.body, "\\begin_layout MarkBoth", i)
1601       if i != -1:
1602         convert_TeX_brace_to_Argument(document, i, 1, 1, False, False)
1603         i = i + 1
1604       if i == -1:
1605         return
1606
1607
1608 def revert_SIGPLAN(document):
1609   " Reverts InsetArgument of MarkBoth to TeX-code "
1610   if document.textclass == "sigplanconf":
1611     i = 0
1612     j = 0
1613     while True:
1614       if i != -1:
1615         i = find_token(document.body, "\\begin_layout Conference", i)
1616       if i != -1:
1617         revert_Argument_to_TeX_brace(document, i, 1, 1, False)
1618         i = i + 1
1619       if j != -1:
1620         j = find_token(document.body, "\\begin_layout Author", j)
1621       if j != -1:
1622         revert_Argument_to_TeX_brace(document, j, 1, 2, False)
1623         j = j + 1
1624       if i == -1 and j == -1:
1625         return
1626
1627
1628 def convert_SIGPLAN(document):
1629   " Converts ERT of MarkBoth to InsetArgument "
1630   if document.textclass == "sigplanconf":
1631     i = 0
1632     j = 0
1633     while True:
1634       if i != -1:
1635         i = find_token(document.body, "\\begin_layout Conference", i)
1636       if i != -1:
1637         convert_TeX_brace_to_Argument(document, i, 1, 1, False, False)
1638         i = i + 1
1639       if j != -1:
1640         j = find_token(document.body, "\\begin_layout Author", j)
1641       if j != -1:
1642         convert_TeX_brace_to_Argument(document, j, 1, 2, False, False)
1643         j = j + 1
1644       if i == -1 and j == -1:
1645         return
1646
1647
1648 def revert_SIGGRAPH(document):
1649   " Reverts InsetArgument of Flex CRcat to TeX-code "
1650   if document.textclass == "acmsiggraph":
1651     i = 0
1652     while True:
1653       if i != -1:
1654         i = find_token(document.body, "\\begin_inset Flex CRcat", i)
1655       if i != -1:
1656         revert_Argument_to_TeX_brace(document, i, 1, 3, False)
1657         i = i + 1
1658       if i == -1:
1659         return
1660
1661
1662 def convert_SIGGRAPH(document):
1663   " Converts ERT of Flex CRcat to InsetArgument "
1664   if document.textclass == "acmsiggraph":
1665     i = 0
1666     while True:
1667       if i != -1:
1668         i = find_token(document.body, "\\begin_inset Flex CRcat", i)
1669       if i != -1:
1670         convert_TeX_brace_to_Argument(document, i, 1, 3, True, False)
1671         i = i + 1
1672       if i == -1:
1673         return
1674
1675
1676 def revert_EuropeCV(document):
1677   " Reverts InsetArgument of Flex CRcat to TeX-code "
1678   if document.textclass == "europecv":
1679     i = 0
1680     j = 0
1681     k = 0
1682     m = 0
1683     while True:
1684       if i != -1:
1685         i = find_token(document.body, "\\begin_layout Item", i)
1686       if i != -1:
1687         revert_Argument_to_TeX_brace(document, i, 2, 2, False)
1688         i = i + 1
1689       if j != -1:
1690         j = find_token(document.body, "\\begin_layout BulletedItem", j)
1691       if j != -1:
1692         revert_Argument_to_TeX_brace(document, j, 2, 2, False)
1693         j = j + 1
1694       if k != -1:
1695         k = find_token(document.body, "\\begin_layout Language", k)
1696       if k != -1:
1697         revert_Argument_to_TeX_brace(document, k, 2, 6, False)
1698         k = k + 1
1699       if m != -1:
1700         m = find_token(document.body, "\\begin_layout LastLanguage", m)
1701       if m != -1:
1702         revert_Argument_to_TeX_brace(document, m, 2, 6, False)
1703         m = m + 1
1704       if i == -1 and j == -1 and k == -1 and m == -1:
1705         return
1706
1707
1708 def convert_EuropeCV(document):
1709   " Converts ERT of Flex CRcat to InsetArgument "
1710   if document.textclass == "europecv":
1711     i = 0
1712     j = 0
1713     k = 0
1714     m = 0
1715     while True:
1716       if i != -1:
1717         i = find_token(document.body, "\\begin_layout Item", i)
1718       if i != -1:
1719         convert_TeX_brace_to_Argument(document, i, 2, 2, False, False)
1720         i = i + 1
1721       if j != -1:
1722         j = find_token(document.body, "\\begin_layout BulletedItem", j)
1723       if j != -1:
1724         convert_TeX_brace_to_Argument(document, j, 2, 2, False, False)
1725         j = j + 1
1726       if k != -1:
1727         k = find_token(document.body, "\\begin_layout Language", k)
1728       if k != -1:
1729         convert_TeX_brace_to_Argument(document, k, 2, 6, False, False)
1730         k = k + 1
1731       if m != -1:
1732         m = find_token(document.body, "\\begin_layout LastLanguage", m)
1733       if m != -1:
1734         convert_TeX_brace_to_Argument(document, m, 2, 6, False, False)
1735         m = m + 1
1736       if i == -1 and j == -1 and k == -1 and m == -1:
1737         return
1738
1739
1740 def revert_literate(document):
1741     " Revert Literate document to old format "
1742     if del_token(document.header, "noweb", 0):
1743       document.textclass = "literate-" + document.textclass
1744       i = 0
1745       while True:
1746         i = find_token(document.body, "\\begin_layout Chunk", i)
1747         if i == -1:
1748           break
1749         document.body[i] = "\\begin_layout Scrap"
1750         i = i + 1
1751
1752
1753 def convert_literate(document):
1754     " Convert Literate document to new format"
1755     i = find_token(document.header, "\\textclass", 0)    
1756     if (i != -1) and "literate-" in document.header[i]:
1757       document.textclass = document.header[i].replace("\\textclass literate-", "")
1758       j = find_token(document.header, "\\begin_modules", 0)
1759       if (j != -1):
1760         document.header.insert(j + 1, "noweb")
1761       else:
1762         document.header.insert(i + 1, "\\end_modules")
1763         document.header.insert(i + 1, "noweb")
1764         document.header.insert(i + 1, "\\begin_modules")
1765       i = 0
1766       while True:
1767         i = find_token(document.body, "\\begin_layout Scrap", i)
1768         if i == -1:
1769           break
1770         document.body[i] = "\\begin_layout Chunk"
1771         i = i + 1
1772
1773
1774 def revert_itemargs(document):
1775     " Reverts \\item arguments to TeX-code "
1776     i = 0
1777     while True:
1778         i = find_token(document.body, "\\begin_inset Argument item:", i)
1779         if i == -1:
1780             return
1781         j = find_end_of_inset(document.body, i)
1782         # Find containing paragraph layout
1783         parent = get_containing_layout(document.body, i)
1784         if parent == False:
1785             document.warning("Malformed lyx document: Can't find parent paragraph layout")
1786             i = i + 1
1787             continue
1788         parbeg = parent[1]
1789         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1790         endPlain = find_end_of_layout(document.body, beginPlain)
1791         content = document.body[beginPlain + 1 : endPlain]
1792         del document.body[i:j+1]
1793         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
1794         document.body[parbeg + 1:parbeg + 1] = subst
1795         i = i + 1
1796
1797
1798 def revert_garamondx_newtxmath(document):
1799     " Revert native garamond newtxmath definition to LaTeX " 
1800
1801     i = find_token(document.header, "\\font_math", 0)
1802     if i == -1:
1803        return
1804     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1805         val = get_value(document.header, "\\font_math", i)
1806         if val == "garamondx-ntxm":
1807             add_to_preamble(document, "\\usepackage[garamondx]{newtxmath}")
1808             document.header[i] = "\\font_math auto"
1809
1810
1811 def revert_garamondx(document):
1812     " Revert native garamond font definition to LaTeX " 
1813
1814     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1815         i = find_token(document.header, "\\font_roman garamondx", 0)
1816         if i != -1:
1817             osf = False
1818             j = find_token(document.header, "\\font_osf true", 0)
1819             if j != -1:
1820                 osf = True
1821             preamble = "\\usepackage"
1822             if osf:
1823                 preamble += "[osfI]"
1824             preamble += "{garamondx}"
1825             add_to_preamble(document, [preamble])
1826             document.header[i] = "\\font_roman default"
1827
1828
1829 ##
1830 # Conversion hub
1831 #
1832
1833 supported_versions = ["2.1.0","2.1"]
1834 convert = [
1835            [414, []],
1836            [415, [convert_undertilde]],
1837            [416, []],
1838            [417, [convert_japanese_encodings]],
1839            [418, []],
1840            [419, []],
1841            [420, [convert_biblio_style]],
1842            [421, [convert_longtable_captions]],
1843            [422, [convert_use_packages]],
1844            [423, [convert_use_mathtools]],
1845            [424, [convert_cite_engine_type]],
1846            [425, []],
1847            [426, []],
1848            [427, []],
1849            [428, [convert_cell_rotation]],
1850            [429, [convert_table_rotation]],
1851            [430, [convert_listoflistings]],
1852            [431, [convert_use_amssymb]],
1853            [432, []],
1854            [433, [convert_armenian]],
1855            [434, []],
1856            [435, []],
1857            [436, []],
1858            [437, []],
1859            [438, []],
1860            [439, []],
1861            [440, []],
1862            [441, [convert_mdnomath]],
1863            [442, []],
1864            [443, []],
1865            [444, []],
1866            [445, []],
1867            [446, [convert_latexargs]],
1868            [447, [convert_IEEEtran, convert_AASTeX, convert_AGUTeX, convert_IJMP, convert_SIGPLAN, convert_SIGGRAPH, convert_EuropeCV]],
1869            [448, [convert_literate]],
1870            [449, []],
1871            [450, []]
1872           ]
1873
1874 revert =  [
1875            [449, [revert_garamondx, revert_garamondx_newtxmath]],
1876            [448, [revert_itemargs]],
1877            [447, [revert_literate]],
1878            [446, [revert_IEEEtran, revert_AASTeX, revert_AGUTeX, revert_IJMP, revert_SIGPLAN, revert_SIGGRAPH, revert_EuropeCV]],
1879            [445, [revert_latexargs]],
1880            [444, [revert_uop]],
1881            [443, [revert_biolinum]],
1882            [442, []],
1883            [441, [revert_newtxmath]],
1884            [440, [revert_mdnomath]],
1885            [439, [revert_mathfonts]],
1886            [438, [revert_minionpro]],
1887            [437, [revert_ipadeco, revert_ipachar]],
1888            [436, [revert_texgyre]],
1889            [435, [revert_mathdesign]],
1890            [434, [revert_txtt]],
1891            [433, [revert_libertine]],
1892            [432, [revert_armenian]],
1893            [431, [revert_languages, revert_ancientgreek]],
1894            [430, [revert_use_amssymb]],
1895            [429, [revert_listoflistings]],
1896            [428, [revert_table_rotation]],
1897            [427, [revert_cell_rotation]],
1898            [426, [revert_tipa]],
1899            [425, [revert_verbatim]],
1900            [424, [revert_cancel]],
1901            [423, [revert_cite_engine_type]],
1902            [422, [revert_use_mathtools]],
1903            [421, [revert_use_packages]],
1904            [420, [revert_longtable_captions]],
1905            [419, [revert_biblio_style]],
1906            [418, [revert_australian]],
1907            [417, [revert_justification]],
1908            [416, [revert_japanese_encodings]],
1909            [415, [revert_negative_space, revert_math_spaces]],
1910            [414, [revert_undertilde]],
1911            [413, [revert_visible_space]]
1912           ]
1913
1914
1915 if __name__ == "__main__":
1916     pass