]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_2_1.py
110b6339b0530505b4cbc98e21c6337fae9e0b33
[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, find_end_of_inset, \
29     find_end_of_layout, find_re, get_option_value, get_value, get_quoted_value, \
30     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     i = 0
1137     while True:
1138       i = find_token(document.body, "\\begin_inset Argument", i)
1139       if i == -1:
1140         return
1141       # We cannot do more here since we have no access to the layout.
1142       # InsetArgument itself will do the real work
1143       # (see InsetArgument::updateBuffer())
1144       document.body[i] = "\\begin_inset Argument 999"
1145       i = i + 1
1146
1147
1148 def revert_latexargs(document):
1149     " Revert InsetArgument to old syntax "
1150
1151     # FIXME: This method does not revert correctly (it does
1152     #        not reorder the arguments)
1153     # What needs to be done is this:
1154     # * find all arguments in a paragraph and reorder them
1155     #   according to their ID (which is deleted)
1156     # So: \\begin_inset Argument 2 ... \\begin_inset Argument 1
1157     # => \\begin_inset Argument ... \\begin_inset Argument
1158     #    with correct order.
1159     i = 0
1160     while True:
1161       i = find_token(document.body, "\\begin_inset Argument", i)
1162       if i == -1:
1163         return
1164       # Convert the syntax so that LyX 2.0 can at least open this
1165       document.body[i] = "\\begin_inset Argument"
1166       i = i + 1
1167
1168
1169 def revert_Argument_to_TeX_brace(document, line, n, nmax, environment):
1170     '''
1171     Reverts an InsetArgument to TeX-code
1172     usage:
1173     revert_Argument_to_TeX_brace(document, LineOfBeginLayout, StartArgument, EndArgument, isEnvironment)
1174     LineOfBeginLayout is the line  of the \begin_layout statement
1175     StartArgument is the number of the first argument that needs to be converted
1176     EndArgument is the number of the last argument that needs to be converted or the last defined one
1177     isEnvironment must be true, if the layout id for a LaTeX environment
1178     '''
1179     lineArg = 0
1180     while lineArg != -1 and n < nmax + 1:
1181       lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
1182       if lineArg != -1:
1183         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1184         # we have to assure that no other inset is in the Argument
1185         beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1186         endInset = find_token(document.body, "\\end_inset", beginPlain)
1187         k = beginPlain + 1
1188         l = k
1189         while beginInset < endInset and beginInset != -1:
1190           beginInset = find_token(document.body, "\\begin_inset", k)
1191           endInset = find_token(document.body, "\\end_inset", l)
1192           k = beginInset + 1
1193           l = endInset + 1
1194         if environment == False:
1195           document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
1196           del(document.body[lineArg : beginPlain + 1])
1197         else:
1198           document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
1199           document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
1200         n = n + 1
1201
1202
1203 def revert_IEEEtran(document):
1204   '''
1205   Reverts InsetArgument of
1206   Page headings
1207   Biography
1208   Biography without photo
1209   to TeX-code
1210   '''
1211   if document.textclass == "IEEEtran":
1212     i = 0
1213     j = 0
1214     k = 0
1215     while True:
1216       if i != -1:
1217         i = find_token(document.body, "\\begin_layout Page headings", i)
1218       if i != -1:
1219         revert_Argument_to_TeX_brace(document, i, 1, 1, False)
1220         i = i + 1
1221       if j != -1:
1222         j = find_token(document.body, "\\begin_layout Biography without photo", j)
1223       if j != -1:
1224         revert_Argument_to_TeX_brace(document, j, 1, 1, True)
1225         j = j + 1
1226       if k != -1:
1227         k = find_token(document.body, "\\begin_layout Biography", k)
1228         kA = find_token(document.body, "\\begin_layout Biography without photo", k)
1229         if k == kA and k != -1:
1230           k = k + 1
1231           continue
1232       if k != -1:
1233         # start with the second argument, therefore 2
1234         revert_Argument_to_TeX_brace(document, k, 2, 2, True)
1235         k = k + 1
1236       if i == -1 and j == -1 and k == -1:
1237         return
1238
1239
1240 def convert_Argument_to_TeX_brace(document, line, n, nmax, environment):
1241     '''
1242     Converts TeX code to an InsetArgument
1243     !!! Be careful if the braces are different in your case as expected here:
1244     - }{ separates mandatory arguments of commands
1245     - { and } surround a mandatory argument of an environment
1246     usage:
1247     convert_Argument_to_TeX_brace(document, LineOfBeginLayout, StartArgument, EndArgument, isEnvironment)
1248     LineOfBeginLayout is the line  of the \begin_layout statement
1249     StartArgument is the number of the first ERT that needs to be converted
1250     EndArgument is the number of the last ERT that needs to be converted
1251     isEnvironment must be true, if the layout id for a LaTeX environment
1252     
1253     Notes:
1254     - this routine will fail if the user has additional TeX-braces (there is nothing we can do)
1255     - this routine can currently handle only one mandatory argument of environments
1256     Todo:
1257     - support the case that }{ is in the file in 2 separate ERTs
1258     '''
1259     lineArg = line
1260     while lineArg != -1 and n < nmax + 1:
1261       lineArg = find_token(document.body, "\\begin_inset ERT", lineArg)
1262       if environment == False and lineArg != -1:
1263         bracePair = find_token(document.body, "}{", lineArg)
1264         # assure that the "}{" is in this ERT (5 is or files saved with LyX 2.0, 4 for files exported by LyX 2.1)
1265         if bracePair == lineArg + 5 or bracePair == lineArg + 4:
1266           end = find_token(document.body, "\\end_inset", bracePair)
1267           document.body[lineArg : end + 1] = ["\\end_layout", "", "\\end_inset"]
1268           if n == 1:
1269             document.body[line + 1 : line + 1] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
1270           else:
1271             document.body[endn + 1 : endn + 1] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
1272           n = n + 1
1273           endn = end
1274         else:
1275           lineArg = lineArg + 1
1276       if environment == True and lineArg != -1:
1277         opening = find_token(document.body, "{", lineArg)
1278         if opening == lineArg + 5 or opening == lineArg + 4: # assure that the "{" is in this ERT
1279           end = find_token(document.body, "\\end_inset", opening)
1280           document.body[lineArg : end + 1] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
1281           n = n + 1
1282           lineArg2 = find_token(document.body, "\\begin_inset ERT", lineArg)
1283           closing = find_token(document.body, "}", lineArg2)
1284           if closing == lineArg2 + 5 or closing == lineArg2 + 4: # assure that the "}" is in this ERT
1285             end2 = find_token(document.body, "\\end_inset", closing)
1286             document.body[lineArg2 : end2 + 1] = ["\\end_layout", "", "\\end_inset"]
1287         else:
1288           lineArg = lineArg + 1
1289
1290
1291 def convert_IEEEtran(document):
1292   '''
1293   Converts ERT of
1294   Page headings
1295   Biography
1296   Biography without photo
1297   to InsetArgument
1298   '''
1299   if document.textclass == "IEEEtran":
1300     i = 0
1301     j = 0
1302     k = 0
1303     while True:
1304       if i != -1:
1305         i = find_token(document.body, "\\begin_layout Page headings", i)
1306       if i != -1:
1307         convert_Argument_to_TeX_brace(document, i, 1, 1, False)
1308         i = i + 1
1309       if j != -1:
1310         j = find_token(document.body, "\\begin_layout Biography without photo", j)
1311       if j != -1:
1312         convert_Argument_to_TeX_brace(document, j, 1, 1, True)
1313         j = j + 1
1314       if k != -1:
1315         # assure that we don't handle Biography Biography without photo
1316         k = find_token(document.body, "\\begin_layout Biography", k)
1317         kA = find_token(document.body, "\\begin_layout Biography without photo", k - 1)
1318       if k == kA and k != -1:
1319         k = k + 1
1320         continue
1321       if k != -1:
1322         # the argument we want to convert is the second one
1323         convert_Argument_to_TeX_brace(document, k, 2, 2, True)
1324         k = k + 1
1325       if i == -1 and j == -1 and k == -1:
1326         return
1327
1328
1329 def revert_AASTeX(document):
1330   " Reverts InsetArgument of Altaffilation to TeX-code "
1331   if document.textclass == "aastex":
1332     i = 0
1333     while True:
1334       if i != -1:
1335         i = find_token(document.body, "\\begin_layout Altaffilation", i)
1336       if i != -1:
1337         revert_Argument_to_TeX_brace(document, i, 1, 1, False)
1338         i = i + 1
1339       if i == -1:
1340         return
1341
1342
1343 def convert_AASTeX(document):
1344   " Converts ERT of Altaffilation to InsetArgument "
1345   if document.textclass == "aastex":
1346     i = 0
1347     while True:
1348       if i != -1:
1349         i = find_token(document.body, "\\begin_layout Altaffilation", i)
1350       if i != -1:
1351         convert_Argument_to_TeX_brace(document, i, 1, 1, False)
1352         i = i + 1
1353       if i == -1:
1354         return
1355
1356
1357 def revert_AGUTeX(document):
1358   " Reverts InsetArgument of Author affiliation to TeX-code "
1359   if document.textclass == "agutex":
1360     i = 0
1361     while True:
1362       if i != -1:
1363         i = find_token(document.body, "\\begin_layout Author affiliation", i)
1364       if i != -1:
1365         revert_Argument_to_TeX_brace(document, i, 1, 1, False)
1366         i = i + 1
1367       if i == -1:
1368         return
1369
1370
1371 def convert_AGUTeX(document):
1372   " Converts ERT of Author affiliation to InsetArgument "
1373   if document.textclass == "agutex":
1374     i = 0
1375     while True:
1376       if i != -1:
1377         i = find_token(document.body, "\\begin_layout Author affiliation", i)
1378       if i != -1:
1379         convert_Argument_to_TeX_brace(document, i, 1, 1, False)
1380         i = i + 1
1381       if i == -1:
1382         return
1383
1384
1385 def revert_IJMP(document):
1386   " Reverts InsetArgument of MarkBoth to TeX-code "
1387   if document.textclass == "ijmpc" or document.textclass == "ijmpd":
1388     i = 0
1389     while True:
1390       if i != -1:
1391         i = find_token(document.body, "\\begin_layout MarkBoth", i)
1392       if i != -1:
1393         revert_Argument_to_TeX_brace(document, i, 1, 1, False)
1394         i = i + 1
1395       if i == -1:
1396         return
1397
1398
1399 def convert_IJMP(document):
1400   " Converts ERT of MarkBoth to InsetArgument "
1401   if document.textclass == "ijmpc" or document.textclass == "ijmpd":
1402     i = 0
1403     while True:
1404       if i != -1:
1405         i = find_token(document.body, "\\begin_layout MarkBoth", i)
1406       if i != -1:
1407         convert_Argument_to_TeX_brace(document, i, 1, 1, False)
1408         i = i + 1
1409       if i == -1:
1410         return
1411
1412
1413 def revert_SIGPLAN(document):
1414   " Reverts InsetArgument of MarkBoth to TeX-code "
1415   if document.textclass == "sigplanconf":
1416     i = 0
1417     j = 0
1418     while True:
1419       if i != -1:
1420         i = find_token(document.body, "\\begin_layout Conference", i)
1421       if i != -1:
1422         revert_Argument_to_TeX_brace(document, i, 1, 1, False)
1423         i = i + 1
1424       if j != -1:
1425         j = find_token(document.body, "\\begin_layout Author", j)
1426       if j != -1:
1427         revert_Argument_to_TeX_brace(document, j, 1, 2, False)
1428         j = j + 1
1429       if i == -1 and j == -1:
1430         return
1431
1432
1433 def convert_SIGPLAN(document):
1434   " Converts ERT of MarkBoth to InsetArgument "
1435   if document.textclass == "sigplanconf":
1436     i = 0
1437     j = 0
1438     while True:
1439       if i != -1:
1440         i = find_token(document.body, "\\begin_layout Conference", i)
1441       if i != -1:
1442         convert_Argument_to_TeX_brace(document, i, 1, 1, False)
1443         i = i + 1
1444       if j != -1:
1445         j = find_token(document.body, "\\begin_layout Author", j)
1446       if j != -1:
1447         convert_Argument_to_TeX_brace(document, j, 1, 2, False)
1448         j = j + 1
1449       if i == -1 and j == -1:
1450         return
1451
1452
1453 def revert_literate(document):
1454     " Revert Literate document to old format "
1455     if del_token(document.header, "noweb", 0):
1456       document.textclass = "literate-" + document.textclass
1457       i = 0
1458       while True:
1459         i = find_token(document.body, "\\begin_layout Chunk", i)
1460         if i == -1:
1461           break
1462         document.body[i] = "\\begin_layout Scrap"
1463         i = i + 1
1464
1465
1466 def convert_literate(document):
1467     " Convert Literate document to new format"
1468     i = find_token(document.header, "\\textclass", 0)    
1469     if (i != -1) and "literate-" in document.header[i]:
1470       document.textclass = document.header[i].replace("\\textclass literate-", "")
1471       j = find_token(document.header, "\\begin_modules", 0)
1472       if (j != -1):
1473         document.header.insert(j + 1, "noweb")
1474       else:
1475         document.header.insert(i + 1, "\\end_modules")
1476         document.header.insert(i + 1, "noweb")
1477         document.header.insert(i + 1, "\\begin_modules")
1478       i = 0
1479       while True:
1480         i = find_token(document.body, "\\begin_layout Scrap", i)
1481         if i == -1:
1482           break
1483         document.body[i] = "\\begin_layout Chunk"
1484         i = i + 1
1485
1486
1487 def revert_itemargs(document):
1488     " Reverts \\item arguments to TeX-code "
1489     while True:
1490         i = find_token(document.body, "\\begin_inset Argument item:", 0)
1491         j = find_end_of_inset(document.body, i)
1492         if i == -1:
1493             break
1494         lastlay = find_token_backwards(document.body, "\\begin_layout", i)
1495         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1496         endLayout = find_token(document.body, "\\end_layout", beginPlain)
1497         endInset = find_token(document.body, "\\end_inset", endLayout)
1498         content = document.body[beginPlain + 1 : endLayout]
1499         del document.body[i:j+1]
1500         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
1501         document.body[lastlay + 1:lastlay + 1] = subst
1502         i = i + 1
1503
1504
1505 ##
1506 # Conversion hub
1507 #
1508
1509 supported_versions = ["2.1.0","2.1"]
1510 convert = [
1511            [414, []],
1512            [415, [convert_undertilde]],
1513            [416, []],
1514            [417, [convert_japanese_encodings]],
1515            [418, []],
1516            [419, []],
1517            [420, [convert_biblio_style]],
1518            [421, [convert_longtable_captions]],
1519            [422, [convert_use_packages]],
1520            [423, [convert_use_mathtools]],
1521            [424, [convert_cite_engine_type]],
1522            [425, []],
1523            [426, []],
1524            [427, []],
1525            [428, [convert_cell_rotation]],
1526            [429, [convert_table_rotation]],
1527            [430, [convert_listoflistings]],
1528            [431, [convert_use_amssymb]],
1529            [432, []],
1530            [433, [convert_armenian]],
1531            [434, []],
1532            [435, []],
1533            [436, []],
1534            [437, []],
1535            [438, []],
1536            [439, []],
1537            [440, []],
1538            [441, [convert_mdnomath]],
1539            [442, []],
1540            [443, []],
1541            [444, []],
1542            [445, []],
1543            [446, [convert_latexargs]],
1544            [447, [convert_IEEEtran, convert_AASTeX, convert_AGUTeX, convert_IJMP, convert_SIGPLAN]],
1545            [448, [convert_literate]],
1546            [449, []]
1547           ]
1548
1549 revert =  [
1550            [448, [revert_itemargs]],
1551            [447, [revert_literate]],
1552            [446, [revert_IEEEtran, revert_AASTeX, revert_AGUTeX, revert_IJMP, revert_SIGPLAN]],
1553            [445, [revert_latexargs]],
1554            [444, [revert_uop]],
1555            [443, [revert_biolinum]],
1556            [442, []],
1557            [441, [revert_newtxmath]],
1558            [440, [revert_mdnomath]],
1559            [439, [revert_mathfonts]],
1560            [438, [revert_minionpro]],
1561            [437, [revert_ipadeco, revert_ipachar]],
1562            [436, [revert_texgyre]],
1563            [435, [revert_mathdesign]],
1564            [434, [revert_txtt]],
1565            [433, [revert_libertine]],
1566            [432, [revert_armenian]],
1567            [431, [revert_languages, revert_ancientgreek]],
1568            [430, [revert_use_amssymb]],
1569            [429, [revert_listoflistings]],
1570            [428, [revert_table_rotation]],
1571            [427, [revert_cell_rotation]],
1572            [426, [revert_tipa]],
1573            [425, [revert_verbatim]],
1574            [424, [revert_cancel]],
1575            [423, [revert_cite_engine_type]],
1576            [422, [revert_use_mathtools]],
1577            [421, [revert_use_packages]],
1578            [420, [revert_longtable_captions]],
1579            [419, [revert_biblio_style]],
1580            [418, [revert_australian]],
1581            [417, [revert_justification]],
1582            [416, [revert_japanese_encodings]],
1583            [415, [revert_negative_space, revert_math_spaces]],
1584            [414, [revert_undertilde]],
1585            [413, [revert_visible_space]]
1586           ]
1587
1588
1589 if __name__ == "__main__":
1590     pass