]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_2_1.py
a61f9e995cd72010f8535ded1da5a11d75b61c10
[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 count_pars_in_inset, del_token, find_token, find_token_exact, \
29     find_token_backwards, find_end_of, find_end_of_inset, find_end_of_layout, \
30     find_end_of_sequence, find_re, get_option_value, get_containing_layout, \
31     get_containing_inset, get_value, get_quoted_value, set_option_value
32
33 #from parser_tools import find_token, find_end_of, find_tokens, \
34   #find_end_of_inset, find_end_of_layout, \
35   #is_in_inset, del_token, check_token
36
37 from lyx2lyx_tools import add_to_preamble, put_cmd_in_ert, get_ert
38
39 #from lyx2lyx_tools import insert_to_preamble, \
40 #  lyx2latex, latex_length, revert_flex_inset, \
41 #  revert_font_attrs, hex2ratio, str2bool
42
43 ####################################################################
44 # Private helper functions
45
46 #def remove_option(lines, m, option):
47     #''' removes option from line m. returns whether we did anything '''
48     #l = lines[m].find(option)
49     #if l == -1:
50         #return False
51     #val = lines[m][l:].split('"')[1]
52     #lines[m] = lines[m][:l - 1] + lines[m][l+len(option + '="' + val + '"'):]
53     #return True
54
55
56 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt):
57     '''
58     Reverts an InsetArgument to TeX-code
59     usage:
60     revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt)
61     LineOfBegin is the line  of the \begin_layout or \begin_inset statement
62     LineOfEnd is the line  of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
63     StartArgument is the number of the first argument that needs to be converted
64     EndArgument is the number of the last argument that needs to be converted or the last defined one
65     isEnvironment must be true, if the layout is for a LaTeX environment
66     isOpt must be true, if the argument is an optional one
67     '''
68     lineArg = 0
69     wasOpt = False
70     while lineArg != -1 and n < nmax + 1:
71       lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
72       if lineArg > endline and endline != 0:
73         return wasOpt
74       if lineArg != -1:
75         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
76         # we have to assure that no other inset is in the Argument
77         beginInset = find_token(document.body, "\\begin_inset", beginPlain)
78         endInset = find_token(document.body, "\\end_inset", beginPlain)
79         k = beginPlain + 1
80         l = k
81         while beginInset < endInset and beginInset != -1:
82           beginInset = find_token(document.body, "\\begin_inset", k)
83           endInset = find_token(document.body, "\\end_inset", l)
84           k = beginInset + 1
85           l = endInset + 1
86         if environment == False:
87           if opt == False:
88             document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
89             del(document.body[lineArg : beginPlain + 1])
90             wasOpt = False
91           else:
92             document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
93             document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
94             wasOpt = True
95         else:
96           document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
97           document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
98           wasOpt = False
99         n += 1
100     return wasOpt
101
102
103 def convert_TeX_brace_to_Argument(document, line, n, nmax, inset, environment, opt):
104     '''
105     Converts TeX code for mandatory arguments to an InsetArgument
106     The conversion of TeX code for optional arguments must be done with another routine
107     !!! Be careful if the braces are different in your case as expected here:
108     - "}{" separates mandatory arguments of commands
109     - "}" + "{" separates mandatory arguments of commands
110     - "}" + " " + "{" separates mandatory arguments of commands
111     - { and } surround a mandatory argument of an environment
112     usage:
113     convert_TeX_brace_to_Argument(document, LineOfBeginLayout/Inset, StartArgument, EndArgument, isInset, isEnvironment, isOpt)
114     LineOfBeginLayout/Inset is the line  of the \begin_layout or \begin_inset statement
115     StartArgument is the number of the first ERT that needs to be converted
116     EndArgument is the number of the last ERT that needs to be converted
117     isInset must be true, if braces inside an InsetLayout needs to be converted
118     isEnvironment must be true, if the layout is for a LaTeX environment
119     isOpt must be true, if the argument is an optional one
120     
121     Todo: this routine can currently handle only one mandatory argument of environments
122     '''
123
124     end_layout = find_end_of_layout(document.body, line)
125     lineERT = line
126     endn = line
127     loop = 1
128     while n < nmax + 1:
129       lineERT = find_token(document.body, "\\begin_inset ERT", lineERT, end_layout)
130       if lineERT == -1:
131         break
132       if environment == False:
133         end_ERT = find_end_of_inset(document.body, lineERT)
134         if end_ERT == -1:
135           document.warning("Can't find end of ERT!!")
136           break
137         # Note that this only checks for ][ or }{ at the beginning of a line
138         if opt:
139           bracePair = find_token(document.body, "][", lineERT, end_ERT)
140         else:
141           bracePair = find_token(document.body, "}{", lineERT, end_ERT)
142         if bracePair != -1:
143           end = find_token(document.body, "\\end_inset", bracePair)
144           document.body[lineERT : end_ERT + 1] = ["\\end_layout", "", "\\end_inset"]
145           if loop == 1:
146             # in the case that n > 1 we have optional arguments before
147             # therefore detect them if any
148             if n > 1:
149               # first check if there is an argument
150               lineArg = find_token(document.body, "\\begin_inset Argument", line)
151               if lineArg < lineERT and lineArg != -1:
152                 # we have an argument, so now search backwards for its end
153                 # we must now assure that we don't find other insets like e.g. a newline
154                 endInsetArg = lineERT
155                 endLayoutArg = endInsetArg
156                 while endInsetArg != endLayoutArg + 2 and endInsetArg != -1:
157                   endInsetArg = endInsetArg - 1
158                   endLayoutArg = endInsetArg
159                   endInsetArg = find_token_backwards(document.body, "\\end_inset", endInsetArg)
160                   endLayoutArg = find_token_backwards(document.body, "\\end_layout", endLayoutArg)
161                 line = endInsetArg + 1
162             if inset == False:
163               document.body[line + 1 : line + 1] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
164             else:
165               document.body[line + 4 : line + 4] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
166           else: # if loop != 1
167             document.body[endn : endn] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
168           n += 1
169           endn = end
170           loop += 1
171         else: 
172           # no brace pair found
173           # now check the case that we have "}" + "{" in two ERTs
174           if opt:
175             endBrace = find_token(document.body, "]", lineERT, end_layout)
176           else:
177             endBrace = find_token(document.body, "}", lineERT, end_layout)
178           if endBrace == lineERT + 5:
179             if opt:
180               beginBrace = find_token(document.body, "[", endBrace, end_layout)
181             else:
182               beginBrace = find_token(document.body, "{", endBrace, end_layout)
183             # assure that the ERTs are consecutive (11 or 12 depending if there is a space between the ERTs or not)
184             if beginBrace == endBrace + 11 or beginBrace == endBrace + 12:
185               end = find_token(document.body, "\\end_inset", beginBrace)
186               document.body[lineERT : end + 1] = ["\\end_layout", "", "\\end_inset"]
187               if loop == 1:
188                 # in the case that n > 1 we have optional arguments before
189                 # therefore detect them if any
190                 if n > 1:
191                   # first check if there is an argument
192                   lineArg = find_token(document.body, "\\begin_inset Argument", line)
193                   if lineArg < lineERT and lineArg != -1:
194                     # we have an argument, so now search backwards for its end
195                     # we must now assure that we don't find other insets like e.g. a newline
196                     endInsetArg = lineERT
197                     endLayoutArg = endInsetArg
198                     while endInsetArg != endLayoutArg + 2 and endInsetArg != -1:
199                       endInsetArg = endInsetArg - 1
200                       endLayoutArg = endInsetArg
201                       endInsetArg = find_token_backwards(document.body, "\\end_inset", endInsetArg)
202                       endLayoutArg = find_token_backwards(document.body, "\\end_layout", endLayoutArg)
203                     line = endInsetArg + 1
204                 if inset == False:
205                   document.body[line + 1 : line + 1] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
206                 else:
207                   document.body[line + 4 : line + 4] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
208               else:
209                 document.body[endn : endn] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
210               n += 1
211               loop += 1
212               # set the line where the next argument will be inserted
213               if beginBrace == endBrace + 11:
214                 endn = end - 11
215               else:
216                 endn = end - 12
217             else:
218               lineERT += 1
219           else:
220             lineERT += 1
221       if environment == True:
222         end_ERT = find_end_of_inset(document.body, lineERT)
223         if end_ERT == -1:
224           document.warning("Can't find end of ERT!!")
225           break
226         # Note that this only checks for [ or { at the beginning of a line
227         if opt:
228           opening = find_token(document.body, "[", lineERT, end_ERT)
229         else:
230           opening = find_token(document.body, "{", lineERT, end_ERT)
231         if opening != -1:
232           lineERT2 = find_token(document.body, "\\begin_inset ERT", end_ERT, end_layout)
233           if lineERT2 == -1:
234             # argument in a single ERT
235             # strip off the opening bracket
236             document.body[opening] = document.body[opening][1:]
237             ertcontlastline = end_ERT - 3
238             if (opt and document.body[ertcontlastline].endswith("]")) or document.body[ertcontlastline].endswith("}"):
239               # strip off the closing bracket
240               document.body[ertcontlastline] = document.body[ertcontlastline][:-1]
241               end2 = find_token(document.body, "\\end_inset", ertcontlastline)
242               document.body[lineERT : lineERT + 1] = ["\\begin_inset Argument " + str(n)]
243           else:
244             end_ERT2 = find_end_of_inset(document.body, lineERT2)
245             if end_ERT2 == -1:
246               document.warning("Can't find end of second ERT!!")
247               break
248             if opt:
249               closing = find_token(document.body, "]", lineERT2, end_ERT2)
250             else:
251               closing = find_token(document.body, "}", lineERT2, end_ERT2)
252             if closing != -1: # assure that the "}" is in this ERT
253               end2 = find_token(document.body, "\\end_inset", closing)
254               document.body[lineERT2 : end2 + 1] = ["\\end_layout", "", "\\end_inset"]
255             document.body[lineERT : end_ERT + 1] = ["\\begin_inset Argument " + str(n), "status open", "", "\\begin_layout Plain Layout"]
256           n += 1
257
258
259 ###############################################################################
260 ###
261 ### Conversion and reversion routines
262 ###
263 ###############################################################################
264
265 def revert_visible_space(document):
266     "Revert InsetSpace visible into its ERT counterpart"
267     i = 0
268     while True:
269       i = find_token(document.body, "\\begin_inset space \\textvisiblespace{}", i)
270       if i == -1:
271         return
272       end = find_end_of_inset(document.body, i)
273       subst = put_cmd_in_ert("\\textvisiblespace{}")
274       document.body[i:end + 1] = subst
275
276
277 undertilde_commands = ["utilde"]
278 def convert_undertilde(document):
279     " Load undertilde automatically "
280     i = find_token(document.header, "\\use_mathdots" , 0)
281     if i == -1:
282         i = find_token(document.header, "\\use_mhchem" , 0)
283     if i == -1:
284         i = find_token(document.header, "\\use_esint" , 0)
285     if i == -1:
286         document.warning("Malformed LyX document: Can't find \\use_mathdots.")
287         return;
288     j = find_token(document.preamble, "\\usepackage{undertilde}", 0)
289     if j != -1:
290         # package was loaded in the preamble, convert this to header setting for round trip
291         document.header.insert(i + 1, "\\use_undertilde 2") # on
292         del document.preamble[j]
293     else:
294         j = 0
295         while True:
296             j = find_token(document.body, '\\begin_inset Formula', j)
297             if j == -1:
298                 break
299             k = find_end_of_inset(document.body, j)
300             if k == -1:
301                 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
302                 j += 1
303                 continue
304             code = "\n".join(document.body[j:k])
305             for c in undertilde_commands:
306                 if code.find("\\%s" % c) != -1:
307                     # at least one of the commands was found - need to switch package off
308                     document.header.insert(i + 1, "\\use_undertilde 0") # off
309                     return
310             j = k
311         # no command was found - set to auto (bug 9069)
312         document.header.insert(i + 1, "\\use_undertilde 1") # auto
313
314
315
316 def revert_undertilde(document):
317     " Load undertilde if used in the document "
318     regexp = re.compile(r'(\\use_undertilde)')
319     i = find_re(document.header, regexp, 0)
320     value = "1" # default is auto
321     if i != -1:
322         value = get_value(document.header, "\\use_undertilde" , i).split()[0]
323         del document.header[i]
324     if value == "2": # on
325         add_to_preamble(document, ["\\usepackage{undertilde}"])
326     elif value == "1": # auto
327         i = 0
328         while True:
329             i = find_token(document.body, '\\begin_inset Formula', i)
330             if i == -1:
331                 return
332             j = find_end_of_inset(document.body, i)
333             if j == -1:
334                 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
335                 i += 1
336                 continue
337             code = "\n".join(document.body[i:j])
338             for c in undertilde_commands:
339                 if code.find("\\%s" % c) != -1:
340                     add_to_preamble(document, ["\\usepackage{undertilde}"])
341                     return
342             i = j
343
344
345 def revert_negative_space(document):
346     "Revert InsetSpace negmedspace and negthickspace into its TeX-code counterpart"
347     i = 0
348     j = 0
349     reverted = False
350     while True:
351       i = find_token(document.body, "\\begin_inset space \\negmedspace{}", i)
352       if i == -1:
353         j = find_token(document.body, "\\begin_inset space \\negthickspace{}", j)
354         if j == -1:
355           # load amsmath in the preamble if not already loaded if we are at the end of checking
356           if reverted == True:
357             i = find_token(document.header, "\\use_amsmath 2", 0)
358             if i == -1:
359               add_to_preamble(document, ["\\@ifundefined{negthickspace}{\\usepackage{amsmath}}"])
360           return
361       if i == -1:
362         return
363       end = find_end_of_inset(document.body, i)
364       subst = put_cmd_in_ert("\\negmedspace{}")
365       document.body[i:end + 1] = subst
366       j = find_token(document.body, "\\begin_inset space \\negthickspace{}", j)
367       if j == -1:
368         return
369       end = find_end_of_inset(document.body, j)
370       subst = put_cmd_in_ert("\\negthickspace{}")
371       document.body[j:end + 1] = subst
372       reverted = True
373
374
375 def revert_math_spaces(document):
376     "Revert formulas with protected custom space and protected hfills to TeX-code"
377     i = 0
378     while True:
379       i = find_token(document.body, "\\begin_inset Formula", i)
380       if i == -1:
381         return
382       j = document.body[i].find("\\hspace*")
383       if j != -1:
384         end = find_end_of_inset(document.body, i)
385         subst = put_cmd_in_ert(document.body[i][21:])
386         document.body[i:end + 1] = subst
387       i += 1
388
389
390 def convert_japanese_encodings(document):
391     " Rename the japanese encodings to names understood by platex "
392     jap_enc_dict = {
393         "EUC-JP-pLaTeX": "euc",
394         "JIS-pLaTeX":    "jis",
395         "SJIS-pLaTeX":   "sjis"
396     }
397     i = find_token(document.header, "\\inputencoding" , 0)
398     if i == -1:
399         return
400     val = get_value(document.header, "\\inputencoding", i)
401     if val in list(jap_enc_dict.keys()):
402         document.header[i] = "\\inputencoding %s" % jap_enc_dict[val]
403
404
405 def revert_japanese_encodings(document):
406     " Revert the japanese encodings name changes "
407     jap_enc_dict = {
408         "euc":  "EUC-JP-pLaTeX",
409         "jis":  "JIS-pLaTeX",
410         "sjis": "SJIS-pLaTeX"
411     }
412     i = find_token(document.header, "\\inputencoding" , 0)
413     if i == -1:
414         return
415     val = get_value(document.header, "\\inputencoding", i)
416     if val in list(jap_enc_dict.keys()):
417         document.header[i] = "\\inputencoding %s" % jap_enc_dict[val]
418
419
420 def convert_justification(document):
421     " Add the \\justification buffer param"
422     i = find_token(document.header, "\\suppress_date" , 0)
423     if i == -1:
424         i = find_token(document.header, "\\paperorientation" , 0)
425     if i == -1:
426         i = find_token(document.header, "\\use_indices" , 0)
427     if i == -1:
428         i = find_token(document.header, "\\use_bibtopic" , 0)
429     if i == -1:
430         document.warning("Malformed LyX document: Missing \\suppress_date.")
431         return
432     document.header.insert(i + 1, "\\justification true")
433
434
435 def revert_justification(document):
436     " Revert the \\justification buffer param"
437     if not del_token(document.header, '\\justification', 0):
438         document.warning("Malformed LyX document: Missing \\justification.")
439
440
441 def revert_australian(document):
442     "Set English language variants Australian and Newzealand to English" 
443
444     if document.language == "australian" or document.language == "newzealand": 
445         document.language = "english"
446         i = find_token(document.header, "\\language", 0) 
447         if i != -1: 
448             document.header[i] = "\\language english" 
449     j = 0 
450     while True: 
451         j = find_token(document.body, "\\lang australian", j) 
452         if j == -1:
453             j = find_token(document.body, "\\lang newzealand", 0)
454             if j == -1:
455                 return
456             else:
457                 document.body[j] = document.body[j].replace("\\lang newzealand", "\\lang english")
458         else:
459             document.body[j] = document.body[j].replace("\\lang australian", "\\lang english") 
460         j += 1
461
462
463 def convert_biblio_style(document):
464     "Add a sensible default for \\biblio_style based on the citation engine."
465     i = find_token(document.header, "\\cite_engine", 0)
466     if i != -1:
467         engine = get_value(document.header, "\\cite_engine", i).split("_")[0]
468         style = {"basic": "plain", "natbib": "plainnat", "jurabib": "jurabib"}
469         document.header.insert(i + 1, "\\biblio_style " + style[engine])
470
471
472 def revert_biblio_style(document):
473     "BibTeX insets with default option use the style defined by \\biblio_style."
474     i = find_token(document.header, "\\biblio_style" , 0)
475     if i == -1:
476         document.warning("No \\biblio_style line. Nothing to do.")
477         return
478
479     default_style = get_value(document.header, "\\biblio_style", i)
480     del document.header[i]
481
482     # We are looking for bibtex insets having the default option
483     i = 0
484     while True:
485         i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
486         if i == -1:
487             return
488         j = find_end_of_inset(document.body, i)
489         if j == -1:
490             document.warning("Malformed LyX document: Can't find end of bibtex inset at line " + str(i))
491             i += 1
492             return
493         k = find_token(document.body, "options", i, j)
494         if k != -1:
495             options = get_quoted_value(document.body, "options", k)
496             if "default" in options.split(","):
497                 document.body[k] = 'options "%s"' \
498                     % options.replace("default", default_style)
499         i = j
500
501
502 def handle_longtable_captions(document, forward):
503     begin_table = 0
504     while True:
505         begin_table = find_token(document.body, '<lyxtabular version=', begin_table)
506         if begin_table == -1:
507             break
508         end_table = find_end_of(document.body, begin_table, '<lyxtabular', '</lyxtabular>')
509         if end_table == -1:
510             document.warning("Malformed LyX document: Could not find end of table.")
511             begin_table += 1
512             continue
513         fline = find_token(document.body, "<features", begin_table, end_table)
514         if fline == -1:
515             document.warning("Can't find features for inset at line " + str(begin_table))
516             begin_table += 1
517             continue
518         p = document.body[fline].find("islongtable")
519         if p == -1:
520             # no longtable
521             begin_table += 1
522             continue
523         numrows = get_option_value(document.body[begin_table], "rows")
524         try:
525             numrows = int(numrows)
526         except:
527             document.warning(document.body[begin_table])
528             document.warning("Unable to determine rows!")
529             begin_table = end_table
530             continue
531         begin_row = begin_table
532         for row in range(numrows):
533             begin_row = find_token(document.body, '<row', begin_row, end_table)
534             if begin_row == -1:
535                 document.warning("Can't find row " + str(row + 1))
536                 break
537             end_row = find_end_of(document.body, begin_row, '<row', '</row>')
538             if end_row == -1:
539                 document.warning("Can't find end of row " + str(row + 1))
540                 break
541             if forward:
542                 if (get_option_value(document.body[begin_row], 'caption') == 'true' and
543                     get_option_value(document.body[begin_row], 'endfirsthead') != 'true' and
544                     get_option_value(document.body[begin_row], 'endhead') != 'true' and
545                     get_option_value(document.body[begin_row], 'endfoot') != 'true' and
546                     get_option_value(document.body[begin_row], 'endlastfoot') != 'true'):
547                     document.body[begin_row] = set_option_value(document.body[begin_row], 'caption', 'true", endfirsthead="true')
548             elif get_option_value(document.body[begin_row], 'caption') == 'true':
549                 if get_option_value(document.body[begin_row], 'endfirsthead') == 'true':
550                     document.body[begin_row] = set_option_value(document.body[begin_row], 'endfirsthead', 'false')
551                 if get_option_value(document.body[begin_row], 'endhead') == 'true':
552                     document.body[begin_row] = set_option_value(document.body[begin_row], 'endhead', 'false')
553                 if get_option_value(document.body[begin_row], 'endfoot') == 'true':
554                     document.body[begin_row] = set_option_value(document.body[begin_row], 'endfoot', 'false')
555                 if get_option_value(document.body[begin_row], 'endlastfoot') == 'true':
556                     document.body[begin_row] = set_option_value(document.body[begin_row], 'endlastfoot', 'false')
557             begin_row = end_row
558         # since there could be a tabular inside this one, we 
559         # cannot jump to end.
560         begin_table += 1
561
562
563 def convert_longtable_captions(document):
564     "Add a firsthead flag to caption rows"
565     handle_longtable_captions(document, True)
566
567
568 def revert_longtable_captions(document):
569     "remove head/foot flag from caption rows"
570     handle_longtable_captions(document, False)
571
572
573 def convert_use_packages(document):
574     "use_xxx yyy => use_package xxx yyy"
575     packages = ["amsmath", "esint", "mathdots", "mhchem", "undertilde"]
576     for p in packages:
577         i = find_token(document.header, "\\use_%s" % p, 0)
578         if i != -1:
579             value = get_value(document.header, "\\use_%s" % p, i)
580             document.header[i] = "\\use_package %s %s" % (p, value)
581
582
583 def revert_use_packages(document):
584     "use_package xxx yyy => use_xxx yyy"
585     packages = ["amsmath", "esint", "mhchem", "mathdots", "undertilde"]
586     # the order is arbitrary for the use_package version, and not all packages need to be given.
587     # Ensure a complete list and correct order (important for older LyX versions and especially lyx2lyx)
588     # first loop: find line with first package
589     j = -1
590     for p in packages:
591         regexp = re.compile(r'(\\use_package\s+%s)' % p)
592         i = find_re(document.header, regexp, 0)
593         if i != -1 and (j < 0 or i < j):
594             j = i
595     # second loop: replace or insert packages in front of all existing ones
596     for p in packages:
597         regexp = re.compile(r'(\\use_package\s+%s)' % p)
598         i = find_re(document.header, regexp, 0)
599         if i != -1:
600             value = get_value(document.header, "\\use_package %s" % p, i).split()[1]
601             del document.header[i]
602             document.header.insert(j, "\\use_%s %s" % (p, value))
603         else:
604             document.header.insert(j, "\\use_%s 1" % p)
605         j += 1
606
607
608 def convert_use_package(document, pkg, commands, oldauto):
609     # oldauto defines how the version we are converting from behaves:
610     # if it is true, the old version uses the package automatically.
611     # if it is false, the old version never uses the package.
612     i = find_token(document.header, "\\use_package", 0)
613     if i == -1:
614         document.warning("Malformed LyX document: Can't find \\use_package.")
615         return;
616     j = find_token(document.preamble, "\\usepackage{" + pkg + "}", 0)
617     if j != -1:
618         # package was loaded in the preamble, convert this to header setting for round trip
619         document.header.insert(i + 1, "\\use_package " + pkg + " 2") # on
620         del document.preamble[j]
621     # If oldauto is true we have two options:
622     # We can either set the package to auto - this is correct for files in
623     # format 425 to 463, and may create a conflict for older files which use
624     # any command in commands with a different definition.
625     # Or we can look whether any command in commands is used, and set it to
626     # auto if not and to off if yes. This will not create a conflict, but will
627     # create uncompilable documents for files in format 425 to 463, which use
628     # any command in commands.
629     # We choose the first option since its error is less likely.
630     elif oldauto:
631         document.header.insert(i + 1, "\\use_package " + pkg + " 1") # auto
632     else:
633         j = 0
634         while True:
635             j = find_token(document.body, '\\begin_inset Formula', j)
636             if j == -1:
637                 break
638             k = find_end_of_inset(document.body, j)
639             if k == -1:
640                 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
641                 j += 1
642                 continue
643             code = "\n".join(document.body[j:k])
644             for c in commands:
645                 if code.find("\\%s" % c) != -1:
646                     # at least one of the commands was found - need to switch package off
647                     document.header.insert(i + 1, "\\use_package " + pkg + " 0") # off
648                     return
649             j = k
650         # no command was found - set to auto (bug 9069)
651         document.header.insert(i + 1, "\\use_package " + pkg + " 1") # auto
652
653
654 def revert_use_package(document, pkg, commands, oldauto):
655     # oldauto defines how the version we are reverting to behaves:
656     # if it is true, the old version uses the package automatically.
657     # if it is false, the old version never uses the package.
658     regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
659     i = find_re(document.header, regexp, 0)
660     value = "1" # default is auto
661     if i != -1:
662         value = get_value(document.header, "\\use_package" , i).split()[1]
663         del document.header[i]
664     if value == "2": # on
665         add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
666     elif value == "1" and not oldauto: # auto
667         i = 0
668         while True:
669             i = find_token(document.body, '\\begin_inset Formula', i)
670             if i == -1:
671                 return
672             j = find_end_of_inset(document.body, i)
673             if j == -1:
674                 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
675                 i += 1
676                 continue
677             code = "\n".join(document.body[i:j])
678             for c in commands:
679                 if code.find("\\%s" % c) != -1:
680                     add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
681                     return
682             i = j
683
684
685 mathtools_commands = ["mathclap", "mathllap", "mathrlap", \
686                 "lgathered", "rgathered", "vcentcolon", "dblcolon", \
687                 "coloneqq", "Coloneqq", "coloneq", "Coloneq", "eqqcolon", \
688                 "Eqqcolon", "eqcolon", "Eqcolon", "colonapprox", \
689                 "Colonapprox", "colonsim", "Colonsim"]
690 def convert_use_mathtools(document):
691     "insert use_package mathtools"
692     convert_use_package(document, "mathtools", mathtools_commands, False)
693
694
695 def revert_use_mathtools(document):
696     "remove use_package mathtools"
697     revert_use_package(document, "mathtools", mathtools_commands, False)
698
699
700 # commands provided by stmaryrd.sty but LyX uses other packages:
701 # boxdot lightning, bigtriangledown, bigtriangleup
702 stmaryrd_commands = ["shortleftarrow", "shortrightarrow", "shortuparrow", \
703                     "shortdownarrow", "Yup", "Ydown", "Yleft", "Yright", \
704                     "varcurlyvee", "varcurlywedge", "minuso", "baro", \
705                     "sslash", "bbslash", "moo", "varotimes", "varoast", \
706                     "varobar", "varodot", "varoslash", "varobslash", \
707                     "varocircle", "varoplus", "varominus", "boxast", \
708                     "boxbar", "boxslash", "boxbslash", "boxcircle", \
709                     "boxbox", "boxempty", "merge", "vartimes", \
710                     "fatsemi", "sswarrow", "ssearrow", "curlywedgeuparrow", \
711                     "curlywedgedownarrow", "fatslash", "fatbslash", "lbag", \
712                     "rbag", "varbigcirc", "leftrightarroweq", \
713                     "curlyveedownarrow", "curlyveeuparrow", "nnwarrow", \
714                     "nnearrow", "leftslice", "rightslice", "varolessthan", \
715                     "varogreaterthan", "varovee", "varowedge", "talloblong", \
716                     "interleave", "obar", "obslash", "olessthan", \
717                     "ogreaterthan", "ovee", "owedge", "oblong", "inplus", \
718                     "niplus", "nplus", "subsetplus", "supsetplus", \
719                     "subsetpluseq", "supsetpluseq", "Lbag", "Rbag", \
720                     "llbracket", "rrbracket", "llparenthesis", \
721                     "rrparenthesis", "binampersand", "bindnasrepma", \
722                     "trianglelefteqslant", "trianglerighteqslant", \
723                     "ntrianglelefteqslant", "ntrianglerighteqslant", \
724                     "llfloor", "rrfloor", "llceil", "rrceil", "arrownot", \
725                     "Arrownot", "Mapstochar", "mapsfromchar", "Mapsfromchar", \
726                     "leftrightarrowtriangle", "leftarrowtriangle", \
727                     "rightarrowtriangle", \
728                     "bigcurlyvee", "bigcurlywedge", "bigsqcap", "bigbox", \
729                     "bigparallel", "biginterleave", "bignplus", \
730                     "varcopyright", "longarrownot", "Longarrownot", \
731                     "Mapsto", "mapsfrom", "Mapsfrom" "Longmapsto", \
732                     "longmapsfrom", "Longmapsfrom"]
733 def convert_use_stmaryrd(document):
734     "insert use_package stmaryrd"
735     convert_use_package(document, "stmaryrd", stmaryrd_commands, False)
736
737
738 def revert_use_stmaryrd(document):
739     "remove use_package stmaryrd"
740     revert_use_package(document, "stmaryrd", stmaryrd_commands, False)
741
742
743 stackrel_commands = ["stackrel"]
744 def convert_use_stackrel(document):
745     "insert use_package stackrel"
746     convert_use_package(document, "stackrel", stackrel_commands, False)
747
748
749 def revert_use_stackrel(document):
750     "remove use_package stackrel"
751     revert_use_package(document, "stackrel", stackrel_commands, False)
752
753
754 def convert_cite_engine_type(document):
755     "Determine the \\cite_engine_type from the citation engine."
756     i = find_token(document.header, "\\cite_engine", 0)
757     if i == -1:
758         return
759     engine = get_value(document.header, "\\cite_engine", i)
760     if "_" in engine:
761         engine, type = engine.split("_")
762     else:
763         type = {"basic": "numerical", "jurabib": "authoryear"}[engine]
764     document.header[i] = "\\cite_engine " + engine
765     document.header.insert(i + 1, "\\cite_engine_type " + type)
766
767
768 def revert_cite_engine_type(document):
769     "Natbib had the type appended with an underscore."
770     engine_type = "numerical"
771     i = find_token(document.header, "\\cite_engine_type" , 0)
772     if i == -1:
773         document.warning("No \\cite_engine_type line. Assuming numerical.")
774     else:
775         engine_type = get_value(document.header, "\\cite_engine_type", i)
776         del document.header[i]
777
778     # We are looking for the natbib citation engine
779     i = find_token(document.header, "\\cite_engine natbib", 0)
780     if i == -1:
781         return
782     document.header[i] = "\\cite_engine natbib_" + engine_type
783
784
785 def convert_cite_engine_type_default(document):
786     "Convert \\cite_engine_type to default for the basic citation engine."
787     i = find_token(document.header, "\\cite_engine basic", 0)
788     if i == -1:
789         return
790     i = find_token(document.header, "\\cite_engine_type" , 0)
791     if i == -1:
792         return
793     document.header[i] = "\\cite_engine_type default"
794
795
796 def revert_cite_engine_type_default(document):
797     """Revert \\cite_engine_type default.
798
799     Revert to numerical for the basic cite engine, otherwise to authoryear."""
800     engine_type = "authoryear"
801     i = find_token(document.header, "\\cite_engine_type default" , 0)
802     if i == -1:
803         return
804     j = find_token(document.header, "\\cite_engine basic", 0)
805     if j != -1:
806         engine_type = "numerical"
807     document.header[i] = "\\cite_engine_type " + engine_type
808
809
810 cancel_commands = ["cancel", "bcancel", "xcancel", "cancelto"]
811 # this is the same, as revert_use_cancel() except for the default
812 def revert_cancel(document):
813     "add cancel to the preamble if necessary"
814     revert_use_package(document, "cancel", cancel_commands, False)
815
816
817 def revert_verbatim(document, starred = False):
818     " Revert verbatim environments completely to TeX-code. "
819     i = 0
820     consecutive = False
821
822     layout_name = "Verbatim"
823     latex_name  = "verbatim"
824     if starred:
825         layout_name = "Verbatim*"
826         latex_name  = "verbatim*"
827
828     subst_end = ['\\end_layout', '', '\\begin_layout Plain Layout',
829                  '\\end_layout', '',
830                  '\\begin_layout Plain Layout', '', '',
831                  '\\backslash', '',
832                  'end{%s}' % (latex_name),
833                  '\\end_layout', '', '\\end_inset',
834                  '', '', '\\end_layout']
835     subst_begin = ['\\begin_layout Standard', '\\noindent',
836                    '\\begin_inset ERT', 'status open', '',
837                    '\\begin_layout Plain Layout', '', '', '\\backslash',
838                    'begin{%s}' % (latex_name),
839                    '\\end_layout', '', '\\begin_layout Plain Layout', '']
840
841     while 1:
842         i = find_token(document.body, "\\begin_layout %s" % (layout_name), i)
843         if i == -1:
844             return
845         j = find_end_of_layout(document.body, i)
846         if j == -1:
847             document.warning("Malformed LyX document: Can't find end of %s layout" \
848               % (layout_name))
849             i += 1
850             continue
851         # delete all line breaks insets (there are no other insets)
852         l = i
853         while 1:
854             n = find_token(document.body, "\\begin_inset Newline newline", l, j)
855             if n == -1:
856                 n = find_token(document.body, "\\begin_inset Newline linebreak", l, j)
857                 if n == -1:
858                     break
859             m = find_end_of_inset(document.body, n)
860             del(document.body[m:m+1])
861             document.body[n:n+1] = ['\\end_layout', '', '\\begin_layout Plain Layout']
862             l += 1
863             # we deleted a line, so the end of the inset moved forward.
864             # FIXME But we also added some lines, didn't we? I think this
865             # should be j += 1.
866             j -= 1
867         # consecutive verbatim environments need to be connected
868         k = find_token(document.body, "\\begin_layout %s" % (layout_name), j)
869         if k == j + 2 and consecutive == False:
870             consecutive = True
871             document.body[j:j+1] = ['\\end_layout', '', '\\begin_layout Plain Layout']
872             document.body[i:i+1] = subst_begin
873             continue
874         if k == j + 2 and consecutive == True:
875             document.body[j:j+1] = ['\\end_layout', '', '\\begin_layout Plain Layout']
876             del(document.body[i:i+1])
877             continue
878         if k != j + 2 and consecutive == True:
879             document.body[j:j+1] = subst_end
880             # the next paragraph must not be indented
881             # FIXME This seems to be causing problems, because of the
882             # hardcoded use of 19. We should figure out exactly where
883             # this needs to go by searching for the right tag.
884             document.body[j+19:j+19] = ['\\noindent']
885             del(document.body[i:i+1])
886             consecutive = False
887             continue
888         else:
889             document.body[j:j+1] = subst_end
890             # the next paragraph must not be indented
891             # FIXME This seems to be causing problems, because of the
892             # hardcoded use of 19. We should figure out exactly where
893             # this needs to go by searching for the right tag.
894             document.body[j+19:j+19] = ['\\noindent']
895             document.body[i:i+1] = subst_begin
896
897
898 def revert_tipa(document):
899     " Revert native TIPA insets to mathed or ERT. "
900     i = 0
901     while 1:
902         i = find_token(document.body, "\\begin_inset IPA", i)
903         if i == -1:
904             return
905         j = find_end_of_inset(document.body, i)
906         if j == -1:
907             document.warning("Malformed LyX document: Can't find end of IPA inset")
908             i += 1
909             continue
910         Multipar = False
911         n = find_token(document.body, "\\begin_layout", i, j)
912         if n == -1:
913             document.warning("Malformed LyX document: IPA inset has no embedded layout")
914             i += 1
915             continue
916         m = find_end_of_layout(document.body, n)
917         if m == -1:
918             document.warning("Malformed LyX document: Can't find end of embedded layout")
919             i += 1
920             continue
921         content = document.body[n+1:m]
922         p = find_token(document.body, "\\begin_layout", m, j)
923         if p != -1 or len(content) > 1:
924             Multipar = True
925             content = document.body[i+1:j]
926         if Multipar:
927             # IPA insets with multiple pars need to be wrapped by \begin{IPA}...\end{IPA}
928             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}")
929             add_to_preamble(document, ["\\usepackage{tipa,tipx}"])
930         else:
931             # single-par IPA insets can be reverted to mathed
932             document.body[i:j+1] = ["\\begin_inset Formula $\\text{\\textipa{" + content[0] + "}}$", "\\end_inset"]
933         i = j
934
935
936 def revert_cell_rotation(document):
937   "Revert cell rotations to TeX-code"
938
939   load_rotating = False
940   i = 0
941   try:
942     while True:
943       # first, let's find out if we need to do anything
944       i = find_token(document.body, '<cell ', i)
945       if i == -1:
946         return
947       j = document.body[i].find('rotate="')
948       if j != -1:
949         k = document.body[i].find('"', j + 8)
950         value = document.body[i][j + 8 : k]
951         if value == "0":
952           rgx = re.compile(r' rotate="[^"]+?"')
953           # remove rotate option
954           document.body[i] = rgx.sub('', document.body[i])
955         elif value == "90":
956           rgx = re.compile(r' rotate="[^"]+?"')
957           document.body[i] = rgx.sub(' rotate="true"', document.body[i])
958         else:
959           rgx = re.compile(r' rotate="[^"]+?"')
960           load_rotating = True
961           # remove rotate option
962           document.body[i] = rgx.sub('', document.body[i])
963           # write ERT
964           document.body[i + 5 : i + 5] = \
965             put_cmd_in_ert("\\end{turn}")
966           document.body[i + 4 : i + 4] = \
967             put_cmd_in_ert("\\begin{turn}{" + value + "}")
968         
969       i += 1
970         
971   finally:
972     if load_rotating:
973       add_to_preamble(document, ["\\@ifundefined{turnbox}{\usepackage{rotating}}{}"])
974
975
976 def convert_cell_rotation(document):
977     'Convert cell rotation statements from "true" to "90"'
978
979     i = 0
980     while True:
981       # first, let's find out if we need to do anything
982       i = find_token(document.body, '<cell ', i)
983       if i == -1:
984         return
985       j = document.body[i].find('rotate="true"')
986       if j != -1:
987         rgx = re.compile(r'rotate="[^"]+?"')
988         # convert "true" to "90"
989         document.body[i] = rgx.sub('rotate="90"', document.body[i])
990         
991       i += 1
992
993
994 def revert_table_rotation(document):
995   "Revert table rotations to TeX-code"
996
997   load_rotating = False
998   i = 0
999   try:
1000     while True:
1001       # first, let's find out if we need to do anything
1002       i = find_token(document.body, '<features ', i)
1003       if i == -1:
1004         return
1005       j = document.body[i].find('rotate="')
1006       if j != -1:
1007         end_table = find_token(document.body, '</lyxtabular>', j)
1008         k = document.body[i].find('"', j + 8)
1009         value = document.body[i][j + 8 : k]
1010         if value == "0":
1011           rgx = re.compile(r' rotate="[^"]+?"')
1012           # remove rotate option
1013           document.body[i] = rgx.sub('', document.body[i])
1014         elif value == "90":
1015           rgx = re.compile(r'rotate="[^"]+?"')
1016           document.body[i] = rgx.sub('rotate="true"', document.body[i])
1017         else:
1018           rgx = re.compile(r' rotate="[^"]+?"')
1019           load_rotating = True
1020           # remove rotate option
1021           document.body[i] = rgx.sub('', document.body[i])
1022           # write ERT
1023           document.body[end_table + 3 : end_table + 3] = \
1024             put_cmd_in_ert("\\end{turn}")
1025           document.body[i - 2 : i - 2] = \
1026             put_cmd_in_ert("\\begin{turn}{" + value + "}")
1027         
1028       i += 1
1029         
1030   finally:
1031     if load_rotating:
1032       add_to_preamble(document, ["\\@ifundefined{turnbox}{\usepackage{rotating}}{}"])
1033
1034
1035 def convert_table_rotation(document):
1036     'Convert table rotation statements from "true" to "90"'
1037
1038     i = 0
1039     while True:
1040       # first, let's find out if we need to do anything
1041       i = find_token(document.body, '<features ', i)
1042       if i == -1:
1043         return
1044       j = document.body[i].find('rotate="true"')
1045       if j != -1:
1046         rgx = re.compile(r'rotate="[^"]+?"')
1047         # convert "true" to "90"
1048         document.body[i] = rgx.sub('rotate="90"', document.body[i])
1049         
1050       i += 1
1051
1052
1053 def convert_listoflistings(document):
1054     'Convert ERT \lstlistoflistings to TOC lstlistoflistings inset'
1055     # We can support roundtrip because the command is so simple
1056     i = 0
1057     while True:
1058         i = find_token(document.body, "\\begin_inset ERT", i)
1059         if i == -1:
1060             return
1061         j = find_end_of_inset(document.body, i)
1062         if j == -1:
1063             document.warning("Malformed LyX document: Can't find end of ERT inset")
1064             i += 1
1065             continue
1066         ert = get_ert(document.body, i)
1067         if ert == "\\lstlistoflistings{}":
1068             document.body[i:j] = ["\\begin_inset CommandInset toc", "LatexCommand lstlistoflistings", ""]
1069             i = i + 4
1070         else:
1071             i = j + 1
1072
1073
1074 def revert_listoflistings(document):
1075     'Convert TOC lstlistoflistings inset to ERT lstlistoflistings'
1076     i = 0
1077     while True:
1078         i = find_token(document.body, "\\begin_inset CommandInset toc", i)
1079         if i == -1:
1080             return
1081         if document.body[i+1] == "LatexCommand lstlistoflistings":
1082             j = find_end_of_inset(document.body, i)
1083             if j == -1:
1084                 document.warning("Malformed LyX document: Can't find end of TOC inset")
1085                 i += 1
1086                 continue
1087             subst = put_cmd_in_ert("\\lstlistoflistings{}")
1088             document.body[i:j+1] = subst
1089             add_to_preamble(document, ["\\usepackage{listings}"])
1090         i += 1
1091
1092
1093 def convert_use_amssymb(document):
1094     "insert use_package amssymb"
1095     regexp = re.compile(r'(\\use_package\s+amsmath)')
1096     i = find_re(document.header, regexp, 0)
1097     if i == -1:
1098         document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
1099         return;
1100     value = get_value(document.header, "\\use_package" , i).split()[1]
1101     useamsmath = 0
1102     try:
1103         useamsmath = int(value)
1104     except:
1105         document.warning("Invalid \\use_package amsmath: " + value + ". Assuming auto.")
1106         useamsmath = 1
1107     j = find_token(document.preamble, "\\usepackage{amssymb}", 0)
1108     if j == -1:
1109         document.header.insert(i + 1, "\\use_package amssymb %d" % useamsmath)
1110     else:
1111         document.header.insert(i + 1, "\\use_package amssymb 2")
1112         del document.preamble[j]
1113
1114
1115 def revert_use_amssymb(document):
1116     "remove use_package amssymb"
1117     regexp1 = re.compile(r'(\\use_package\s+amsmath)')
1118     regexp2 = re.compile(r'(\\use_package\s+amssymb)')
1119     i = find_re(document.header, regexp1, 0)
1120     j = find_re(document.header, regexp2, 0)
1121     value1 = "1" # default is auto
1122     value2 = "1" # default is auto
1123     if i != -1:
1124         value1 = get_value(document.header, "\\use_package" , i).split()[1]
1125     if j != -1:
1126         value2 = get_value(document.header, "\\use_package" , j).split()[1]
1127         del document.header[j]
1128     if value1 != value2 and value2 == "2": # on
1129         add_to_preamble(document, ["\\usepackage{amssymb}"])
1130
1131
1132 def convert_use_cancel(document):
1133     "insert use_package cancel"
1134     convert_use_package(document, "cancel", cancel_commands, True)
1135
1136
1137 def revert_use_cancel(document):
1138     "remove use_package cancel"
1139     revert_use_package(document, "cancel", cancel_commands, True)
1140
1141
1142 def revert_ancientgreek(document):
1143     "Set the document language for ancientgreek to greek" 
1144
1145     if document.language == "ancientgreek": 
1146         document.language = "greek"
1147         i = find_token(document.header, "\\language", 0) 
1148         if i != -1: 
1149             document.header[i] = "\\language greek" 
1150     j = 0 
1151     while True: 
1152         j = find_token(document.body, "\\lang ancientgreek", j) 
1153         if j == -1:
1154             return
1155         else:
1156             document.body[j] = document.body[j].replace("\\lang ancientgreek", "\\lang greek") 
1157         j += 1
1158
1159
1160 def revert_languages(document):
1161     "Set the document language for new supported languages to English" 
1162
1163     languages = [
1164                  "coptic", "divehi", "hindi", "kurmanji", "lao", "marathi", "occitan", "sanskrit",
1165                  "syriac", "tamil", "telugu", "urdu"
1166                 ]
1167     for n in range(len(languages)):
1168         if document.language == languages[n]:
1169             document.language = "english"
1170             i = find_token(document.header, "\\language", 0) 
1171             if i != -1: 
1172                 document.header[i] = "\\language english" 
1173         j = 0
1174         while j < len(document.body): 
1175             j = find_token(document.body, "\\lang " + languages[n], j)
1176             if j != -1:
1177                 document.body[j] = document.body[j].replace("\\lang " + languages[n], "\\lang english")
1178                 j += 1
1179             else:
1180                 j = len(document.body)
1181
1182
1183 def convert_armenian(document):
1184     "Use polyglossia and thus non-TeX fonts for Armenian" 
1185
1186     if document.language == "armenian": 
1187         i = find_token(document.header, "\\use_non_tex_fonts", 0) 
1188         if i != -1: 
1189             document.header[i] = "\\use_non_tex_fonts true" 
1190
1191
1192 def revert_armenian(document):
1193     "Use ArmTeX and thus TeX fonts for Armenian" 
1194
1195     if document.language == "armenian": 
1196         i = find_token(document.header, "\\use_non_tex_fonts", 0) 
1197         if i != -1: 
1198             document.header[i] = "\\use_non_tex_fonts false" 
1199
1200
1201 def revert_libertine(document):
1202     " Revert native libertine font definition to LaTeX " 
1203
1204     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1205         i = find_token(document.header, "\\font_roman libertine", 0)
1206         if i != -1:
1207             osf = False
1208             j = find_token(document.header, "\\font_osf true", 0)
1209             if j != -1:
1210                 osf = True
1211             preamble = "\\usepackage"
1212             if osf:
1213                 document.header[j] = "\\font_osf false"
1214                 preamble += "[osf]"
1215             else:
1216                 preamble += "[lining]"
1217             preamble += "{libertine-type1}"
1218             add_to_preamble(document, [preamble])
1219             document.header[i] = "\\font_roman default"
1220
1221
1222 def revert_txtt(document):
1223     " Revert native txtt font definition to LaTeX " 
1224
1225     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1226         i = find_token(document.header, "\\font_typewriter txtt", 0)
1227         if i != -1:
1228             preamble = "\\renewcommand{\\ttdefault}{txtt}"
1229             add_to_preamble(document, [preamble])
1230             document.header[i] = "\\font_typewriter default"
1231
1232
1233 def revert_mathdesign(document):
1234     " Revert native mathdesign font definition to LaTeX " 
1235
1236     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1237         mathdesign_dict = {
1238         "mdbch":  "charter",
1239         "mdput":  "utopia",
1240         "mdugm":  "garamond"
1241         }
1242         i = find_token(document.header, "\\font_roman", 0)
1243         if i == -1:
1244             return
1245         val = get_value(document.header, "\\font_roman", i)
1246         if val in list(mathdesign_dict.keys()):
1247             preamble = "\\usepackage[%s" % mathdesign_dict[val]
1248             expert = False
1249             j = find_token(document.header, "\\font_osf true", 0)
1250             if j != -1:
1251                 expert = True
1252                 document.header[j] = "\\font_osf false"
1253             l = find_token(document.header, "\\font_sc true", 0)
1254             if l != -1:
1255                 expert = True
1256                 document.header[l] = "\\font_sc false"
1257             if expert:
1258                 preamble += ",expert"
1259             preamble += "]{mathdesign}"
1260             add_to_preamble(document, [preamble])
1261             document.header[i] = "\\font_roman default"
1262
1263
1264 def revert_texgyre(document):
1265     " Revert native TeXGyre font definition to LaTeX " 
1266
1267     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1268         texgyre_fonts = ["tgadventor", "tgbonum", "tgchorus", "tgcursor", \
1269                          "tgheros", "tgpagella", "tgschola", "tgtermes"]
1270         i = find_token(document.header, "\\font_roman", 0)
1271         if i != -1:
1272             val = get_value(document.header, "\\font_roman", i)
1273             if val in texgyre_fonts:
1274                 preamble = "\\usepackage{%s}" % val
1275                 add_to_preamble(document, [preamble])
1276                 document.header[i] = "\\font_roman default"
1277         i = find_token(document.header, "\\font_sans", 0)
1278         if i != -1:
1279             val = get_value(document.header, "\\font_sans", i)
1280             if val in texgyre_fonts:
1281                 preamble = "\\usepackage{%s}" % val
1282                 add_to_preamble(document, [preamble])
1283                 document.header[i] = "\\font_sans default"
1284         i = find_token(document.header, "\\font_typewriter", 0)
1285         if i != -1:
1286             val = get_value(document.header, "\\font_typewriter", i)
1287             if val in texgyre_fonts:
1288                 preamble = "\\usepackage{%s}" % val
1289                 add_to_preamble(document, [preamble])
1290                 document.header[i] = "\\font_typewriter default"
1291
1292
1293 def revert_ipadeco(document):
1294     " Revert IPA decorations to ERT "
1295     i = 0
1296     while True:
1297       i = find_token(document.body, "\\begin_inset IPADeco", i)
1298       if i == -1:
1299           return
1300       end = find_end_of_inset(document.body, i)
1301       if end == -1:
1302           document.warning("Can't find end of inset at line " + str(i))
1303           i += 1
1304           continue
1305       line = document.body[i]
1306       rx = re.compile(r'\\begin_inset IPADeco (.*)$')
1307       m = rx.match(line)
1308       decotype = m.group(1)
1309       if decotype != "toptiebar" and decotype != "bottomtiebar":
1310           document.warning("Invalid IPADeco type: " + decotype)
1311           i = end
1312           continue
1313       blay = find_token(document.body, "\\begin_layout Plain Layout", i, end)
1314       if blay == -1:
1315           document.warning("Can't find layout for inset at line " + str(i))
1316           i = end
1317           continue
1318       bend = find_end_of_layout(document.body, blay)
1319       if bend == -1:
1320           document.warning("Malformed LyX document: Could not find end of IPADeco inset's layout.")
1321           i = end
1322           continue
1323       substi = ["\\begin_inset ERT", "status collapsed", "",
1324                 "\\begin_layout Plain Layout", "", "", "\\backslash", 
1325                 decotype + "{", "\\end_layout", "", "\\end_inset"]
1326       substj = ["\\size default", "", "\\begin_inset ERT", "status collapsed", "",
1327                 "\\begin_layout Plain Layout", "", "}", "\\end_layout", "", "\\end_inset"]
1328       # do the later one first so as not to mess up the numbering
1329       document.body[bend:end + 1] = substj
1330       document.body[i:blay + 1] = substi
1331       i = end + len(substi) + len(substj) - (end - bend) - (blay - i) - 2
1332       add_to_preamble(document, "\\usepackage{tipa}")
1333
1334
1335 def revert_ipachar(document):
1336     ' Revert \\IPAChar to ERT '
1337     i = 0
1338     found = False
1339     while i < len(document.body):
1340         m = re.match(r'(.*)\\IPAChar \\(\w+\{\w+\})(.*)', document.body[i])
1341         if m:
1342             found = True
1343             before = m.group(1)
1344             ipachar = m.group(2)
1345             after = m.group(3)
1346             subst = [before,
1347                      '\\begin_inset ERT',
1348                      'status collapsed', '',
1349                      '\\begin_layout Standard',
1350                      '', '', '\\backslash',
1351                      ipachar,
1352                      '\\end_layout', '',
1353                      '\\end_inset', '',
1354                      after]
1355             document.body[i: i+1] = subst
1356             i = i + len(subst)
1357         else:
1358             i += 1
1359     if found:
1360         add_to_preamble(document, "\\usepackage{tone}")
1361
1362
1363 def revert_minionpro(document):
1364     " Revert native MinionPro font definition to LaTeX " 
1365
1366     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1367         i = find_token(document.header, "\\font_roman minionpro", 0)
1368         if i != -1:
1369             osf = False
1370             j = find_token(document.header, "\\font_osf true", 0)
1371             if j != -1:
1372                 osf = True
1373             preamble = "\\usepackage"
1374             if osf:
1375                 document.header[j] = "\\font_osf false"
1376             else:
1377                 preamble += "[lf]"
1378             preamble += "{MinionPro}"
1379             add_to_preamble(document, [preamble])
1380             document.header[i] = "\\font_roman default"
1381
1382
1383 def revert_mathfonts(document):
1384     " Revert native math font definitions to LaTeX " 
1385
1386     i = find_token(document.header, "\\font_math", 0)
1387     if i == -1:
1388        return
1389     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1390         val = get_value(document.header, "\\font_math", i)
1391         if val == "eulervm":
1392             add_to_preamble(document, "\\usepackage{eulervm}")
1393         elif val == "default":
1394             mathfont_dict = {
1395             "lmodern":  "\\renewcommand{\\rmdefault}{lmr}",
1396             "minionpro":  "\\usepackage[onlytext,lf]{MinionPro}",
1397             "minionpro-osf":  "\\usepackage[onlytext]{MinionPro}",
1398             "palatino":  "\\renewcommand{\\rmdefault}{ppl}",
1399             "palatino-osf":  "\\renewcommand{\\rmdefault}{pplj}",
1400             "times":  "\\renewcommand{\\rmdefault}{ptm}",
1401             "utopia":  "\\renewcommand{\\rmdefault}{futs}",
1402             "utopia-osf":  "\\renewcommand{\\rmdefault}{futj}",
1403             }
1404             j = find_token(document.header, "\\font_roman", 0)
1405             if j != -1:
1406                 rm = get_value(document.header, "\\font_roman", j)
1407                 k = find_token(document.header, "\\font_osf true", 0)
1408                 if k != -1:
1409                     rm += "-osf"
1410                 if rm in list(mathfont_dict.keys()):
1411                     add_to_preamble(document, mathfont_dict[rm])
1412                     document.header[j] = "\\font_roman default"
1413                     if k != -1:
1414                         document.header[k] = "\\font_osf false"
1415     del document.header[i]
1416
1417
1418 def revert_mdnomath(document):
1419     " Revert mathdesign and fourier without math " 
1420
1421     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1422         mathdesign_dict = {
1423         "md-charter": "mdbch",
1424         "md-utopia": "mdput",
1425         "md-garamond": "mdugm"
1426         }
1427         i = find_token(document.header, "\\font_roman", 0)
1428         if i == -1:
1429             return
1430         val = get_value(document.header, "\\font_roman", i)
1431         if val in list(mathdesign_dict.keys()):
1432             j = find_token(document.header, "\\font_math", 0)
1433             if j == -1:
1434                 document.header[i] = "\\font_roman %s" % mathdesign_dict[val]
1435             mval = get_value(document.header, "\\font_math", j)
1436             if mval == "default":
1437                 document.header[i] = "\\font_roman default"
1438                 add_to_preamble(document, "\\renewcommand{\\rmdefault}{%s}" % mathdesign_dict[val])
1439             else:
1440                 document.header[i] = "\\font_roman %s" % mathdesign_dict[val]
1441
1442
1443 def convert_mathfonts(document):
1444     document.header.insert(-1, "\\font_math auto")
1445
1446
1447 def convert_mdnomath(document):
1448     " Change mathdesign font name " 
1449
1450     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1451         mathdesign_dict = {
1452         "mdbch":  "md-charter",
1453         "mdput":  "md-utopia",
1454         "mdugm":  "md-garamond"
1455         }
1456         i = find_token(document.header, "\\font_roman", 0)
1457         if i == -1:
1458             return
1459         val = get_value(document.header, "\\font_roman", i)
1460         if val in list(mathdesign_dict.keys()):
1461              document.header[i] = "\\font_roman %s" % mathdesign_dict[val]
1462
1463
1464 def revert_newtxmath(document):
1465     " Revert native newtxmath definitions to LaTeX " 
1466
1467     i = find_token(document.header, "\\font_math", 0)
1468     if i == -1:
1469        return
1470     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1471         val = get_value(document.header, "\\font_math", i)
1472         mathfont_dict = {
1473         "libertine-ntxm":  "\\usepackage[libertine]{newtxmath}",
1474         "minion-ntxm":  "\\usepackage[minion]{newtxmath}",
1475         "newtxmath":  "\\usepackage{newtxmath}",
1476         }
1477         if val in list(mathfont_dict.keys()):
1478             add_to_preamble(document, mathfont_dict[val])
1479             document.header[i] = "\\font_math auto"
1480
1481
1482 def revert_biolinum(document):
1483     " Revert native biolinum font definition to LaTeX " 
1484
1485     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
1486         i = find_token(document.header, "\\font_sans biolinum", 0)
1487         if i != -1:
1488             osf = False
1489             j = find_token(document.header, "\\font_osf true", 0)
1490             if j != -1:
1491                 osf = True
1492             preamble = "\\usepackage"
1493             if not osf:
1494                 preamble += "[lf]"
1495             preamble += "{biolinum-type1}"
1496             add_to_preamble(document, [preamble])
1497             document.header[i] = "\\font_sans default"
1498
1499
1500 def revert_uop(document):
1501     " Revert native URW Classico (Optima) font definition to LaTeX "
1502
1503     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1504         i = find_token(document.header, "\\font_sans uop", 0)
1505         if i != -1:
1506                 preamble = "\\renewcommand{\\sfdefault}{uop}"
1507                 add_to_preamble(document, [preamble])
1508                 document.header[i] = "\\font_sans default"
1509
1510
1511 def convert_latexargs(document):
1512     " Convert InsetArgument to new syntax "
1513
1514     if find_token(document.body, "\\begin_inset Argument", 0) == -1:
1515         # nothing to do.
1516         return
1517
1518     # A list of layouts (document classes) with only optional or no arguments.
1519     # These can be safely converted to the new syntax
1520     # (I took the liberty to add some of my personal layouts/modules here; JSP)
1521     safe_layouts = ["aa", "aapaper", "aastex", "achemso", "acmsiggraph", "AEA",
1522                     "agu-dtd", "agums", "agutex", "amsart", "amsbook", "apa",
1523                     "arab-article", "armenian-article", "article-beamer", "article",
1524                     "beamer", "book", "broadway", "chess", "cl2emult", "ctex-article",
1525                     "ctex-book", "ctex-report", "dinbrief", "docbook-book", "docbook-chapter",
1526                     "docbook", "docbook-section", "doublecol-new", "dtk", "ectaart", "egs",
1527                     "elsarticle", "elsart", "entcs", "europecv", "extarticle", "extbook",
1528                     "extletter", "extreport", "foils", "frletter", "g-brief2", "g-brief",
1529                     "heb-article", "heb-letter", "hollywood", "IEEEtran", "ijmpc", "ijmpd",
1530                     "iopart", "isprs", "jarticle", "jasatex", "jbook", "jgrga", "jreport",
1531                     "jsarticle", "jsbeamer", "jsbook", "jss", "kluwer", "latex8", "letter", "lettre",
1532                     "literate-article", "literate-book", "literate-report", "llncs", "ltugboat",
1533                     "memoir", "moderncv", "mwart", "mwbk", "mwrep", "paper", "powerdot",
1534                     "recipebook", "report", "revtex4", "revtex", "scrartcl", "scrarticle-beamer",
1535                     "scrbook", "scrlettr", "scrlttr2", "scrreprt", "seminar", "siamltex",
1536                     "sigplanconf", "simplecv", "singlecol", "singlecol-new", "slides", "spie",
1537                     "svglobal3", "svglobal", "svjog", "svmono", "svmult", "svprobth", "tarticle",
1538                     "tbook", "treport", "tufte-book", "tufte-handout"]
1539     # A list of "safe" modules, same as above
1540     safe_modules = ["biblatex", "beameraddons", "beamer-resenumerate", "beamersession", "braille",
1541                     "customHeadersFooters", "endnotes", "enumitem", "eqs-within-sections", "figs-within-sections",
1542                     "fix-cm", "fixltx2e", "foottoend", "hanging", "jscharstyles", "knitr", "lilypond",
1543                     "linguistics", "linguisticx", "logicalmkup", "minimalistic", "nomindex", "noweb",
1544                     "pdfcomment", "sweave", "tabs-within-sections", "theorems-ams-bytype",
1545                     "theorems-ams-extended-bytype", "theorems-ams-extended", "theorems-ams", "theorems-bytype",
1546                     "theorems-chap-bytype", "theorems-chap", "theorems-named", "theorems-sec-bytype",
1547                     "theorems-sec", "theorems-starred", "theorems-std", "todonotes"]
1548     # Modules we need to take care of
1549     caveat_modules = ["initials"]
1550     # information about the relevant styles in caveat_modules (number of opt and req args)
1551     # use this if we get more caveat_modules. For now, use hard coding (see below).
1552     # initials = [{'Layout' : 'Initial', 'opt' : 1, 'req' : 1}]
1553
1554     # Is this a known safe layout?
1555     safe_layout = document.textclass in safe_layouts
1556     if not safe_layout:
1557         document.warning("Lyx2lyx knows nothing about textclass '%s'. "
1558                          "Please check if short title insets have been converted correctly."
1559                          % document.textclass)
1560     # Do we use unsafe or unknown modules
1561     mods = document.get_module_list()
1562     unknown_modules = False
1563     used_caveat_modules = list()
1564     for mod in mods:
1565         if mod in safe_modules:
1566             continue
1567         if mod in caveat_modules:
1568             used_caveat_modules.append(mod)
1569             continue
1570         unknown_modules = True
1571         document.warning("Lyx2lyx knows nothing about module '%s'. "
1572                          "Please check if short title insets have been converted correctly."
1573                          % mod)
1574
1575     i = 0
1576     while True:
1577         i = find_token(document.body, "\\begin_inset Argument", i)
1578         if i == -1:
1579             return
1580
1581         if not safe_layout or unknown_modules:
1582             # We cannot do more here since we have no access to this layout.
1583             # InsetArgument itself will do the real work
1584             # (see InsetArgument::updateBuffer())
1585             document.body[i] = "\\begin_inset Argument 999"
1586             i += 1
1587             continue
1588         
1589         # Find containing paragraph layout
1590         parent = get_containing_layout(document.body, i)
1591         if parent == False:
1592             document.warning("Malformed LyX document: Can't find parent paragraph layout")
1593             i += 1
1594             continue
1595         parbeg = parent[1]
1596         parend = parent[2]
1597         allowed_opts = -1
1598         first_req = -1
1599         if len(used_caveat_modules) > 0:
1600             # We know for now that this must be the initials module with the Initial layout
1601             # If we get more such modules, we need some automating.
1602             if parent[0] == "Initial":
1603                 # Layout has 1 opt and 1 req arg.
1604                 # Count the actual arguments
1605                 actualargs = 0
1606                 for p in range(parbeg, parend):
1607                     if document.body[p] == "\\begin_inset Argument":
1608                         actualargs += 1
1609                 if actualargs == 1:
1610                     allowed_opts = 0
1611                     first_req = 2
1612         # Collect all arguments in this paragraph
1613         argnr = 0
1614         for p in range(parbeg, parend):
1615             if document.body[p] == "\\begin_inset Argument":
1616                 argnr += 1
1617                 if allowed_opts != -1:
1618                     # We have less arguments than opt + required.
1619                     # required must take precedence.
1620                     if argnr > allowed_opts and argnr < first_req:
1621                         argnr = first_req
1622                 document.body[p] = "\\begin_inset Argument %d" % argnr
1623         i = parend + 1
1624
1625
1626 def revert_latexargs(document):
1627     " Revert InsetArgument to old syntax "
1628
1629     i = 0
1630     rx = re.compile(r'^\\begin_inset Argument (\d+)$')
1631     args = dict()
1632     while True:
1633         # Search for Argument insets
1634         i = find_token(document.body, "\\begin_inset Argument", i)
1635         if i == -1:
1636             return
1637         m = rx.match(document.body[i])
1638         if not m:
1639             # No ID: inset already reverted
1640             i += 1
1641             continue
1642         # Find containing paragraph layout
1643         parent = get_containing_layout(document.body, i)
1644         if parent == False:
1645             document.warning("Malformed LyX document: Can't find parent paragraph layout")
1646             i += 1
1647             continue
1648         parbeg = parent[1]
1649         parend = parent[2]
1650         # Do not set realparbeg to parent[3], since this does not work if we
1651         # have another inset (e.g. label or index) before the first argument
1652         # inset (this is the case in the user guide of LyX 2.0.8)
1653         realparbeg = -1
1654         # Collect all arguments in this paragraph
1655         realparend = parend
1656         for p in range(parbeg, parend):
1657             m = rx.match(document.body[p])
1658             if m:
1659                 if realparbeg < 0:
1660                     # This is the first argument inset
1661                     realparbeg = p
1662                 val = int(m.group(1))
1663                 j = find_end_of_inset(document.body, p)
1664                 # Revert to old syntax
1665                 document.body[p] = "\\begin_inset Argument"
1666                 if j == -1:
1667                     document.warning("Malformed LyX document: Can't find end of Argument inset")
1668                     continue
1669                 if val > 0:
1670                     args[val] = document.body[p : j + 1]
1671                 # Adjust range end
1672                 realparend = realparend - len(document.body[p : j + 1])
1673                 # Remove arg inset at this position
1674                 del document.body[p : j + 1]
1675             if p >= realparend:
1676                 break
1677         if realparbeg < 0:
1678             # No argument inset found
1679             realparbeg = parent[3]
1680         # Now sort the arg insets
1681         subst = []
1682         for f in sorted(args):
1683             subst += args[f]
1684             del args[f]
1685         # Insert the sorted arg insets at paragraph begin
1686         document.body[realparbeg : realparbeg] = subst
1687
1688         i = realparbeg + 1 + len(subst)
1689
1690
1691 def revert_IEEEtran(document):
1692   '''
1693   Reverts InsetArgument of
1694   Page headings
1695   Biography
1696   Biography without photo
1697   to TeX-code
1698   '''
1699   if document.textclass == "IEEEtran":
1700     i = 0
1701     i2 = 0
1702     j = 0
1703     k = 0
1704     while True:
1705       if i != -1:
1706         i = find_token(document.body, "\\begin_layout Page headings", i)
1707       if i != -1:
1708         revert_Argument_to_TeX_brace(document, i, 0, 1, 1, False, False)
1709         i += 1
1710       if i2 != -1:
1711         i2 = find_token(document.body, "\\begin_inset Flex Paragraph Start", i2)
1712       if i2 != -1:
1713         revert_Argument_to_TeX_brace(document, i2, 0, 1, 1, False, False)
1714         i2 = i2 + 1
1715       if j != -1:
1716         j = find_token(document.body, "\\begin_layout Biography without photo", j)
1717       if j != -1:
1718         revert_Argument_to_TeX_brace(document, j, 0, 1, 1, True, False)
1719         j += 1
1720       if k != -1:
1721         k = find_token(document.body, "\\begin_layout Biography", k)
1722         kA = find_token(document.body, "\\begin_layout Biography without photo", k)
1723         if k == kA and k != -1:
1724           k += 1
1725           continue
1726       if k != -1:
1727         # start with the second argument, therefore 2
1728         revert_Argument_to_TeX_brace(document, k, 0, 2, 2, True, False)
1729         k += 1
1730       if i == -1 and i2 == -1 and j == -1 and k == -1:
1731         return
1732
1733
1734 def revert_IEEEtran_2(document):
1735   '''
1736   Reverts Flex Paragraph Start to TeX-code
1737   '''
1738   if document.textclass == "IEEEtran":
1739     begin = 0
1740     while True:
1741       begin = find_token(document.body, "\\begin_inset Flex Paragraph Start", begin)
1742       if begin == -1:
1743         return
1744       end1 = find_end_of_inset(document.body, begin)
1745       document.body[end1 - 2 : end1 + 1] = put_cmd_in_ert("}")
1746       document.body[begin : begin + 4] = put_cmd_in_ert("\\IEEEPARstart{")
1747       begin = begin + 5
1748
1749
1750 def convert_IEEEtran(document):
1751   '''
1752   Converts ERT of
1753   Page headings
1754   Biography
1755   Biography without photo
1756   to InsetArgument
1757   '''
1758   if document.textclass == "IEEEtran":
1759     i = 0
1760     j = 0
1761     k = 0
1762     while True:
1763       if i != -1:
1764         i = find_token(document.body, "\\begin_layout Page headings", i)
1765       if i != -1:
1766         convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
1767         i += 1
1768       if j != -1:
1769         j = find_token(document.body, "\\begin_layout Biography without photo", j)
1770       if j != -1:
1771         convert_TeX_brace_to_Argument(document, j, 1, 1, False, True, False)
1772         j += 1
1773       if k != -1:
1774         # assure that we don't handle Biography Biography without photo
1775         k = find_token(document.body, "\\begin_layout Biography", k)
1776         kA = find_token(document.body, "\\begin_layout Biography without photo", k - 1)
1777       if k == kA and k != -1:
1778         k += 1
1779         continue
1780       if k != -1:
1781         # the argument we want to convert is the second one
1782         convert_TeX_brace_to_Argument(document, k, 2, 2, False, True, False)
1783         k += 1
1784       if i == -1 and j == -1 and k == -1:
1785         return
1786
1787
1788 def revert_AASTeX(document):
1789   " Reverts InsetArgument of Altaffilation to TeX-code "
1790   if document.textclass == "aastex":
1791     i = 0
1792     while True:
1793       i = find_token(document.body, "\\begin_layout Altaffilation", i)
1794       if i == -1:
1795         return
1796       revert_Argument_to_TeX_brace(document, i, 0, 1, 1, False, False)
1797       i += 1
1798
1799
1800 def convert_AASTeX(document):
1801   " Converts ERT of Altaffilation to InsetArgument "
1802   if document.textclass == "aastex":
1803     i = 0
1804     while True:
1805       i = find_token(document.body, "\\begin_layout Altaffilation", i)
1806       if i == -1:
1807         return
1808       convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
1809       i += 1
1810
1811
1812 def revert_AGUTeX(document):
1813   " Reverts InsetArgument of Author affiliation to TeX-code "
1814   if document.textclass == "agutex":
1815     i = 0
1816     while True:
1817       i = find_token(document.body, "\\begin_layout Author affiliation", i)
1818       if i == -1:
1819         return
1820       revert_Argument_to_TeX_brace(document, i, 0, 1, 1, False, False)
1821       i += 1
1822
1823
1824 def convert_AGUTeX(document):
1825   " Converts ERT of Author affiliation to InsetArgument "
1826   if document.textclass == "agutex":
1827     i = 0
1828     while True:
1829       i = find_token(document.body, "\\begin_layout Author affiliation", i)
1830       if i == -1:
1831         return
1832       convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
1833       i += 1
1834
1835
1836 def revert_IJMP(document):
1837   " Reverts InsetArgument of MarkBoth to TeX-code "
1838   if document.textclass == "ijmpc" or document.textclass == "ijmpd":
1839     i = 0
1840     while True:
1841       i = find_token(document.body, "\\begin_layout MarkBoth", i)
1842       if i == -1:
1843         return
1844       revert_Argument_to_TeX_brace(document, i, 0, 1, 1, False, False)
1845       i += 1
1846
1847
1848 def convert_IJMP(document):
1849   " Converts ERT of MarkBoth to InsetArgument "
1850   if document.textclass == "ijmpc" or document.textclass == "ijmpd":
1851     i = 0
1852     while True:
1853       i = find_token(document.body, "\\begin_layout MarkBoth", i)
1854       if i == -1:
1855         return
1856       convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
1857       i += 1
1858
1859
1860 def revert_SIGPLAN(document):
1861   " Reverts InsetArguments of SIGPLAN to TeX-code "
1862   if document.textclass == "sigplanconf":
1863     i = 0
1864     j = 0
1865     while True:
1866       if i != -1:
1867         i = find_token(document.body, "\\begin_layout Conference", i)
1868       if i != -1:
1869         revert_Argument_to_TeX_brace(document, i, 0, 1, 1, False, False)
1870         i += 1
1871       if j != -1:
1872         j = find_token(document.body, "\\begin_layout Author", j)
1873       if j != -1:
1874         revert_Argument_to_TeX_brace(document, j, 0, 1, 2, False, False)
1875         j += 1
1876       if i == -1 and j == -1:
1877         return
1878
1879
1880 def convert_SIGPLAN(document):
1881   " Converts ERT of SIGPLAN to InsetArgument "
1882   if document.textclass == "sigplanconf":
1883     i = 0
1884     j = 0
1885     while True:
1886       if i != -1:
1887         i = find_token(document.body, "\\begin_layout Conference", i)
1888       if i != -1:
1889         convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
1890         i += 1
1891       if j != -1:
1892         j = find_token(document.body, "\\begin_layout Author", j)
1893       if j != -1:
1894         convert_TeX_brace_to_Argument(document, j, 1, 2, False, False, False)
1895         j += 1
1896       if i == -1 and j == -1:
1897         return
1898
1899
1900 def revert_SIGGRAPH(document):
1901   " Reverts InsetArgument of Flex CRcat to TeX-code "
1902   if document.textclass == "acmsiggraph":
1903     i = 0
1904     while True:
1905       i = find_token(document.body, "\\begin_inset Flex CRcat", i)
1906       if i == -1:
1907         return
1908       revert_Argument_to_TeX_brace(document, i, 0, 1, 3, False, False)
1909       i += 1
1910
1911
1912 def convert_SIGGRAPH(document):
1913   " Converts ERT of Flex CRcat to InsetArgument "
1914   if document.textclass == "acmsiggraph":
1915     i = 0
1916     while True:
1917       i = find_token(document.body, "\\begin_inset Flex CRcat", i)
1918       if i == -1:
1919         return
1920       convert_TeX_brace_to_Argument(document, i, 1, 3, True, False, False)
1921       i += 1
1922
1923
1924 def revert_EuropeCV(document):
1925   " Reverts InsetArguments of europeCV to TeX-code "
1926   if document.textclass == "europecv":
1927     i = 0
1928     j = 0
1929     k = 0
1930     m = 0
1931     while True:
1932       if i != -1:
1933         i = find_token(document.body, "\\begin_layout Item", i)
1934       if i != -1:
1935         revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False)
1936         i += 1
1937       if j != -1:
1938         j = find_token(document.body, "\\begin_layout BulletedItem", j)
1939       if j != -1:
1940         revert_Argument_to_TeX_brace(document, j, 0, 2, 2, False, False)
1941         j += 1
1942       if k != -1:
1943         k = find_token(document.body, "\\begin_layout Language", k)
1944       if k != -1:
1945         revert_Argument_to_TeX_brace(document, k, 0, 2, 6, False, False)
1946         k += 1
1947       if m != -1:
1948         m = find_token(document.body, "\\begin_layout LastLanguage", m)
1949       if m != -1:
1950         revert_Argument_to_TeX_brace(document, m, 0, 2, 6, False, False)
1951         m += 1
1952       if i == -1 and j == -1 and k == -1 and m == -1:
1953         return
1954
1955
1956 def convert_EuropeCV(document):
1957   " Converts ERT of europeCV to InsetArgument "
1958   if document.textclass == "europecv":
1959     i = 0
1960     j = 0
1961     k = 0
1962     m = 0
1963     while True:
1964       if i != -1:
1965         i = find_token(document.body, "\\begin_layout Item", i)
1966       if i != -1:
1967         convert_TeX_brace_to_Argument(document, i, 2, 2, False, False, False)
1968         i += 1
1969       if j != -1:
1970         j = find_token(document.body, "\\begin_layout BulletedItem", j)
1971       if j != -1:
1972         convert_TeX_brace_to_Argument(document, j, 2, 2, False, False, False)
1973         j += 1
1974       if k != -1:
1975         k = find_token(document.body, "\\begin_layout Language", k)
1976       if k != -1:
1977         convert_TeX_brace_to_Argument(document, k, 2, 6, False, False, False)
1978         k += 1
1979       if m != -1:
1980         m = find_token(document.body, "\\begin_layout LastLanguage", m)
1981       if m != -1:
1982         convert_TeX_brace_to_Argument(document, m, 2, 6, False, False, False)
1983         m += 1
1984       if i == -1 and j == -1 and k == -1 and m == -1:
1985         return
1986
1987
1988 def revert_ModernCV(document):
1989   " Reverts InsetArguments of modernCV to TeX-code "
1990   if document.textclass == "moderncv":
1991     j = 0
1992     k = 0
1993     m = 0
1994     o = 0
1995     p = 0
1996     while True:
1997       if j != -1:
1998         j = find_token(document.body, "\\begin_layout Entry", j)
1999       if j != -1:
2000         revert_Argument_to_TeX_brace(document, j, 0, 1, 5, False, False)
2001         j += 1
2002       if k != -1:
2003         k = find_token(document.body, "\\begin_layout Item", k)
2004       if k != -1:
2005         revert_Argument_to_TeX_brace(document, k, 0, 1, 1, False, False)
2006         k += 1
2007       if m != -1:
2008         m = find_token(document.body, "\\begin_layout ItemWithComment", m)
2009       if m != -1:
2010         revert_Argument_to_TeX_brace(document, m, 0, 1, 2, False, False)
2011         document.body[m] = document.body[m].replace("\\begin_layout ItemWithComment", "\\begin_layout Language")
2012         m += 1
2013       if o != -1:
2014         o = find_token(document.body, "\\begin_layout DoubleItem", o)
2015       if o != -1:
2016         revert_Argument_to_TeX_brace(document, o, 0, 1, 3, False, False)
2017         document.body[o] = document.body[o].replace("\\begin_layout DoubleItem", "\\begin_layout Computer")
2018         o = o + 1
2019       if p != -1:
2020         p = find_token(document.body, "\\begin_layout Social", p)
2021       if p != -1:
2022         revert_Argument_to_TeX_brace(document, p, 0, 1, 1, False, True)
2023         p = p + 1
2024       if j == -1 and k == -1 and m == -1 and o == -1 and p == -1:
2025         return
2026
2027
2028 def revert_ModernCV_2(document):
2029   " Reverts the Flex:Column inset of modernCV to TeX-code "
2030   if document.textclass == "moderncv":
2031     flex = 0
2032     flexEnd = -1
2033     while True:
2034       flex = find_token(document.body, "\\begin_inset Flex Column", flex)
2035       if flex == -1:
2036         return flexEnd
2037       flexEnd = find_end_of_inset(document.body, flex)
2038       wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True)
2039       revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False)
2040       flexEnd = find_end_of_inset(document.body, flex)
2041       if wasOpt == True:
2042         document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\cvcolumn")
2043       else:
2044         document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\cvcolumn{")
2045       document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
2046       flex += 1
2047
2048
2049 def revert_ModernCV_3(document):
2050   " Reverts the Column style of modernCV to TeX-code "
2051   if document.textclass == "moderncv":
2052     # revert the layouts
2053     revert_ModernCV(document)
2054     p = 0
2055     # get the position of the end of the last column inset
2056     LastFlexEnd = revert_ModernCV_2(document)
2057     while True:
2058       p = find_token(document.body, "\\begin_layout Columns", p)
2059       if p == -1:
2060         return
2061       pEnd = find_end_of_layout(document.body, p)
2062       document.body[p] = document.body[p].replace("\\begin_layout Columns", "\\begin_layout Standard")
2063       if LastFlexEnd != -1:
2064         document.body[p + 1 : p + 1] = put_cmd_in_ert("\\begin{cvcolumns}")
2065         document.body[LastFlexEnd + 24 : LastFlexEnd + 24] = put_cmd_in_ert("\\end{cvcolumns}")
2066       p += 1
2067
2068
2069 def revert_ModernCV_4(document):
2070   " Reverts the style Social to TeX-code "
2071   if document.textclass == "moderncv":
2072     # revert the layouts
2073     revert_ModernCV(document)
2074     p = 0
2075     while True:
2076       p = find_token(document.body, "\\begin_layout Social", p)
2077       if p == -1:
2078         return
2079       pEnd = find_end_of_layout(document.body, p)
2080       document.body[p] = document.body[p].replace("\\begin_layout Social", "\\begin_layout Standard")
2081       document.body[p + 1 : p + 1] = put_cmd_in_ert("\\social")
2082       hasOpt = find_token(document.body, "[", p + 9)
2083       if hasOpt < p + 18:
2084         document.body[p + 30 : p + 30] = put_cmd_in_ert("{")
2085         document.body[p + 41 : p + 41] = put_cmd_in_ert("}")
2086       else:
2087         document.body[p + 11 : p + 11] = put_cmd_in_ert("{")
2088         document.body[p + 21 : p + 21] = put_cmd_in_ert("}")
2089       p += 1
2090
2091
2092 def convert_ModernCV(document):
2093   " Converts ERT of modernCV to InsetArgument "
2094   if document.textclass == "moderncv":
2095     i = 0
2096     j = 0
2097     k = 0
2098     m = 0
2099     o = 0
2100     while True:
2101       if i != -1:
2102         i = find_token(document.body, "\\begin_layout DoubleItem", i)
2103       if i != -1:
2104         convert_TeX_brace_to_Argument(document, i, 1, 1, False, False, False)
2105         document.body[o] = document.body[o].replace("\\begin_layout DoubleItem", "\\begin_layout DoubleListItem")
2106         i += 1
2107       if j != -1:
2108         j = find_token(document.body, "\\begin_layout Entry", j)
2109       if j != -1:
2110         convert_TeX_brace_to_Argument(document, j, 1, 5, False, False, False)
2111         j += 1
2112       if k != -1:
2113         k = find_token(document.body, "\\begin_layout Item", k)
2114       if k != -1:
2115         convert_TeX_brace_to_Argument(document, k, 1, 1, False, False, False)
2116         k += 1
2117       if m != -1:
2118         m = find_token(document.body, "\\begin_layout Language", m)
2119       if m != -1:
2120         convert_TeX_brace_to_Argument(document, m, 1, 2, False, False, False)
2121         m += 1
2122       if i == -1 and j == -1 and k == -1 and m == -1:
2123         return
2124
2125
2126 def revert_Initials(document):
2127   " Reverts InsetArgument of Initial to TeX-code "
2128   i = 0
2129   while True:
2130     i = find_token(document.body, "\\begin_layout Initial", i)
2131     if i == -1:
2132       return
2133     # first arg (optional) and second arg (first mandatory) are supported in LyX 2.0.x
2134     revert_Argument_to_TeX_brace(document, i, 0, 3, 3, False, False)
2135     i += 1
2136
2137
2138 def convert_Initials(document):
2139   " Converts ERT of Initial to InsetArgument "
2140   i = 0
2141   while True:
2142     i = find_token(document.body, "\\begin_layout Initial", i)
2143     if i == -1:
2144       return
2145     convert_TeX_brace_to_Argument(document, i, 3, 3, False, False, False)
2146     i += 1
2147
2148
2149 def revert_literate(document):
2150     " Revert Literate document to old format "
2151     if del_token(document.header, "noweb", 0):
2152       document.textclass = "literate-" + document.textclass
2153       i = 0
2154       while True:
2155         i = find_token(document.body, "\\begin_layout Chunk", i)
2156         if i == -1:
2157           break
2158         document.body[i] = "\\begin_layout Scrap"
2159         i += 1
2160
2161
2162 def convert_literate(document):
2163     " Convert Literate document to new format"
2164     i = find_token(document.header, "\\textclass", 0)    
2165     if (i != -1) and "literate-" in document.header[i]:
2166       document.textclass = document.header[i].replace("\\textclass literate-", "")
2167       j = find_token(document.header, "\\begin_modules", 0)
2168       if (j != -1):
2169         document.header.insert(j + 1, "noweb")
2170       else:
2171         document.header.insert(i + 1, "\\end_modules")
2172         document.header.insert(i + 1, "noweb")
2173         document.header.insert(i + 1, "\\begin_modules")
2174       i = 0
2175       while True:
2176         i = find_token(document.body, "\\begin_layout Scrap", i)
2177         if i == -1:
2178           break
2179         document.body[i] = "\\begin_layout Chunk"
2180         i += 1
2181
2182
2183 def revert_itemargs(document):
2184     " Reverts \\item arguments to TeX-code "
2185     i = 0
2186     while True:
2187         i = find_token(document.body, "\\begin_inset Argument item:", i)
2188         if i == -1:
2189             return
2190         j = find_end_of_inset(document.body, i)
2191         # Find containing paragraph layout
2192         parent = get_containing_layout(document.body, i)
2193         if parent == False:
2194             document.warning("Malformed LyX document: Can't find parent paragraph layout")
2195             i += 1
2196             continue
2197         parbeg = parent[3]
2198         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2199         endPlain = find_end_of_layout(document.body, beginPlain)
2200         content = document.body[beginPlain + 1 : endPlain]
2201         del document.body[i:j+1]
2202         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
2203         document.body[parbeg : parbeg] = subst
2204         i += 1
2205
2206
2207 def revert_garamondx_newtxmath(document):
2208     " Revert native garamond newtxmath definition to LaTeX " 
2209
2210     i = find_token(document.header, "\\font_math", 0)
2211     if i == -1:
2212        return
2213     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
2214         val = get_value(document.header, "\\font_math", i)
2215         if val == "garamondx-ntxm":
2216             add_to_preamble(document, "\\usepackage[garamondx]{newtxmath}")
2217             document.header[i] = "\\font_math auto"
2218
2219
2220 def revert_garamondx(document):
2221     " Revert native garamond font definition to LaTeX " 
2222
2223     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
2224         i = find_token(document.header, "\\font_roman garamondx", 0)
2225         if i != -1:
2226             osf = False
2227             j = find_token(document.header, "\\font_osf true", 0)
2228             if j != -1:
2229                 osf = True
2230             preamble = "\\usepackage"
2231             if osf:
2232                 preamble += "[osfI]"
2233             preamble += "{garamondx}"
2234             add_to_preamble(document, [preamble])
2235             document.header[i] = "\\font_roman default"
2236
2237
2238 def convert_beamerargs(document):
2239     " Converts beamer arguments to new layout "
2240     
2241     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2242     if document.textclass not in beamer_classes:
2243         return
2244
2245     shifted_layouts = ["Part", "Section", "Subsection", "Subsubsection"]
2246     list_layouts = ["Itemize", "Enumerate", "Description"]
2247     rx = re.compile(r'^\\begin_inset Argument (\d+)$')
2248
2249     i = 0
2250     while True:
2251         i = find_token(document.body, "\\begin_inset Argument", i)
2252         if i == -1:
2253             return
2254         # Find containing paragraph layout
2255         parent = get_containing_layout(document.body, i)
2256         if parent == False:
2257             document.warning("Malformed LyX document: Can't find parent paragraph layout")
2258             i += 1
2259             continue
2260         parbeg = parent[1]
2261         parend = parent[2]
2262         layoutname = parent[0]
2263         for p in range(parbeg, parend):
2264             if layoutname in shifted_layouts:
2265                 m = rx.match(document.body[p])
2266                 if m:
2267                     argnr = int(m.group(1))
2268                     argnr += 1
2269                     document.body[p] = "\\begin_inset Argument %d" % argnr
2270             if layoutname == "AgainFrame":
2271                 m = rx.match(document.body[p])
2272                 if m:
2273                     document.body[p] = "\\begin_inset Argument 3"
2274                     if document.body[p + 4] == "\\begin_inset ERT":
2275                         if document.body[p + 9].startswith("<"):
2276                             # This is an overlay specification
2277                             # strip off the <
2278                             document.body[p + 9] = document.body[p + 9][1:]
2279                             if document.body[p + 9].endswith(">"):
2280                                 # strip off the >
2281                                 document.body[p + 9] = document.body[p + 9][:-1]
2282                                 # Shift this one
2283                                 document.body[p] = "\\begin_inset Argument 2"
2284             if layoutname in list_layouts:
2285                 m = rx.match(document.body[p])
2286                 if m:
2287                     if m.group(1) == "1":
2288                         if document.body[p + 4] == "\\begin_inset ERT":
2289                             if document.body[p + 9].startswith("<"):
2290                                 # This is an overlay specification
2291                                 # strip off the <
2292                                 document.body[p + 9] = document.body[p + 9][1:]
2293                                 if document.body[p + 9].endswith(">"):
2294                                     # strip off the >
2295                                     document.body[p + 9] = document.body[p + 9][:-1]
2296                         elif document.body[p + 4].startswith("<"):
2297                             # This is an overlay specification (without ERT)
2298                             # strip off the <
2299                             document.body[p + 4] = document.body[p + 4][1:]
2300                             if document.body[p + 4].endswith(">"):
2301                                 # strip off the >
2302                                 document.body[p + 4] = document.body[p + 4][:-1]
2303                         elif layoutname != "Itemize":
2304                             # Shift this one
2305                             document.body[p] = "\\begin_inset Argument 2"
2306         i += 1
2307
2308
2309 #
2310 # Helper function for the frame conversion routines
2311 #
2312 # FIXME: This method currently requires the arguments to be either
2313 #        * In one (whole) ERT each: <ERT>[<arg1>]</ERT><ERT><arg2></ERT><ERT>[arg3]</ERT>
2314 #        * Altogether in one whole ERT: <ERT>[<arg1>]<arg2>[arg3]</ERT>
2315 #        If individual arguments mix ERT and non-ERT or are splitted
2316 #        over several ERTs, the parsing fails.
2317 def convert_beamerframeargs(document, i, parbeg):
2318     ertend = i
2319     while True:
2320         if document.body[parbeg] != "\\begin_inset ERT":
2321             return ertend
2322         ertend = find_end_of_inset(document.body, parbeg)
2323         if ertend == -1:
2324             document.warning("Malformed LyX document: missing ERT \\end_inset")
2325             return ertend
2326         ertcont = parbeg + 5
2327         if document.body[ertcont].startswith("[<"):
2328             # This is a default overlay specification
2329             # strip off the [<
2330             document.body[ertcont] = document.body[ertcont][2:]
2331             if document.body[ertcont].endswith(">]"):
2332                 # strip off the >]
2333                 document.body[ertcont] = document.body[ertcont][:-2]
2334             elif document.body[ertcont].endswith("]"):
2335                 # divide the args
2336                 tok = document.body[ertcont].find('>][')
2337                 if tok != -1:
2338                     subst = [document.body[ertcont][:tok],
2339                               '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 3',
2340                               'status collapsed', '', '\\begin_layout Plain Layout',
2341                               document.body[ertcont][tok + 3:-1]]
2342                     document.body[ertcont : ertcont + 1] = subst
2343                     ertend += 11
2344             # Convert to ArgInset
2345             document.body[parbeg] = "\\begin_inset Argument 2"
2346         elif document.body[ertcont].startswith("<"):
2347             # This is an overlay specification
2348             # strip off the <
2349             document.body[ertcont] = document.body[ertcont][1:]
2350             if document.body[ertcont].endswith(">"):
2351                 # strip off the >
2352                 document.body[ertcont] = document.body[ertcont][:-1]
2353                 # Convert to ArgInset
2354                 document.body[parbeg] = "\\begin_inset Argument 1"
2355             elif document.body[ertcont].endswith(">]"):
2356                 # divide the args
2357                 tok = document.body[ertcont].find('>[<')
2358                 if tok != -1:
2359                     document.body[ertcont : ertcont + 1] = [document.body[ertcont][:tok],
2360                                                     '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
2361                                                     'status collapsed', '', '\\begin_layout Plain Layout',
2362                                                     document.body[ertcont][tok + 3:-2]]
2363                 # Convert to ArgInset
2364                 document.body[parbeg] = "\\begin_inset Argument 1"
2365                 ertend += 11
2366             elif document.body[ertcont].endswith("]"):
2367                 # divide the args
2368                 tok = document.body[ertcont].find('>[<')
2369                 if tok != -1:
2370                     # divide the args
2371                     tokk = document.body[ertcont].find('>][')
2372                     if tokk != -1:
2373                         document.body[ertcont : ertcont + 1] = [document.body[ertcont][:tok],
2374                                                         '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
2375                                                         'status collapsed', '', '\\begin_layout Plain Layout',
2376                                                         document.body[ertcont][tok + 3:tokk],
2377                                                         '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 3',
2378                                                         'status collapsed', '', '\\begin_layout Plain Layout',
2379                                                         document.body[ertcont][tokk + 3:-1]]
2380                         ertend += 22
2381                 else:
2382                     tokk = document.body[ertcont].find('>[')
2383                     if tokk != -1:
2384                         document.body[ertcont : ertcont + 1] = [document.body[ertcont][:tokk],
2385                                                         '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 3',
2386                                                         'status collapsed', '', '\\begin_layout Plain Layout',
2387                                                         document.body[ertcont][tokk + 2:-1]]
2388                         ertend += 11
2389                 # Convert to ArgInset
2390                 document.body[parbeg] = "\\begin_inset Argument 1"
2391         elif document.body[ertcont].startswith("["):
2392             # This is an ERT option
2393             # strip off the [
2394             document.body[ertcont] = document.body[ertcont][1:]
2395             if document.body[ertcont].endswith("]"):
2396                 # strip off the ]
2397                 document.body[ertcont] = document.body[ertcont][:-1]
2398                 # Convert to ArgInset
2399                 document.body[parbeg] = "\\begin_inset Argument 3"
2400         parbeg = ertend + 3
2401         continue
2402     return ertend
2403
2404
2405 def convert_againframe_args(document):
2406     " Converts beamer AgainFrame to new layout "
2407
2408     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2409     if document.textclass not in beamer_classes:
2410         return
2411    
2412     i = 0
2413     while True:
2414         i = find_token(document.body, "\\begin_layout AgainFrame", i)
2415         if i == -1:
2416             break
2417         parent = get_containing_layout(document.body, i)
2418         if parent[1] != i:
2419             document.warning("Wrong parent layout!")
2420         j = parent[2]
2421         parbeg = parent[3]
2422         if i != -1:
2423             # Convert ERT arguments
2424             # FIXME: See restrictions in convert_beamerframeargs method
2425             ertend = convert_beamerframeargs(document, i, parbeg)
2426             if ertend == -1:
2427                 break
2428         i = j
2429
2430
2431 def convert_corollary_args(document):
2432     " Converts beamer corrolary-style ERT arguments native InsetArgs "
2433     
2434     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2435     if document.textclass not in beamer_classes:
2436         return
2437    
2438     corollary_layouts = ["Corollary", "Definition", "Definitions", "Example", "Examples", "Fact", "Proof", "Theorem"]
2439     for lay in corollary_layouts:
2440         i = 0
2441         while True:
2442             i = find_token_exact(document.body, "\\begin_layout " + lay, i)
2443             if i == -1:
2444                 break
2445             parent = get_containing_layout(document.body, i)
2446             if parent[1] != i:
2447                 document.warning("Wrong parent layout!")
2448             j = parent[2]
2449             parbeg = parent[3]
2450             if i != -1:
2451                 if document.body[parbeg] == "\\begin_inset ERT":
2452                     ertcontfirstline = parbeg + 5
2453                     # Find the last ERT in this paragraph (which might also be the first)
2454                     lastertbeg = find_token_backwards(document.body, "\\begin_inset ERT", j)
2455                     if lastertbeg == -1:
2456                         document.warning("Last ERT not found!")
2457                         break
2458                     lastertend = find_end_of_inset(document.body, lastertbeg)
2459                     if lastertend == -1:
2460                         document.warning("End of last ERT not found!")
2461                         break
2462                     ertcontlastline = lastertend - 3
2463                     if document.body[ertcontfirstline].startswith("<"):
2464                         # This is an overlay specification
2465                         # strip off the <
2466                         document.body[ertcontfirstline] = document.body[ertcontfirstline][1:]
2467                         if document.body[ertcontlastline].endswith(">"):
2468                             # strip off the >
2469                             document.body[ertcontlastline] = document.body[ertcontlastline][:-1]
2470                             if ertcontfirstline < ertcontlastline:
2471                                 # Multiline ERT. Might contain TeX code.  Embrace in ERT.
2472                                 document.body[ertcontlastline : ertcontlastline + 1] = [
2473                                                                     document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
2474                                 document.body[ertcontfirstline : ertcontfirstline + 1] = [
2475                                                                     '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 1',
2476                                                                     'status collapsed', '', '\\begin_layout Plain Layout',
2477                                                                     '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
2478                                                                     document.body[ertcontfirstline]]
2479                             else:
2480                                 # Convert to ArgInset
2481                                 document.body[parbeg] = "\\begin_inset Argument 1"
2482                         elif document.body[ertcontlastline].endswith("]"):
2483                             # divide the args
2484                             tok = document.body[ertcontfirstline].find('>[')
2485                             if tok != -1:
2486                                 if ertcontfirstline < ertcontlastline:
2487                                     # Multiline ERT. Might contain TeX code.  Embrace in ERT.
2488                                     document.body[ertcontlastline : ertcontlastline + 1] = [
2489                                                                         document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
2490                                     document.body[ertcontfirstline : ertcontfirstline + 1] = [document.body[ertcontfirstline][:tok],
2491                                                                         '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
2492                                                                         'status collapsed', '', '\\begin_layout Plain Layout',
2493                                                                         '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
2494                                                                         document.body[ertcontfirstline][tok + 2:-1]]
2495                                 else:
2496                                     document.body[ertcontfirstline : ertcontfirstline + 1] = [document.body[ertcontfirstline][:tok],
2497                                                                         '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
2498                                                                         'status collapsed', '', '\\begin_layout Plain Layout',
2499                                                                         document.body[ertcontfirstline][tok + 2:-1]]
2500                             # Convert to ArgInset
2501                             document.body[parbeg] = "\\begin_inset Argument 1"
2502                         i = j
2503                         continue
2504                     elif document.body[ertcontlastline].startswith("["):
2505                         if document.body[ertcontlastline].endswith("]"):
2506                             # This is an ERT option
2507                             # strip off the [
2508                             document.body[ertcontlastline] = document.body[ertcontlastline][1:]
2509                             # strip off the ]
2510                             document.body[ertcontlastline] = document.body[ertcontlastline][:-1]
2511                             # Convert to ArgInset
2512                             document.body[parbeg] = "\\begin_inset Argument 2"
2513                         else:
2514                             convert_TeX_brace_to_Argument(document, i, 2, 2, False, True, True)
2515                     i += 1
2516                     continue
2517             i = j
2518
2519
2520
2521 def convert_quote_args(document):
2522     " Converts beamer quote style ERT args to native InsetArgs "
2523     
2524     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2525     if document.textclass not in beamer_classes:
2526         return
2527    
2528     quote_layouts = ["Uncover", "Only", "Quotation", "Quote", "Verse"]
2529     for lay in quote_layouts:
2530         i = 0
2531         while True:
2532             i = find_token(document.body, "\\begin_layout " + lay, i)
2533             if i == -1:
2534                 break
2535             parent = get_containing_layout(document.body, i)
2536             if parent[1] != i:
2537                 document.warning("Wrong parent layout!")
2538             j = parent[2]
2539             parbeg = parent[3]
2540             if i != -1:
2541                 if document.body[parbeg] == "\\begin_inset ERT":
2542                     if document.body[i + 6].startswith("<"):
2543                         # This is an overlay specification
2544                         # strip off the <
2545                         document.body[i + 6] = document.body[i + 6][1:]
2546                         if document.body[i + 6].endswith(">"):
2547                             # strip off the >
2548                             document.body[i + 6] = document.body[i + 6][:-1]
2549                             # Convert to ArgInset
2550                             document.body[i + 1] = "\\begin_inset Argument 1"
2551             i = j
2552
2553
2554 def cleanup_beamerargs(document):
2555     " Clean up empty ERTs (conversion artefacts) "
2556
2557     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2558     if document.textclass not in beamer_classes:
2559         return
2560
2561     i = 0
2562     while True:
2563         i = find_token(document.body, "\\begin_inset Argument", i)
2564         if i == -1:
2565             return
2566         j = find_end_of_inset(document.body, i)
2567         if j == -1:
2568             document.warning("Malformed LyX document: Can't find end of Argument inset")
2569             i += 1
2570             continue
2571         while True:
2572             ertbeg = find_token(document.body, "\\begin_inset ERT", i, j)
2573             if ertbeg == -1:
2574                 break
2575             ertend = find_end_of_inset(document.body, ertbeg)
2576             if ertend == -1:
2577                 document.warning("Malformed LyX document: Can't find end of ERT inset")
2578                 break
2579             stripped = [line for line in document.body[ertbeg : ertend + 1] if line.strip()]
2580             if len(stripped) == 5:
2581                 # This is an empty ERT
2582                 offset = len(document.body[ertbeg : ertend + 1])
2583                 del document.body[ertbeg : ertend + 1]
2584                 j = j - offset
2585             else:
2586                 i = ertend
2587         i += 1
2588
2589
2590 def revert_beamerargs(document):
2591     " Reverts beamer arguments to old layout "
2592     
2593     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2594     if document.textclass not in beamer_classes:
2595         return
2596
2597     i = 0
2598     list_layouts = ["Itemize", "Enumerate", "Description"]
2599     headings = ["Part", "Section", "Section*", "Subsection", "Subsection*",
2600                 "Subsubsection", "Subsubsection*", "FrameSubtitle", "NoteItem"]
2601     quote_layouts = ["Uncover", "Only", "Quotation", "Quote", "Verse"]
2602     corollary_layouts = ["Corollary", "Definition", "Definitions", "Example", "Examples", "Fact", "Proof", "Theorem"]
2603     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
2604
2605     while True:
2606         i = find_token(document.body, "\\begin_inset Argument", i)
2607         if i == -1:
2608             return
2609         # Find containing paragraph layout
2610         parent = get_containing_layout(document.body, i)
2611         if parent == False:
2612             document.warning("Malformed LyX document: Can't find parent paragraph layout")
2613             i += 1
2614             continue
2615         parbeg = parent[1]
2616         parend = parent[2]
2617         realparbeg = parent[3]
2618         layoutname = parent[0]
2619         realparend = parend
2620         for p in range(parbeg, parend):
2621             if p >= realparend:
2622                 i = realparend
2623                 break
2624             if layoutname in headings:
2625                 m = rx.match(document.body[p])
2626                 if m:
2627                     argnr = m.group(1)
2628                     if argnr == "1":
2629                         # Find containing paragraph layout
2630                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2631                         endPlain = find_end_of_layout(document.body, beginPlain)
2632                         endInset = find_end_of_inset(document.body, p)
2633                         argcontent = document.body[beginPlain + 1 : endPlain]
2634                         # Adjust range end
2635                         realparend = realparend - len(document.body[p : endInset + 1])
2636                         # Remove arg inset
2637                         del document.body[p : endInset + 1]
2638                         if layoutname == "FrameSubtitle":
2639                             pre = put_cmd_in_ert("\\" + layoutname.lower() + "<") + argcontent + put_cmd_in_ert(">")
2640                         elif layoutname == "NoteItem":
2641                             pre = put_cmd_in_ert("\\note<") + argcontent + put_cmd_in_ert(">[item]")
2642                         elif layoutname.endswith('*'):
2643                             pre = put_cmd_in_ert("\\lyxframeend\\" + layoutname.lower()[:-1] + "<") + argcontent + put_cmd_in_ert(">*")
2644                         else:
2645                             pre = put_cmd_in_ert("\\lyxframeend\\" + layoutname.lower() + "<") + argcontent + put_cmd_in_ert(">")
2646                         secarg = find_token(document.body, "\\begin_inset Argument 2", parbeg, parend)
2647                         if secarg != -1:
2648                             # Find containing paragraph layout
2649                             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", secarg)
2650                             endPlain = find_end_of_layout(document.body, beginPlain)
2651                             endInset = find_end_of_inset(document.body, secarg)
2652                             argcontent = document.body[beginPlain + 1 : endPlain]
2653                             # Adjust range end
2654                             realparend = realparend - len(document.body[secarg : endInset + 1])
2655                             del document.body[secarg : endInset + 1]
2656                             pre += put_cmd_in_ert("[") + argcontent + put_cmd_in_ert("]")
2657                         pre += put_cmd_in_ert("{")
2658                         document.body[parbeg] = "\\begin_layout Standard"
2659                         document.body[realparbeg : realparbeg] = pre
2660                         pe = find_end_of_layout(document.body, parbeg)
2661                         post = put_cmd_in_ert("}")
2662                         document.body[pe : pe] = post
2663                         realparend += len(pre) + len(post)
2664             if layoutname == "AgainFrame":
2665                 m = rx.match(document.body[p])
2666                 if m:
2667                     argnr = m.group(1)
2668                     if argnr == "3":
2669                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2670                         endPlain = find_end_of_layout(document.body, beginPlain)
2671                         endInset = find_end_of_inset(document.body, p)
2672                         content = document.body[beginPlain + 1 : endPlain]
2673                         # Adjust range end
2674                         realparend = realparend - len(document.body[p : endInset + 1])
2675                         # Remove arg inset
2676                         del document.body[p : endInset + 1]
2677                         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
2678                         document.body[realparbeg : realparbeg] = subst
2679             if layoutname == "Overprint":
2680                 m = rx.match(document.body[p])
2681                 if m:
2682                     argnr = m.group(1)
2683                     if argnr == "1":
2684                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2685                         endPlain = find_end_of_layout(document.body, beginPlain)
2686                         endInset = find_end_of_inset(document.body, p)
2687                         content = document.body[beginPlain + 1 : endPlain]
2688                         # Adjust range end
2689                         realparend = realparend - len(document.body[p : endInset + 1])
2690                         # Remove arg inset
2691                         del document.body[p : endInset + 1]
2692                         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
2693                         document.body[realparbeg : realparbeg] = subst
2694             if layoutname == "OverlayArea":
2695                 m = rx.match(document.body[p])
2696                 if m:
2697                     argnr = m.group(1)
2698                     if argnr == "2":
2699                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2700                         endPlain = find_end_of_layout(document.body, beginPlain)
2701                         endInset = find_end_of_inset(document.body, p)
2702                         content = document.body[beginPlain + 1 : endPlain]
2703                         # Adjust range end
2704                         realparend = realparend - len(document.body[p : endInset + 1])
2705                         # Remove arg inset
2706                         del document.body[p : endInset + 1]
2707                         subst = put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
2708                         document.body[realparbeg : realparbeg] = subst
2709             if layoutname in list_layouts:
2710                 m = rx.match(document.body[p])
2711                 if m:
2712                     argnr = m.group(1)
2713                     if argnr == "1":
2714                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2715                         endPlain = find_end_of_layout(document.body, beginPlain)
2716                         endInset = find_end_of_inset(document.body, p)
2717                         content = document.body[beginPlain + 1 : endPlain]
2718                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2719                         realparend = realparend + len(subst) - len(content)
2720                         document.body[beginPlain + 1 : endPlain] = subst
2721                     elif argnr == "item:1":
2722                         j = find_end_of_inset(document.body, i)
2723                         # Find containing paragraph layout
2724                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2725                         endPlain = find_end_of_layout(document.body, beginPlain)
2726                         content = document.body[beginPlain + 1 : endPlain]
2727                         del document.body[i:j+1]
2728                         if layoutname == "Description":
2729                             # Description only has one (overlay) item arg
2730                             subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2731                             # This must be put after the first space (begin of decription body
2732                             # in LyX's arkward description list syntax)
2733                             # Try to find that place ...
2734                             rxx = re.compile(r'^([^\\ ]+ )(.*)$')
2735                             for q in range(parbeg, parend):
2736                                 m = rxx.match(document.body[q])
2737                                 if m:
2738                                     # We found it. Now insert the ERT argument just there:
2739                                     document.body[q : q] = [m.group(1), ''] + subst + ['', m.group(2)]
2740                                     break
2741                         else:
2742                             subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
2743                             document.body[realparbeg : realparbeg] = subst
2744                     elif argnr == "item:2":
2745                         j = find_end_of_inset(document.body, i)
2746                         # Find containing paragraph layout
2747                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2748                         endPlain = find_end_of_layout(document.body, beginPlain)
2749                         content = document.body[beginPlain + 1 : endPlain]
2750                         del document.body[i:j+1]
2751                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2752                         document.body[realparbeg : realparbeg] = subst
2753             if layoutname in quote_layouts:
2754                 m = rx.match(document.body[p])
2755                 if m:
2756                     argnr = m.group(1)
2757                     if argnr == "1":
2758                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2759                         endPlain = find_end_of_layout(document.body, beginPlain)
2760                         endInset = find_end_of_inset(document.body, p)
2761                         content = document.body[beginPlain + 1 : endPlain]
2762                         # Adjust range end
2763                         realparend = realparend - len(document.body[p : endInset + 1])
2764                         # Remove arg inset
2765                         del document.body[p : endInset + 1]
2766                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2767                         document.body[realparbeg : realparbeg] = subst
2768             if layoutname in corollary_layouts:
2769                 m = rx.match(document.body[p])
2770                 if m:
2771                     argnr = m.group(1)
2772                     if argnr == "2":
2773                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2774                         endPlain = find_end_of_layout(document.body, beginPlain)
2775                         endInset = find_end_of_inset(document.body, p)
2776                         content = document.body[beginPlain + 1 : endPlain]
2777                         # Adjust range end
2778                         realparend = realparend - len(document.body[p : endInset + 1])
2779                         # Remove arg inset
2780                         del document.body[p : endInset + 1]
2781                         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
2782                         document.body[realparbeg : realparbeg] = subst
2783         
2784         i = realparend
2785
2786
2787 def revert_beamerargs2(document):
2788     " Reverts beamer arguments to old layout, step 2 "
2789     
2790     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2791     if document.textclass not in beamer_classes:
2792         return
2793
2794     i = 0
2795     shifted_layouts = ["Part", "Section", "Subsection", "Subsubsection"]
2796     corollary_layouts = ["Corollary", "Definition", "Definitions", "Example", "Examples", "Fact", "Proof", "Theorem"]
2797     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
2798
2799     while True:
2800         i = find_token(document.body, "\\begin_inset Argument", i)
2801         if i == -1:
2802             return
2803         # Find containing paragraph layout
2804         parent = get_containing_layout(document.body, i)
2805         if parent == False:
2806             document.warning("Malformed LyX document: Can't find parent paragraph layout")
2807             i += 1
2808             continue
2809         parbeg = parent[1]
2810         parend = parent[2]
2811         realparbeg = parent[3]
2812         layoutname = parent[0]
2813         realparend = parend
2814         for p in range(parbeg, parend):
2815             if p >= realparend:
2816                 i = realparend
2817                 break
2818             if layoutname in shifted_layouts:
2819                 m = rx.match(document.body[p])
2820                 if m:
2821                     argnr = m.group(1)
2822                     if argnr == "2":
2823                         document.body[p] = "\\begin_inset Argument 1"       
2824             if layoutname in corollary_layouts:
2825                 m = rx.match(document.body[p])
2826                 if m:
2827                     argnr = m.group(1)
2828                     if argnr == "1":
2829                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2830                         endPlain = find_end_of_layout(document.body, beginPlain)
2831                         endInset = find_end_of_inset(document.body, p)
2832                         content = document.body[beginPlain + 1 : endPlain]
2833                         # Adjust range end
2834                         realparend = realparend - len(document.body[p : endInset + 1])
2835                         # Remove arg inset
2836                         del document.body[p : endInset + 1]
2837                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2838                         document.body[realparbeg : realparbeg] = subst
2839             if layoutname == "OverlayArea":
2840                 m = rx.match(document.body[p])
2841                 if m:
2842                     argnr = m.group(1)
2843                     if argnr == "1":
2844                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2845                         endPlain = find_end_of_layout(document.body, beginPlain)
2846                         endInset = find_end_of_inset(document.body, p)
2847                         content = document.body[beginPlain + 1 : endPlain]
2848                         # Adjust range end
2849                         realparend = realparend - len(document.body[p : endInset + 1])
2850                         # Remove arg inset
2851                         del document.body[p : endInset + 1]
2852                         subst = put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
2853                         document.body[realparbeg : realparbeg] = subst
2854             if layoutname == "AgainFrame":
2855                 m = rx.match(document.body[p])
2856                 if m:
2857                     argnr = m.group(1)
2858                     if argnr == "2":
2859                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2860                         endPlain = find_end_of_layout(document.body, beginPlain)
2861                         endInset = find_end_of_inset(document.body, p)
2862                         content = document.body[beginPlain + 1 : endPlain]
2863                         # Adjust range end
2864                         realparend = realparend - len(document.body[p : endInset + 1])
2865                         # Remove arg inset
2866                         del document.body[p : endInset + 1]
2867                         subst = put_cmd_in_ert("[<") + content + put_cmd_in_ert(">]")
2868                         document.body[realparbeg : realparbeg] = subst
2869         i = realparend
2870
2871
2872 def revert_beamerargs3(document):
2873     " Reverts beamer arguments to old layout, step 3 "
2874     
2875     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2876     if document.textclass not in beamer_classes:
2877         return
2878
2879     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
2880     i = 0
2881     while True:
2882         i = find_token(document.body, "\\begin_inset Argument", i)
2883         if i == -1:
2884             return
2885         # Find containing paragraph layout
2886         parent = get_containing_layout(document.body, i)
2887         if parent == False:
2888             document.warning("Malformed LyX document: Can't find parent paragraph layout")
2889             i += 1
2890             continue
2891         parbeg = parent[1]
2892         parend = parent[2]
2893         realparbeg = parent[3]
2894         layoutname = parent[0]
2895         realparend = parend
2896         for p in range(parbeg, parend):
2897             if p >= realparend:
2898                 i = realparend
2899                 break
2900             if layoutname == "AgainFrame":
2901                 m = rx.match(document.body[p])
2902                 if m:
2903                     argnr = m.group(1)
2904                     if argnr == "1":
2905                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
2906                         endPlain = find_end_of_layout(document.body, beginPlain)
2907                         endInset = find_end_of_inset(document.body, p)
2908                         content = document.body[beginPlain + 1 : endPlain]
2909                         # Adjust range end
2910                         realparend = realparend - len(document.body[p : endInset + 1])
2911                         # Remove arg inset
2912                         del document.body[p : endInset + 1]
2913                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
2914                         document.body[realparbeg : realparbeg] = subst
2915         i = realparend
2916
2917
2918 def revert_beamerflex(document):
2919     " Reverts beamer Flex insets "
2920     
2921     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
2922     if document.textclass not in beamer_classes:
2923         return
2924
2925     new_flexes = {"Bold" : "\\textbf", "Emphasize" : "\\emph", "Only" : "\\only",
2926                   "Uncover" : "\\uncover", "Visible" : "\\visible",
2927                   "Invisible" : "\\invisible", "Alternative" : "\\alt",
2928                   "Beamer_Note" : "\\note"}
2929     old_flexes = {"Alert" : "\\alert", "Structure" : "\\structure"}
2930     rx = re.compile(r'^\\begin_inset Flex (.+)$')
2931
2932     i = 0
2933     while True:
2934         i = find_token(document.body, "\\begin_inset Flex", i)
2935         if i == -1:
2936             return
2937         m = rx.match(document.body[i])
2938         if m:
2939             flextype = m.group(1)
2940             z = find_end_of_inset(document.body, i)
2941             if z == -1:
2942                 document.warning("Can't find end of Flex " + flextype + " inset.")
2943                 i += 1
2944                 continue
2945             if flextype in new_flexes:
2946                 pre = put_cmd_in_ert(new_flexes[flextype])
2947                 arg = find_token(document.body, "\\begin_inset Argument 1", i, z)
2948                 if arg != -1:
2949                     argend = find_end_of_inset(document.body, arg)
2950                     if argend == -1:
2951                         document.warning("Can't find end of Argument!")
2952                         i += 1
2953                         continue
2954                     # Find containing paragraph layout
2955                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
2956                     endPlain = find_end_of_layout(document.body, beginPlain)
2957                     argcontent = document.body[beginPlain + 1 : endPlain]
2958                     # Adjust range end
2959                     z = z - len(document.body[arg : argend + 1])
2960                     # Remove arg inset
2961                     del document.body[arg : argend + 1]
2962                     pre += put_cmd_in_ert("<") + argcontent + put_cmd_in_ert(">")
2963                 arg = find_token(document.body, "\\begin_inset Argument 2", i, z)
2964                 if arg != -1:
2965                     argend = find_end_of_inset(document.body, arg)
2966                     if argend == -1:
2967                         document.warning("Can't find end of Argument!")
2968                         i += 1
2969                         continue
2970                     # Find containing paragraph layout
2971                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
2972                     endPlain = find_end_of_layout(document.body, beginPlain)
2973                     argcontent = document.body[beginPlain + 1 : endPlain]
2974                     # Adjust range end
2975                     z = z - len(document.body[arg : argend + 1])
2976                     # Remove arg inset
2977                     del document.body[arg : argend + 1]
2978                     if flextype == "Alternative":
2979                         pre += put_cmd_in_ert("{") + argcontent + put_cmd_in_ert("}")
2980                     else:
2981                         pre += put_cmd_in_ert("[") + argcontent + put_cmd_in_ert("]")
2982                 pre += put_cmd_in_ert("{")
2983                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2984                 endPlain = find_end_of_layout(document.body, beginPlain)
2985                 # Adjust range end
2986                 z = z - len(document.body[i : beginPlain + 1])
2987                 z += len(pre)
2988                 document.body[i : beginPlain + 1] = pre
2989                 post = put_cmd_in_ert("}")
2990                 document.body[z - 2 : z + 1] = post
2991             elif flextype in old_flexes:
2992                 pre = put_cmd_in_ert(old_flexes[flextype])
2993                 arg = find_token(document.body, "\\begin_inset Argument 1", i, z)
2994                 if arg == -1:
2995                     i += 1
2996                     continue
2997                 argend = find_end_of_inset(document.body, arg)
2998                 if argend == -1:
2999                     document.warning("Can't find end of Argument!")
3000                     i += 1
3001                     continue
3002                 # Find containing paragraph layout
3003                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3004                 endPlain = find_end_of_layout(document.body, beginPlain)
3005                 argcontent = document.body[beginPlain + 1 : endPlain]
3006                 # Adjust range end
3007                 z = z - len(document.body[arg : argend + 1])
3008                 # Remove arg inset
3009                 del document.body[arg : argend + 1]
3010                 pre += put_cmd_in_ert("<") + argcontent + put_cmd_in_ert(">")
3011                 pre += put_cmd_in_ert("{")
3012                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
3013                 endPlain = find_end_of_layout(document.body, beginPlain)
3014                 # Adjust range end
3015                 z = z - len(document.body[i : beginPlain + 1])
3016                 z += len(pre)
3017                 document.body[i : beginPlain + 1] = pre
3018                 post = put_cmd_in_ert("}")
3019                 document.body[z - 2 : z + 1] = post
3020         
3021         i += 1
3022
3023
3024 def revert_beamerblocks(document):
3025     " Reverts beamer block arguments to ERT "
3026     
3027     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3028     if document.textclass not in beamer_classes:
3029         return
3030
3031     blocks = ["Block", "ExampleBlock", "AlertBlock"]
3032
3033     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
3034     i = 0
3035     while True:
3036         i = find_token(document.body, "\\begin_inset Argument", i)
3037         if i == -1:
3038             return
3039         # Find containing paragraph layout
3040         parent = get_containing_layout(document.body, i)
3041         if parent == False:
3042             document.warning("Malformed LyX document: Can't find parent paragraph layout")
3043             i += 1
3044             continue
3045         parbeg = parent[1]
3046         parend = parent[2]
3047         realparbeg = parent[3]
3048         layoutname = parent[0]
3049         realparend = parend
3050         for p in range(parbeg, parend):
3051             if p >= realparend:
3052                 i = realparend
3053                 break
3054             if layoutname in blocks:
3055                 m = rx.match(document.body[p])
3056                 if m:
3057                     argnr = m.group(1)
3058                     if argnr == "1":
3059                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3060                         endPlain = find_end_of_layout(document.body, beginPlain)
3061                         endInset = find_end_of_inset(document.body, p)
3062                         content = document.body[beginPlain + 1 : endPlain]
3063                         # Adjust range end
3064                         realparend = realparend - len(document.body[p : endInset + 1])
3065                         # Remove arg inset
3066                         del document.body[p : endInset + 1]
3067                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3068                         document.body[realparbeg : realparbeg] = subst
3069                     elif argnr == "2":
3070                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3071                         endPlain = find_end_of_layout(document.body, beginPlain)
3072                         endInset = find_end_of_inset(document.body, p)
3073                         content = document.body[beginPlain + 1 : endPlain]
3074                         # Adjust range end
3075                         realparend = realparend - len(document.body[p : endInset + 1])
3076                         # Remove arg inset
3077                         del document.body[p : endInset + 1]
3078                         subst = put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
3079                         document.body[realparbeg : realparbeg] = subst
3080         i = realparend
3081
3082
3083
3084 def convert_beamerblocks(document):
3085     " Converts beamer block ERT args to native InsetArgs "
3086     
3087     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3088     if document.textclass not in beamer_classes:
3089         return
3090    
3091     blocks = ["Block", "ExampleBlock", "AlertBlock"]
3092     for lay in blocks:
3093         i = 0
3094         while True:
3095             i = find_token_exact(document.body, "\\begin_layout " + lay, i)
3096             if i == -1:
3097                 break
3098             parent = get_containing_layout(document.body, i)
3099             if parent == False or parent[1] != i:
3100                 document.warning("Wrong parent layout!")
3101                 i += 1
3102                 continue
3103             parbeg = parent[3]
3104             parend = parent[2]
3105             j = parend
3106             if i != -1:
3107                 # If the paragraph starts with a language switch, adjust parbeg
3108                 if len(document.body[parbeg]) == 0 and parbeg < parend \
3109                 and document.body[parbeg + 1].startswith("\\lang"):
3110                     parbeg += 2
3111                 if document.body[parbeg] == "\\begin_inset ERT":
3112                     ertcontfirstline = parbeg + 5
3113                     lastertbeg = -1
3114                     lastertend = -1
3115                     while True:
3116                         # Find the last ERT in this paragraph used for arguments
3117                         # (which might also be the first)
3118                         lastertbeg = find_token_backwards(document.body, "\\begin_inset ERT", j)
3119                         if lastertbeg == -1:
3120                             document.warning("Last ERT not found!")
3121                             break
3122                         lastertend = find_end_of_inset(document.body, lastertbeg)
3123                         if lastertend == -1:
3124                             document.warning("End of last ERT not found!")
3125                             break
3126                         # Is this ERT really used for an argument?
3127                         # Note: This will fail when non-argument ERTs actually use brackets
3128                         #       (e.g. \pause{})
3129                         regexp = re.compile(r'.*[>\]\}]', re.IGNORECASE)
3130                         cbracket = find_re(document.body, regexp, lastertbeg, lastertend)
3131                         if cbracket != -1:
3132                             break
3133                         if lastertbeg == parbeg:
3134                             break
3135                         j = lastertbeg - 1
3136                     if lastertbeg == -1 or lastertend == -1:
3137                         break
3138                     ertcontlastline = lastertend - 3
3139                     while True:
3140                         if document.body[ertcontfirstline].lstrip().startswith("<"):
3141                             # This is an overlay specification
3142                             # strip off the <
3143                             document.body[ertcontfirstline] = document.body[ertcontfirstline].lstrip()[1:]
3144                             if document.body[ertcontlastline].rstrip().endswith(">"):
3145                                 # strip off the >
3146                                 document.body[ertcontlastline] = document.body[ertcontlastline].rstrip()[:-1]
3147                                 # Convert to ArgInset
3148                                 document.body[parbeg] = "\\begin_inset Argument 1"
3149                             elif document.body[ertcontlastline].rstrip().endswith("}"):
3150                                 # strip off the }
3151                                 document.body[ertcontlastline] = document.body[ertcontlastline].rstrip()[:-1]
3152                                 # divide the args
3153                                 ertcontdivline = ertcontfirstline
3154                                 tok = document.body[ertcontdivline].find('>{')
3155                                 if tok == -1:
3156                                     regexp = re.compile(r'.*>\{', re.IGNORECASE)
3157                                     ertcontdivline = find_re(document.body, regexp, ertcontfirstline, lastertend)
3158                                     tok = document.body[ertcontdivline].find('>{')
3159                                 if tok != -1:
3160                                     if ertcontfirstline < ertcontlastline:
3161                                         # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3162                                         document.body[ertcontlastline : ertcontlastline + 1] = [
3163                                                                             document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
3164                                         if ertcontdivline == ertcontfirstline:
3165                                             document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3166                                                                                 '\\end_layout', '', '\\end_inset', '',
3167                                                                                 '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
3168                                                                                 'status collapsed', '', '\\begin_layout Plain Layout',
3169                                                                                 '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3170                                                                                 document.body[ertcontdivline][tok + 2:]]
3171                                         else:
3172                                             document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3173                                                                                 '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
3174                                                                                 'status collapsed', '', '\\begin_layout Plain Layout',
3175                                                                                 '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3176                                                                                 document.body[ertcontdivline][tok + 2:]]
3177                                     else:
3178                                         document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3179                                                                             '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
3180                                                                             'status collapsed', '', '\\begin_layout Plain Layout',
3181                                                                             document.body[ertcontdivline][tok + 2:]]
3182                                 else:
3183                                     # check if have delimiters in two different ERTs
3184                                     tok = document.body[ertcontdivline].find('>')
3185                                     if tok == -1:
3186                                         regexp = re.compile(r'.*>', re.IGNORECASE)
3187                                         ertcontdivline = find_re(document.body, regexp, ertcontfirstline, lastertend)
3188                                         tok = document.body[ertcontdivline].find('>')
3189                                     if tok != -1:
3190                                         tokk = document.body[ertcontdivline].find('{')
3191                                         if tokk == -1:
3192                                             regexp = re.compile(r'.*\{', re.IGNORECASE)
3193                                             ertcontdivlinetwo = find_re(document.body, regexp, ertcontfirstline, lastertend)
3194                                             tokk = document.body[ertcontdivlinetwo].find('{')
3195                                         if tokk != -1:
3196                                             if ertcontfirstline < ertcontlastline:
3197                                                 # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3198                                                 document.body[ertcontlastline : ertcontlastline + 1] = [
3199                                                                                     document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
3200                                                 document.body[ertcontdivline : ertcontdivlinetwo + 1] = [document.body[ertcontdivline][:tok],
3201                                                                                     '\\end_layout', '', '\\end_inset', '', '\\end_layout', '', 
3202                                                                                     '\\end_inset', '', '', '\\begin_inset Argument 2',
3203                                                                                     'status collapsed', '', '\\begin_layout Plain Layout',
3204                                                                                     '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3205                                                                                     document.body[ertcontdivlinetwo][tokk + 1:]]
3206                                             else:
3207                                                 document.body[ertcontdivline : ertcontdivlinetwo + 1] = [document.body[ertcontdivline][:tok],
3208                                                                                     '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 2',
3209                                                                                     'status collapsed', '', '\\begin_layout Plain Layout',
3210                                                                                     document.body[ertcontdivlinetwo][tokk + 1:]]
3211                                 # Convert to ArgInset
3212                                 if ertcontfirstline < ertcontlastline:
3213                                     # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3214                                     document.body[parbeg : parbeg + 1] = ['\\begin_inset Argument 1',
3215                                                                         'status collapsed', '', '\\begin_layout Plain Layout',
3216                                                                         '\\begin_inset ERT', '']
3217                                 else:
3218                                     document.body[parbeg] = "\\begin_inset Argument 1"
3219                         elif document.body[ertcontfirstline].lstrip().startswith("{"):
3220                             # This is the block title
3221                             if document.body[ertcontlastline].rstrip().endswith("}"):
3222                                 # strip off the braces
3223                                 document.body[ertcontfirstline] = document.body[ertcontfirstline].lstrip()[1:]
3224                                 document.body[ertcontlastline] = document.body[ertcontlastline].rstrip()[:-1]
3225                                 if ertcontfirstline < ertcontlastline:
3226                                     # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3227                                     document.body[parend : parend + 1] = [
3228                                                                         document.body[parend], '\\end_inset', '', '\\end_layout']
3229                                     document.body[parbeg : parbeg + 1] = ['\\begin_inset Argument 2',
3230                                                                         'status collapsed', '', '\\begin_layout Plain Layout',
3231                                                                         '\\begin_inset ERT', '']
3232                                 else:
3233                                     # Convert to ArgInset
3234                                     document.body[parbeg] = "\\begin_inset Argument 2"
3235                             # the overlay argument can also follow the title, so ...
3236                             elif document.body[ertcontlastline].rstrip().endswith(">"):
3237                                 # strip off the {
3238                                 document.body[ertcontfirstline] = document.body[ertcontfirstline].lstrip()[1:]
3239                                 # strip off the >
3240                                 document.body[ertcontlastline] = document.body[ertcontlastline].rstrip()[:-1]
3241                                 # divide the args
3242                                 ertcontdivline = ertcontfirstline
3243                                 tok = document.body[ertcontdivline].find('}<')
3244                                 if tok == -1:
3245                                     regexp = re.compile(r'.*\}<', re.IGNORECASE)
3246                                     ertcontdivline = find_re(document.body, regexp, ertcontfirstline, lastertend)
3247                                     tok = document.body[ertcontdivline].find('}<')
3248                                 if tok != -1:
3249                                     if ertcontfirstline < ertcontlastline:
3250                                         # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3251                                         document.body[ertcontlastline : ertcontlastline + 1] = [
3252                                                                             document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
3253                                         if ertcontdivline == ertcontfirstline:
3254                                             document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3255                                                                                 '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 1',
3256                                                                                 'status collapsed', '', '\\begin_layout Plain Layout',
3257                                                                                 '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3258                                                                                 document.body[ertcontdivline][tok + 2:]]
3259                                         else:
3260                                             document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3261                                                                                 '\\end_layout', '', '\\end_inset', '',
3262                                                                                 '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 1',
3263                                                                                 'status collapsed', '', '\\begin_layout Plain Layout',
3264                                                                                 '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3265                                                                                 document.body[ertcontdivline][tok + 2:]]
3266                                     else:
3267                                         document.body[ertcontdivline : ertcontdivline + 1] = [document.body[ertcontdivline][:tok],
3268                                                                             '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 1',
3269                                                                             'status collapsed', '', '\\begin_layout Plain Layout',
3270                                                                             document.body[ertcontdivline][tok + 2:]]
3271                                 else:
3272                                     # check if have delimiters in two different ERTs
3273                                     tok = document.body[ertcontdivline].find('}')
3274                                     if tok == -1:
3275                                         regexp = re.compile(r'.*\}', re.IGNORECASE)
3276                                         ertcontdivline = find_re(document.body, regexp, ertcontfirstline, lastertend)
3277                                         tok = document.body[ertcontdivline].find('}')
3278                                         if tok != -1:
3279                                             tokk = document.body[ertcontdivline].find('<')
3280                                             if tokk == -1:
3281                                                 regexp = re.compile(r'.*<', re.IGNORECASE)
3282                                                 ertcontdivlinetwo = find_re(document.body, regexp, ertcontfirstline, lastertend)
3283                                                 tokk = document.body[ertcontdivlinetwo].find('<')
3284                                                 if tokk != -1:
3285                                                     if ertcontfirstline < ertcontlastline:
3286                                                         # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3287                                                         document.body[ertcontlastline : ertcontlastline + 1] = [
3288                                                                                             document.body[ertcontlastline], '\\end_layout', '', '\\end_inset']
3289                                                         document.body[ertcontdivline : ertcontdivlinetwo + 1] = [document.body[ertcontdivline][:tok],
3290                                                                                             '\\end_layout', '', '\\end_inset', '', '\\end_layout', '', 
3291                                                                                             '\\end_inset', '', '', '\\begin_inset Argument 1',
3292                                                                                             'status collapsed', '', '\\begin_layout Plain Layout',
3293                                                                                             '\\begin_inset ERT', '', 'status open' '', '\\begin_layout Plain Layout',
3294                                                                                             document.body[ertcontdivlinetwo][tokk + 1:]]
3295                                                     else:
3296                                                         document.body[ertcontdivline : ertcontdivlinetwo + 1] = [document.body[ertcontdivline][:tok],
3297                                                                                             '\\end_layout', '', '\\end_inset', '', '', '\\begin_inset Argument 1',
3298                                                                                             'status collapsed', '', '\\begin_layout Plain Layout',
3299                                                                                             document.body[ertcontdivlinetwo][tokk + 1:]]
3300                                 # Convert to ArgInset
3301                                 if ertcontfirstline < ertcontlastline:
3302                                     # Multiline ERT. Might contain TeX code.  Embrace in ERT.
3303                                     document.body[parbeg : parbeg + 1] = ['\\begin_inset Argument 2',
3304                                                                         'status collapsed', '', '\\begin_layout Plain Layout',
3305                                                                         '\\begin_inset ERT', '']
3306                                 else:
3307                                     document.body[parbeg] = "\\begin_inset Argument 2"
3308                             elif count_pars_in_inset(document.body, ertcontfirstline) > 1:
3309                                 # Multipar ERT. Skip this.
3310                                 break
3311                             else:
3312                                 # ERT has contents after the closing bracket. We cannot convert this.
3313                                 # convert_TeX_brace_to_Argument cannot either.
3314                                 #convert_TeX_brace_to_Argument(document, i, 2, 2, False, True, False)
3315                                 break
3316                         else:
3317                             break
3318                         j = find_end_of_layout(document.body, i)
3319                         if j == -1:
3320                             document.warning("end of layout not found!")
3321                         k = find_token(document.body, "\\begin_inset Argument", i, j)
3322                         if k == -1:
3323                             document.warning("InsetArgument not found!")
3324                             break
3325                         l = find_end_of_inset(document.body, k)
3326                         m = find_token(document.body, "\\begin_inset ERT", l, j)
3327                         if m == -1:
3328                             break
3329                         ertcontfirstline = m + 5
3330                         parbeg = m
3331             i = j
3332
3333
3334 def convert_overprint(document):
3335     " Convert old beamer overprint layouts to ERT "
3336     
3337     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3338     if document.textclass not in beamer_classes:
3339         return
3340
3341     i = 0
3342     while True:
3343         i = find_token(document.body, "\\begin_layout Overprint", i)
3344         if i == -1:
3345             return
3346         # Find end of sequence
3347         j = find_end_of_sequence(document.body, i)
3348         if j == -1:
3349             document.warning("Malformed LyX document. Cannot find end of Overprint sequence!")
3350             i += 1
3351             continue
3352         endseq = j
3353         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\begin{overprint}")
3354         esubst = list()
3355         if document.body[j] == "\\end_deeper":
3356             esubst = ["", "\\begin_layout Standard"] + put_cmd_in_ert("\\end{overprint}") + ["\\end_layout"]
3357         else:
3358             esubst = ["\\end_layout", "", "\\begin_layout Standard"] + put_cmd_in_ert("\\end{overprint}") + ["\\end_layout"]
3359         endseq = endseq + len(esubst) - len(document.body[j : j])
3360         document.body[j : j] = esubst
3361         argbeg = find_token(document.body, "\\begin_inset Argument 1", i, j)
3362         if argbeg != -1:
3363             argend = find_end_of_layout(document.body, argbeg)
3364             if argend == -1:
3365                 document.warning("Malformed LyX document. Cannot find end of Overprint argument!")
3366                 i += 1
3367                 continue
3368             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", argbeg)
3369             endPlain = find_end_of_layout(document.body, beginPlain)
3370             content = document.body[beginPlain + 1 : endPlain]
3371             # Adjust range end
3372             endseq = endseq - len(document.body[argbeg : argend + 1])
3373             # Remove arg inset
3374             del document.body[argbeg : argend + 1]
3375             subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3376             
3377         endseq = endseq - len(document.body[i : i])
3378         document.body[i : i] = subst + ["\\end_layout"]
3379         endseq += len(subst)
3380         
3381         for p in range(i, endseq):
3382             if document.body[p] == "\\begin_layout Overprint":
3383                 document.body[p] = "\\begin_layout Standard"
3384
3385         i = endseq
3386
3387
3388 def revert_overprint(document):
3389     " Revert old beamer overprint layouts to ERT "
3390     
3391     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3392     if document.textclass not in beamer_classes:
3393         return
3394
3395     i = 0
3396     while True:
3397         i = find_token(document.body, "\\begin_layout Overprint", i)
3398         if i == -1:
3399             return
3400         # Find end of sequence
3401         j = find_end_of_sequence(document.body, i)
3402         if j == -1:
3403             document.warning("Malformed LyX document. Cannot find end of Overprint sequence!")
3404             i += 1
3405             continue
3406         endseq = j
3407         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\begin{overprint}")
3408         esubst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\end{overprint}")
3409         endseq = endseq + len(esubst) - len(document.body[j : j])
3410         if document.body[j] == "\\end_deeper":
3411             document.body[j : j] = [""] + esubst + ["", "\\end_layout"]
3412         else:
3413             document.body[j : j] = ["\\end_layout", ""] + esubst
3414         r = i
3415         while r < j:
3416             if document.body[r] == "\\begin_deeper":
3417                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3418                 if s != -1:
3419                     document.body[r] = ""
3420                     document.body[s] = ""
3421                     r = s
3422                     continue
3423             r = r + 1
3424         argbeg = find_token(document.body, "\\begin_inset Argument 1", i, j)
3425         if argbeg != -1:
3426             # Is this really our argument?
3427             nested = find_token(document.body, "\\begin_deeper", i, argbeg)
3428             if nested != -1:
3429                 argend = find_end_of_inset(document.body, argbeg)
3430                 if argend == -1:
3431                     document.warning("Malformed LyX document. Cannot find end of Overprint argument!")
3432                     i += 1
3433                     continue
3434                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", argbeg)
3435                 endPlain = find_end_of_layout(document.body, beginPlain)
3436                 content = document.body[beginPlain + 1 : endPlain]
3437                 # Adjust range end
3438                 endseq = endseq - len(document.body[argbeg : argend])
3439                 # Remove arg inset
3440                 del document.body[argbeg : argend + 1]
3441                 subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3442             
3443         endseq = endseq - len(document.body[i : i])
3444         document.body[i : i] = subst + ["\\end_layout"]
3445         endseq += len(subst)
3446      
3447         p = i
3448         while True:
3449             if p >= endseq:
3450                 break
3451             if document.body[p] == "\\begin_layout Overprint":
3452                 q = find_end_of_layout(document.body, p)
3453                 if q == -1:
3454                     document.warning("Malformed LyX document. Cannot find end of Overprint layout!")
3455                     p += 1
3456                     continue
3457                 subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\onslide")
3458                 argbeg = find_token(document.body, "\\begin_inset Argument item:1", p, q)
3459                 if argbeg != -1:
3460                     argend = find_end_of_inset(document.body, argbeg)
3461                     if argend == -1:
3462                         document.warning("Malformed LyX document. Cannot find end of Overprint item argument!")
3463                         p += 1
3464                         continue
3465                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", argbeg)
3466                     endPlain = find_end_of_layout(document.body, beginPlain)
3467                     content = document.body[beginPlain + 1 : endPlain]
3468                     # Adjust range end
3469                     endseq = endseq - len(document.body[argbeg : argend + 1])
3470                     # Remove arg inset
3471                     del document.body[argbeg : argend + 1]
3472                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3473                 endseq = endseq - len(document.body[p : p + 1]) + len(subst)
3474                 document.body[p : p + 1] = subst
3475             p = p + 1
3476
3477         i = endseq
3478
3479
3480 def revert_frametitle(document):
3481     " Reverts beamer frametitle layout to ERT "
3482     
3483     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3484     if document.textclass not in beamer_classes:
3485         return
3486
3487     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
3488     i = 0
3489     while True:
3490         i = find_token(document.body, "\\begin_layout FrameTitle", i)
3491         if i == -1:
3492             return
3493         j = find_end_of_layout(document.body, i)
3494         if j == -1:
3495             document.warning("Malformed LyX document: Can't find end of FrameTitle layout")
3496             i += 1
3497             continue
3498         endlay = j
3499         document.body[j : j] = put_cmd_in_ert("}") + document.body[j : j]
3500         endlay += len(put_cmd_in_ert("}"))
3501         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\frametitle")
3502         for p in range(i, j):
3503             if p >= endlay:
3504                 break
3505             m = rx.match(document.body[p])
3506             if m:
3507                 argnr = m.group(1)
3508                 if argnr == "1":
3509                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3510                     endPlain = find_end_of_layout(document.body, beginPlain)
3511                     endInset = find_end_of_inset(document.body, p)
3512                     content = document.body[beginPlain + 1 : endPlain]
3513                     # Adjust range end
3514                     endlay = endlay - len(document.body[p : endInset + 1])
3515                     # Remove arg inset
3516                     del document.body[p : endInset + 1]
3517                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3518                 elif argnr == "2":
3519                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3520                     endPlain = find_end_of_layout(document.body, beginPlain)
3521                     endInset = find_end_of_inset(document.body, p)
3522                     content = document.body[beginPlain + 1 : endPlain]
3523                     # Adjust range end
3524                     endlay = endlay - len(document.body[p : endInset + 1])
3525                     # Remove arg inset
3526                     del document.body[p : endInset + 1]
3527                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3528                     
3529         subst += put_cmd_in_ert("{")
3530         document.body[i : i + 1] = subst
3531         i = endlay
3532
3533
3534 def convert_epigraph(document):
3535     " Converts memoir epigraph to new syntax "
3536     
3537     if document.textclass != "memoir":
3538         return
3539
3540     i = 0
3541     while True:
3542         i = find_token(document.body, "\\begin_layout Epigraph", i)
3543         if i == -1:
3544             return
3545         j = find_end_of_layout(document.body, i)
3546         if j == -1:
3547             document.warning("Malformed LyX document: Can't find end of Epigraph layout")
3548             i += 1
3549             continue
3550         endlay = j
3551         subst = list()
3552         ert = find_token(document.body, "\\begin_inset ERT", i, j)
3553         if ert != -1:
3554             endInset = find_end_of_inset(document.body, ert)
3555             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", ert)
3556             endPlain = find_end_of_layout(document.body, beginPlain)
3557             ertcont = beginPlain + 2
3558             if document.body[ertcont] == "}{":
3559                 # strip off the <
3560                 # Convert to ArgInset
3561                 endlay = endlay - 2 * len(document.body[j])
3562                 begsubst = ['\\begin_inset Argument post:1', 'status collapsed', '',
3563                             '\\begin_layout Plain Layout']
3564                 endsubst = ['\\end_layout', '', '\\end_inset', '', document.body[j]]
3565                 document.body[j : j + 1] = endsubst
3566                 document.body[endInset + 1 : endInset + 1] = begsubst
3567                 # Adjust range end
3568                 endlay += len(begsubst) + len(endsubst)
3569                 endlay = endlay - len(document.body[ert : endInset + 1])
3570                 del document.body[ert : endInset + 1]
3571                     
3572         i = endlay
3573
3574
3575 def revert_epigraph(document):
3576     " Reverts memoir epigraph argument to ERT "
3577     
3578     if document.textclass != "memoir":
3579         return
3580
3581     i = 0
3582     while True:
3583         i = find_token(document.body, "\\begin_layout Epigraph", i)
3584         if i == -1:
3585             return
3586         j = find_end_of_layout(document.body, i)
3587         if j == -1:
3588             document.warning("Malformed LyX document: Can't find end of Epigraph layout")
3589             i += 1
3590             continue
3591         endlay = j
3592         subst = list()
3593         p = find_token(document.body, "\\begin_layout Argument post:1", i, j)
3594         if p != -1:
3595             beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
3596             endPlain = find_end_of_layout(document.body, beginPlain)
3597             endInset = find_end_of_inset(document.body, p)
3598             content = document.body[beginPlain + 1 : endPlain]
3599             # Adjust range end
3600             endlay = endlay - len(document.body[p : endInset + 1])
3601             # Remove arg inset
3602             del document.body[p : endInset + 1]
3603             subst += put_cmd_in_ert("}{") + content
3604         else:
3605             subst += put_cmd_in_ert("}{")
3606                     
3607         document.body[j : j] = subst + document.body[j : j]
3608         i = endlay
3609
3610
3611 def convert_captioninsets(document):
3612     " Converts caption insets to new syntax "
3613     
3614     i = 0
3615     while True:
3616       i = find_token(document.body, "\\begin_inset Caption", i)
3617       if i == -1:
3618           return
3619       document.body[i] = "\\begin_inset Caption Standard"
3620       i += 1
3621
3622
3623 def revert_captioninsets(document):
3624     " Reverts caption insets to old syntax "
3625     
3626     i = 0
3627     while True:
3628       i = find_token(document.body, "\\begin_inset Caption Standard", i)
3629       if i == -1:
3630           return
3631       document.body[i] = "\\begin_inset Caption"
3632       i += 1
3633
3634
3635 def convert_captionlayouts(document):
3636     " Convert caption layouts to caption insets. "
3637
3638     caption_dict = {
3639         "Captionabove":  "Above",
3640         "Captionbelow":  "Below",
3641         "FigCaption"  :  "FigCaption",
3642         "Table_Caption" :  "Table",
3643         "CenteredCaption" : "Centered",
3644         "Bicaption" : "Bicaption",
3645         }
3646
3647     i = 0
3648     while True:
3649         i = find_token(document.body, "\\begin_layout", i)
3650         if i == -1:
3651             return
3652         val = get_value(document.body, "\\begin_layout", i)
3653         if val in list(caption_dict.keys()):
3654             j = find_end_of_layout(document.body, i)
3655             if j == -1:
3656                 document.warning("Malformed LyX document: Missing `\\end_layout'.")
3657                 return
3658
3659             document.body[j:j] = ["\\end_layout", "", "\\end_inset", "", ""]
3660             document.body[i:i+1] = ["\\begin_layout %s" % document.default_layout,
3661                                     "\\begin_inset Caption %s" % caption_dict[val], "",
3662                                     "\\begin_layout %s" % document.default_layout]
3663         i += 1
3664
3665
3666 def revert_captionlayouts(document):
3667     " Revert caption insets to caption layouts. "
3668     
3669     caption_dict = {
3670         "Above" : "Captionabove",
3671         "Below" : "Captionbelow",
3672         "FigCaption"  :  "FigCaption",
3673         "Table" : "Table_Caption",
3674         "Centered" : "CenteredCaption",
3675         "Bicaption" : "Bicaption",
3676         }
3677     
3678     i = 0
3679     rx = re.compile(r'^\\begin_inset Caption (\S+)$')
3680     while True:
3681         i = find_token(document.body, "\\begin_inset Caption", i)
3682         if i == -1:
3683             return
3684
3685         m = rx.match(document.body[i])
3686         val = ""
3687         if m:
3688             val = m.group(1)
3689         if val not in list(caption_dict.keys()):
3690             i += 1
3691             continue
3692         
3693         # We either need to delete the previous \begin_layout line, or we
3694         # need to end the previous layout if this inset is not in the first
3695         # position of the paragraph.
3696         layout_before = find_token_backwards(document.body, "\\begin_layout", i)
3697         if layout_before == -1:
3698             document.warning("Malformed LyX document: Missing `\\begin_layout'.")
3699             return
3700         layout_line = document.body[layout_before]
3701         del_layout_before = True
3702         l = layout_before + 1
3703         while l < i:
3704             if document.body[l] != "":
3705                 del_layout_before = False
3706                 break
3707             l = l + 1
3708         if del_layout_before:
3709             del document.body[layout_before:i]
3710             i = layout_before
3711         else:
3712             document.body[i:i] = ["\\end_layout", ""]
3713             i = i + 2
3714
3715         # Find start of layout in the inset and end of inset
3716         j = find_token(document.body, "\\begin_layout", i)
3717         if j == -1:
3718             document.warning("Malformed LyX document: Missing `\\begin_layout'.")
3719             return
3720         k = find_end_of_inset(document.body, i)
3721         if k == -1:
3722             document.warning("Malformed LyX document: Missing `\\end_inset'.")
3723             return
3724
3725         # We either need to delete the following \end_layout line, or we need
3726         # to restart the old layout if this inset is not at the paragraph end.
3727         layout_after = find_token(document.body, "\\end_layout", k)
3728         if layout_after == -1:
3729             document.warning("Malformed LyX document: Missing `\\end_layout'.")
3730             return
3731         del_layout_after = True
3732         l = k + 1
3733         while l < layout_after:
3734             if document.body[l] != "":
3735                 del_layout_after = False
3736                 break
3737             l = l + 1
3738         if del_layout_after:
3739             del document.body[k+1:layout_after+1]
3740         else:
3741             document.body[k+1:k+1] = [layout_line, ""]
3742
3743         # delete \begin_layout and \end_inset and replace \begin_inset with
3744         # "\begin_layout XXX". This works because we can only have one
3745         # paragraph in the caption inset: The old \end_layout will be recycled.
3746         del document.body[k]
3747         if document.body[k] == "":
3748             del document.body[k]
3749         del document.body[j]
3750         if document.body[j] == "":
3751             del document.body[j]
3752         document.body[i] = "\\begin_layout %s" % caption_dict[val]
3753         if document.body[i+1] == "":
3754             del document.body[i+1]
3755         i += 1
3756
3757
3758 def revert_fragileframe(document):
3759     " Reverts beamer FragileFrame layout to ERT "
3760     
3761     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3762     if document.textclass not in beamer_classes:
3763         return
3764
3765     i = 0
3766     while True:
3767         i = find_token(document.body, "\\begin_layout FragileFrame", i)
3768         if i == -1:
3769             return
3770         # Find end of sequence
3771         j = find_end_of_sequence(document.body, i)
3772         if j == -1:
3773             document.warning("Malformed LyX document. Cannot find end of FragileFrame sequence!")
3774             i += 1
3775             continue
3776         endseq = j
3777         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\begin{frame}")
3778         esubst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\end{frame}")
3779         endseq = endseq + len(esubst) - len(document.body[j : j])
3780         if document.body[j] == "\\end_deeper":
3781             document.body[j : j] = [""] + esubst + ["", "\\end_layout"]
3782         else:
3783             document.body[j : j] = esubst
3784         for q in range(i, j):
3785             if document.body[q] == "\\begin_layout FragileFrame":
3786                 document.body[q] = "\\begin_layout %s" % document.default_layout
3787         r = i
3788         while r < j:
3789             if document.body[r] == "\\begin_deeper":
3790                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3791                 if s != -1:
3792                     document.body[r] = ""
3793                     document.body[s] = ""
3794                     r = s
3795                     continue
3796             r = r + 1
3797         for p in range(1, 5):
3798             arg = find_token(document.body, "\\begin_inset Argument %d" % p, i, j)
3799             if arg != -1:
3800                 if p == 1:
3801                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3802                     endPlain = find_end_of_layout(document.body, beginPlain)
3803                     endInset = find_end_of_inset(document.body, arg)
3804                     content = document.body[beginPlain + 1 : endPlain]
3805                     # Adjust range end
3806                     j = j - len(document.body[arg : endInset + 1])
3807                     # Remove arg inset
3808                     del document.body[arg : endInset + 1]
3809                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3810                 elif p == 2:
3811                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3812                     endPlain = find_end_of_layout(document.body, beginPlain)
3813                     endInset = find_end_of_inset(document.body, arg)
3814                     content = document.body[beginPlain + 1 : endPlain]
3815                     # Adjust range end
3816                     j = j - len(document.body[arg : endInset + 1])
3817                     # Remove arg inset
3818                     del document.body[arg : endInset + 1]
3819                     subst += put_cmd_in_ert("[<") + content + put_cmd_in_ert(">]")
3820                 elif p == 3:
3821                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3822                     endPlain = find_end_of_layout(document.body, beginPlain)
3823                     endInset = find_end_of_inset(document.body, arg)
3824                     content = document.body[beginPlain + 1 : endPlain]
3825                     # Adjust range end
3826                     j = j - len(document.body[arg : endInset + 1])
3827                     # Remove arg inset
3828                     del document.body[arg : endInset + 1]
3829                     subst += put_cmd_in_ert("[fragile,") + content + put_cmd_in_ert("]")
3830                 elif p == 4:
3831                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3832                     endPlain = find_end_of_layout(document.body, beginPlain)
3833                     endInset = find_end_of_inset(document.body, arg)
3834                     content = document.body[beginPlain + 1 : endPlain]
3835                     # Adjust range end
3836                     j = j - len(document.body[arg : endInset + 1])
3837                     # Remove arg inset
3838                     del document.body[arg : endInset + 1]
3839                     subst += put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
3840             elif p == 3:
3841                 subst += put_cmd_in_ert("[fragile]")
3842                     
3843         document.body[i : i + 1] = subst
3844         i = j
3845
3846
3847 def revert_newframes(document):
3848     " Reverts beamer Frame and PlainFrame layouts to old forms "
3849     
3850     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
3851     if document.textclass not in beamer_classes:
3852         return
3853
3854     frame_dict = {
3855         "Frame" : "BeginFrame",
3856         "PlainFrame" : "BeginPlainFrame",
3857         }
3858
3859     rx = re.compile(r'^\\begin_layout (\S+)$')
3860     i = 0
3861     while True:
3862         i = find_token(document.body, "\\begin_layout", i)
3863         if i == -1:
3864             return
3865
3866         m = rx.match(document.body[i])
3867         val = ""
3868         if m:
3869             val = m.group(1)
3870         if val not in list(frame_dict.keys()):
3871             i += 1
3872             continue
3873         # Find end of sequence
3874         j = find_end_of_sequence(document.body, i)
3875         if j == -1:
3876             document.warning("Malformed LyX document. Cannot find end of Frame sequence!")
3877             i += 1
3878             continue
3879         endseq = j
3880         subst = ["\\begin_layout %s" % frame_dict[val]]
3881         esubst = ["", "\\begin_layout EndFrame", "", "\\end_layout"]
3882         endseq = endseq + len(esubst) - len(document.body[j : j])
3883         if document.body[j] == "\\end_deeper":
3884             document.body[j : j] = esubst
3885         else:
3886             document.body[j+1 : j+1] = esubst
3887         for q in range(i, j):
3888             if document.body[q] == "\\begin_layout %s" % val:
3889                 document.body[q] = "\\begin_layout %s" % document.default_layout
3890         r = i
3891         while r < j:
3892             if document.body[r] == "\\begin_deeper":
3893                 s = find_end_of(document.body, r, "\\begin_deeper", "\\end_deeper")
3894                 if s != -1:
3895                     document.body[r] = ""
3896                     document.body[s] = ""
3897                     r = s
3898                     continue
3899             r = r + 1
3900         l = find_end_of_layout(document.body, i)
3901         for p in range(1, 5):
3902             arg = find_token(document.body, "\\begin_inset Argument %d" % p, i, l)
3903             if arg != -1:
3904                 if p == 1:
3905                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3906                     endPlain = find_end_of_layout(document.body, beginPlain)
3907                     endInset = find_end_of_inset(document.body, arg)
3908                     content = document.body[beginPlain + 1 : endPlain]
3909                     # Adjust range end
3910                     l = l - len(document.body[arg : endInset + 1])
3911                     # Remove arg inset
3912                     del document.body[arg : endInset + 1]
3913                     subst += put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
3914                 elif p == 2:
3915                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3916                     endPlain = find_end_of_layout(document.body, beginPlain)
3917                     endInset = find_end_of_inset(document.body, arg)
3918                     content = document.body[beginPlain + 1 : endPlain]
3919                     # Adjust range end
3920                     l = l - len(document.body[arg : endInset + 1])
3921                     # Remove arg inset
3922                     del document.body[arg : endInset + 1]
3923                     subst += put_cmd_in_ert("[<") + content + put_cmd_in_ert(">]")
3924                 elif p == 3:
3925                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3926                     endPlain = find_end_of_layout(document.body, beginPlain)
3927                     endInset = find_end_of_inset(document.body, arg)
3928                     content = document.body[beginPlain + 1 : endPlain]
3929                     # Adjust range end
3930                     l = l - len(document.body[arg : endInset + 1])
3931                     # Remove arg inset
3932                     del document.body[arg : endInset + 1]
3933                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
3934                 elif p == 4:
3935                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
3936                     endPlain = find_end_of_layout(document.body, beginPlain)
3937                     endInset = find_end_of_inset(document.body, arg)
3938                     content = document.body[beginPlain + 1 : endPlain]
3939                     # Adjust range end
3940                     l = l - len(document.body[arg : endInset + 1])
3941                     # Remove arg inset
3942                     del document.body[arg : endInset + 1]
3943                     subst += content
3944                     
3945         document.body[i : i + 1] = subst
3946         i = j
3947
3948 # known encodings that do not change their names (same LyX and LaTeX names)
3949 known_enc_tuple = ("auto", "default", "ansinew", "applemac", "armscii8", "ascii",
3950     "cp437", "cp437de", "cp850", "cp852", "cp855", "cp858", "cp862", "cp865", "cp866",
3951     "cp1250", "cp1251", "cp1252", "cp1255", "cp1256", "cp1257", "koi8-r", "koi8-u",
3952     "pt154", "pt254", "tis620-0", "utf8", "utf8x", "utf8-plain")
3953
3954 def convert_encodings(document):
3955     "Use the LyX names of the encodings instead of the LaTeX names."
3956     LaTeX2LyX_enc_dict = {
3957         "8859-6":     "iso8859-6",
3958         "8859-8":     "iso8859-8",
3959         "Bg5":        "big5",
3960         "euc":        "euc-jp-platex",
3961         "EUC-JP":     "euc-jp",
3962         "EUC-TW":     "euc-tw",
3963         "GB":         "euc-cn",
3964         "GBK":        "gbk",
3965         "iso88595":   "iso8859-5",
3966         "iso-8859-7": "iso8859-7",
3967         "JIS":        "jis",
3968         "jis":        "jis-platex",
3969         "KS":         "euc-kr",
3970         "l7xenc":     "iso8859-13",
3971         "latin1":     "iso8859-1",
3972         "latin2":     "iso8859-2",
3973         "latin3":     "iso8859-3",
3974         "latin4":     "iso8859-4",
3975         "latin5":     "iso8859-9",
3976         "latin9":     "iso8859-15",
3977         "latin10":    "iso8859-16",
3978         "SJIS":       "shift-jis",
3979         "sjis":       "shift-jis-platex",
3980         "UTF8":       "utf8-cjk"
3981     }
3982     i = find_token(document.header, "\\inputencoding" , 0)
3983     if i == -1:
3984         return
3985     val = get_value(document.header, "\\inputencoding", i)
3986     if val in list(LaTeX2LyX_enc_dict.keys()):
3987         document.header[i] = "\\inputencoding %s" % LaTeX2LyX_enc_dict[val]
3988     elif val not in known_enc_tuple:
3989         document.warning("Ignoring unknown input encoding: `%s'" % val)
3990
3991
3992 def revert_encodings(document):
3993     """Revert to using the LaTeX names of the encodings instead of the LyX names.
3994     Also revert utf8-platex to sjis, the language default when using Japanese.
3995     """
3996     LyX2LaTeX_enc_dict = {
3997         "big5":             "Bg5",
3998         "euc-cn":           "GB",
3999         "euc-kr":           "KS",
4000         "euc-jp":           "EUC-JP",
4001         "euc-jp-platex":    "euc",
4002         "euc-tw":           "EUC-TW",
4003         "gbk":              "GBK",
4004         "iso8859-1":        "latin1",
4005         "iso8859-2":        "latin2",
4006         "iso8859-3":        "latin3",
4007         "iso8859-4":        "latin4",
4008         "iso8859-5":        "iso88595",
4009         "iso8859-6":        "8859-6",
4010         "iso8859-7":        "iso-8859-7",
4011         "iso8859-8":        "8859-8",
4012         "iso8859-9":        "latin5",
4013         "iso8859-13":       "l7xenc",
4014         "iso8859-15":       "latin9",
4015         "iso8859-16":       "latin10",
4016         "jis":              "JIS",
4017         "jis-platex":       "jis",
4018         "shift-jis":        "SJIS",
4019         "shift-jis-platex": "sjis",
4020         "utf8-cjk":         "UTF8",
4021         "utf8-platex":      "sjis"
4022     }
4023     i = find_token(document.header, "\\inputencoding" , 0)
4024     if i == -1:
4025         return
4026     val = get_value(document.header, "\\inputencoding", i)
4027     if val in list(LyX2LaTeX_enc_dict.keys()):
4028         document.header[i] = "\\inputencoding %s" % LyX2LaTeX_enc_dict[val]
4029     elif val not in known_enc_tuple:
4030         document.warning("Ignoring unknown input encoding: `%s'" % val)
4031
4032
4033 def revert_IEEEtran_3(document):
4034   '''
4035   Reverts Flex Insets to TeX-code
4036   '''
4037   if document.textclass == "IEEEtran":
4038     h = 0
4039     i = 0
4040     j = 0
4041     while True:
4042       if h != -1:
4043         h = find_token(document.body, "\\begin_inset Flex Author Mark", h)
4044       if h != -1:
4045         endh = find_end_of_inset(document.body, h)
4046         document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
4047         document.body[h : h + 4] = put_cmd_in_ert("\\IEEEauthorrefmark{")
4048         h = h + 5
4049       if i != -1:
4050         i = find_token(document.body, "\\begin_inset Flex Author Name", i)
4051       if i != -1:
4052         endi = find_end_of_inset(document.body, i)
4053         document.body[endi - 2 : endi + 1] = put_cmd_in_ert("}")
4054         document.body[i : i + 4] = put_cmd_in_ert("\\IEEEauthorblockN{")
4055         i = i + 5
4056       if j != -1:
4057         j = find_token(document.body, "\\begin_inset Flex Author Affiliation", j)
4058       if j != -1:
4059         endj = find_end_of_inset(document.body, j)
4060         document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
4061         document.body[j : j + 4] = put_cmd_in_ert("\\IEEEauthorblockA{")
4062         j = j + 5
4063       if i == -1 and j == -1 and h == -1:
4064         return
4065
4066
4067 def revert_kurier_fonts(document):
4068   " Revert kurier font definition to LaTeX "
4069   
4070   i = find_token(document.header, "\\font_math", 0)
4071   if i != -1:
4072     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
4073       val = get_value(document.header, "\\font_math", i)
4074       if val == "kurier-math":
4075         add_to_preamble(document, "\\let\\Myrmdefault\\rmdefault\n" \
4076           "\\usepackage[math]{kurier}\n" \
4077           "\\renewcommand{\\rmdefault}{\\Myrmdefault}")
4078         document.header[i] = "\\font_math auto"
4079   
4080   if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
4081     kurier_fonts = ["kurier", "kurierc", "kurierl", "kurierlc"]
4082     k = find_token(document.header, "\\font_sans kurier", 0)
4083     if k != -1:
4084       sf = get_value(document.header, "\\font_sans", k)
4085       if sf in kurier_fonts:
4086         add_to_preamble(document, "\\renewcommand{\\sfdefault}{%s}" % sf)
4087         document.header[k] = "\\font_sans default"
4088
4089 def revert_iwona_fonts(document):
4090   " Revert iwona font definition to LaTeX "
4091   
4092   i = find_token(document.header, "\\font_math", 0)
4093   if i != -1:
4094     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
4095       val = get_value(document.header, "\\font_math", i)
4096       if val == "iwona-math":
4097         add_to_preamble(document, "\\let\\Myrmdefault\\rmdefault\n" \
4098           "\\usepackage[math]{iwona}\n" \
4099           "\\renewcommand{\\rmdefault}{\\Myrmdefault}")
4100         document.header[i] = "\\font_math auto"
4101   
4102   if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1: 
4103     iwona_fonts = ["iwona", "iwonac", "iwonal", "iwonalc"]
4104     k = find_token(document.header, "\\font_sans iwona", 0)
4105     if k != -1:
4106       sf = get_value(document.header, "\\font_sans", k)
4107       if sf in iwona_fonts:
4108         add_to_preamble(document, "\\renewcommand{\\sfdefault}{%s}" % sf)
4109         document.header[k] = "\\font_sans default"
4110
4111
4112 def revert_new_libertines(document):
4113     " Revert new libertine font definition to LaTeX "
4114   
4115     if find_token(document.header, "\\use_non_tex_fonts true", 0) != -1:
4116         return
4117
4118     i = find_token(document.header, "\\font_typewriter libertine-mono", 0)
4119     if i != -1:
4120         preamble = "\\usepackage"
4121         sc = find_token(document.header, "\\font_tt_scale", 0)
4122         if sc != -1:
4123             scval = get_value(document.header, "\\font_tt_scale", sc)
4124             if scval != "100":
4125                 preamble += "[scale=%f]" % (float(scval) / 100)
4126                 document.header[sc] = "\\font_tt_scale 100"
4127         preamble += "{libertineMono-type1}"
4128         add_to_preamble(document, [preamble])
4129         document.header[i] = "\\font_typewriter default"
4130    
4131     k = find_token(document.header, "\\font_sans biolinum", 0)
4132     if k != -1:
4133         preamble = "\\usepackage"
4134         options = ""
4135         j = find_token(document.header, "\\font_osf true", 0)
4136         if j != -1:
4137             options += "osf"
4138         else:
4139             options += "lining"
4140         sc = find_token(document.header, "\\font_sf_scale", 0)
4141         if sc != -1:
4142             scval = get_value(document.header, "\\font_sf_scale", sc)
4143             if scval != "100":
4144                 options += ",scale=%f" % (float(scval) / 100)
4145                 document.header[sc] = "\\font_sf_scale 100"
4146         if options != "":
4147             preamble += "[" + options +"]"
4148         preamble += "{biolinum-type1}"
4149         add_to_preamble(document, [preamble])
4150         document.header[k] = "\\font_sans default"
4151
4152
4153 def convert_lyxframes(document):
4154     " Converts old beamer frames to new style "
4155     
4156     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
4157     if document.textclass not in beamer_classes:
4158         return
4159    
4160     framebeg = ["BeginFrame", "BeginPlainFrame"]
4161     frameend = ["Frame", "PlainFrame", "EndFrame", "BeginFrame", "BeginPlainFrame", "AgainFrame",
4162                 "Section", "Section*", "Subsection", "Subsection*", "Subsubsection", "Subsubsection*"]
4163     for lay in framebeg:
4164         i = 0
4165         while True:
4166             i = find_token_exact(document.body, "\\begin_layout " + lay, i)
4167             if i == -1:
4168                 break
4169             parent = get_containing_layout(document.body, i)
4170             if parent == False or parent[1] != i:
4171                 document.warning("Wrong parent layout!")
4172                 i += 1
4173                 continue
4174             frametype = parent[0]
4175             j = parent[2]
4176             parbeg = parent[3]
4177             if i != -1:
4178                 # Step I: Convert ERT arguments
4179                 # FIXME: See restrictions in convert_beamerframeargs method
4180                 ertend = convert_beamerframeargs(document, i, parbeg)
4181                 if ertend == -1:
4182                     break
4183                 # Step II: Now rename the layout and convert the title to an argument
4184                 j = find_end_of_layout(document.body, i)
4185                 document.body[j : j + 1] = ['\\end_layout', '', '\\end_inset', '', '\\end_layout']
4186                 if lay == "BeginFrame":
4187                     document.body[i] = "\\begin_layout Frame"
4188                 else:
4189                     document.body[i] = "\\begin_layout PlainFrame"
4190                 document.body[ertend + 1 : ertend + 1] = ['\\begin_inset Argument 4',
4191                                                 'status open', '', '\\begin_layout Plain Layout']
4192                 # Step III: find real frame end
4193                 j = j + 8
4194                 jj = j
4195                 inInset = get_containing_inset(document.body, i)
4196                 while True:
4197                     fend = find_token(document.body, "\\begin_layout", jj)
4198                     if fend == -1:
4199                         document.warning("Malformed LyX document: No real frame end!")
4200                         return
4201                     val = get_value(document.body, "\\begin_layout", fend)
4202                     if val not in frameend:
4203                         jj = fend + 1
4204                         continue
4205                     # is this frame nested in an inset (e.g., Note)?
4206                     if inInset != False:
4207                         # if so, end the frame inside the inset
4208                         if inInset[2] < fend:
4209                             fend = inInset[2]
4210                     if val == frametype:
4211                         document.body[fend : fend] = ['\\end_deeper', '', '\\begin_layout Separator', '', '\\end_layout']
4212                     # consider explicit EndFrames between two identical frame types
4213                     elif val == "EndFrame":
4214                         nextlayout = find_token(document.body, "\\begin_layout", fend + 1)
4215                         if nextlayout != -1 and get_value(document.body, "\\begin_layout", nextlayout) == frametype:
4216                             document.body[fend : fend] = ['\\end_deeper', '', '\\begin_layout Separator', '', '\\end_layout']
4217                         else:
4218                             document.body[fend : fend] = ['\\end_deeper']
4219                     else:
4220                         document.body[fend : fend] = ['\\end_deeper']
4221                     document.body[j + 1 : j + 1] = ['', '\\begin_deeper']
4222                     break
4223             i = j
4224
4225
4226 def remove_endframes(document):
4227     " Remove deprecated beamer endframes "
4228     
4229     beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
4230     if document.textclass not in beamer_classes:
4231         return
4232    
4233     i = 0
4234     while True:
4235         i = find_token_exact(document.body, "\\begin_layout EndFrame", i)
4236         if i == -1:
4237             break
4238         j = find_end_of_layout(document.body, i)
4239         if j == -1:
4240             document.warning("Malformed LyX document: Missing \\end_layout to EndFrame")
4241             i += 1
4242             continue
4243         del document.body[i : j + 1]
4244
4245
4246 def revert_powerdot_flexes(document):
4247     " Reverts powerdot flex insets "
4248     
4249     if document.textclass != "powerdot":
4250         return
4251
4252     flexes = {"Onslide" : "\\onslide",
4253               "Onslide*" : "\\onslide*",
4254               "Onslide+" : "\\onslide+"}
4255     rx = re.compile(r'^\\begin_inset Flex (.+)$')
4256
4257     i = 0
4258     while True:
4259         i = find_token(document.body, "\\begin_inset Flex", i)
4260         if i == -1:
4261             return
4262         m = rx.match(document.body[i])
4263         if m:
4264             flextype = m.group(1)
4265             z = find_end_of_inset(document.body, i)
4266             if z == -1:
4267                 document.warning("Can't find end of Flex " + flextype + " inset.")
4268                 i += 1
4269                 continue
4270             if flextype in flexes:
4271                 pre = put_cmd_in_ert(flexes[flextype])
4272                 arg = find_token(document.body, "\\begin_inset Argument 1", i, z)
4273                 if arg != -1:
4274                     argend = find_end_of_inset(document.body, arg)
4275                     if argend == -1:
4276                         document.warning("Can't find end of Argument!")
4277                         i += 1
4278                         continue
4279                     # Find containing paragraph layout
4280                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg)
4281                     endPlain = find_end_of_layout(document.body, beginPlain)
4282                     argcontent = document.body[beginPlain + 1 : endPlain]
4283                     # Adjust range end
4284                     z = z - len(document.body[arg : argend + 1])
4285                     # Remove arg inset
4286                     del document.body[arg : argend + 1]
4287                     pre += put_cmd_in_ert("{") + argcontent + put_cmd_in_ert("}")
4288                 pre += put_cmd_in_ert("{")
4289                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4290                 endPlain = find_end_of_layout(document.body, beginPlain)
4291                 # Adjust range end
4292                 z = z - len(document.body[i : beginPlain + 1])
4293                 z += len(pre)
4294                 document.body[i : beginPlain + 1] = pre
4295                 post = put_cmd_in_ert("}")
4296                 document.body[z - 2 : z + 1] = post     
4297         i += 1
4298
4299
4300 def revert_powerdot_pause(document):
4301     " Reverts powerdot pause layout to ERT "
4302     
4303     if document.textclass != "powerdot":
4304         return
4305
4306     i = 0
4307     while True:
4308         i = find_token(document.body, "\\begin_layout Pause", i)
4309         if i == -1:
4310             return
4311         j = find_end_of_layout(document.body, i)
4312         if j == -1:
4313             document.warning("Malformed LyX document: Can't find end of Pause layout")
4314             i += 1
4315             continue
4316         endlay = j
4317         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\pause")
4318         for p in range(i, j):
4319             if p >= endlay:
4320                 break
4321             arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
4322             if arg != -1:
4323                 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4324                 endPlain = find_end_of_layout(document.body, beginPlain)
4325                 endInset = find_end_of_inset(document.body, p)
4326                 content = document.body[beginPlain + 1 : endPlain]
4327                 # Adjust range end
4328                 endlay = endlay - len(document.body[p : endInset + 1])
4329                 # Remove arg inset
4330                 del document.body[p : endInset + 1]
4331                 subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4332                     
4333         document.body[i : i + 1] = subst
4334         i = endlay
4335
4336
4337 def revert_powerdot_itemargs(document):
4338     " Reverts powerdot item arguments to ERT "
4339     
4340     if document.textclass != "powerdot":
4341         return
4342
4343     i = 0
4344     list_layouts = ["Itemize", "ItemizeType1", "Enumerate", "EnumerateType1"]
4345     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
4346
4347     while True:
4348         i = find_token(document.body, "\\begin_inset Argument", i)
4349         if i == -1:
4350             return
4351         # Find containing paragraph layout
4352         parent = get_containing_layout(document.body, i)
4353         if parent == False:
4354             document.warning("Malformed LyX document: Can't find parent paragraph layout")
4355             i += 1
4356             continue
4357         parbeg = parent[1]
4358         parend = parent[2]
4359         realparbeg = parent[3]
4360         layoutname = parent[0]
4361         realparend = parend
4362         for p in range(parbeg, parend):
4363             if p >= realparend:
4364                 i = realparend
4365                 break
4366             if layoutname in list_layouts:
4367                 m = rx.match(document.body[p])
4368                 if m:
4369                     argnr = m.group(1)
4370                     if argnr == "item:1":
4371                         j = find_end_of_inset(document.body, i)
4372                         # Find containing paragraph layout
4373                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4374                         endPlain = find_end_of_layout(document.body, beginPlain)
4375                         content = document.body[beginPlain + 1 : endPlain]
4376                         del document.body[i:j+1]
4377                         subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4378                         document.body[realparbeg : realparbeg] = subst
4379                     elif argnr == "item:2":
4380                         j = find_end_of_inset(document.body, i)
4381                         # Find containing paragraph layout
4382                         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
4383                         endPlain = find_end_of_layout(document.body, beginPlain)
4384                         content = document.body[beginPlain + 1 : endPlain]
4385                         del document.body[i:j+1]
4386                         subst = put_cmd_in_ert("<") + content + put_cmd_in_ert(">")
4387                         document.body[realparbeg : realparbeg] = subst
4388         
4389         i = realparend
4390
4391
4392 def revert_powerdot_columns(document):
4393     " Reverts powerdot twocolumn to TeX-code "
4394     if document.textclass != "powerdot":
4395         return
4396
4397     rx = re.compile(r'^\\begin_inset Argument (\S+)$')
4398     i = 0
4399     while True:
4400         i = find_token(document.body, "\\begin_layout Twocolumn", i)
4401         if i == -1:
4402             return
4403         j = find_end_of_layout(document.body, i)
4404         if j == -1:
4405             document.warning("Malformed LyX document: Can't find end of Twocolumn layout")
4406             i += 1
4407             continue
4408         endlay = j
4409         document.body[j : j] = put_cmd_in_ert("}") + document.body[j : j]
4410         endlay += len(put_cmd_in_ert("}"))
4411         subst = ["\\begin_layout Standard"] + put_cmd_in_ert("\\twocolumn")
4412         for p in range(i, j):
4413             if p >= endlay:
4414                 break
4415             m = rx.match(document.body[p])
4416             if m:
4417                 argnr = m.group(1)
4418                 if argnr == "1":
4419                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4420                     endPlain = find_end_of_layout(document.body, beginPlain)
4421                     endInset = find_end_of_inset(document.body, p)
4422                     content = document.body[beginPlain + 1 : endPlain]
4423                     # Adjust range end
4424                     endlay = endlay - len(document.body[p : endInset + 1])
4425                     # Remove arg inset
4426                     del document.body[p : endInset + 1]
4427                     subst += put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
4428                 elif argnr == "2":
4429                     beginPlain = find_token(document.body, "\\begin_layout Plain Layout", p)
4430                     endPlain = find_end_of_layout(document.body, beginPlain)
4431                     endInset = find_end_of_inset(document.body, p)
4432                     content = document.body[beginPlain + 1 : endPlain]
4433                     # Adjust range end
4434                     endlay = endlay - len(document.body[p : endInset + 1])
4435                     # Remove arg inset
4436                     del document.body[p : endInset + 1]
4437                     subst += put_cmd_in_ert("{") + content + put_cmd_in_ert("}")
4438                     
4439         subst += put_cmd_in_ert("{")
4440         document.body[i : i + 1] = subst
4441         i = endlay
4442
4443
4444 def revert_mbox_fbox(document):
4445     'Convert revert mbox/fbox boxes to TeX-code'
4446     i = 0
4447     while True:
4448         i = find_token(document.body, "\\begin_inset Box", i)
4449         if i == -1:
4450             return
4451         j = find_token(document.body, "width", i)
4452         if j != i + 7:
4453             document.warning("Malformed LyX document: Can't find box width")
4454             return
4455         width = get_value(document.body, "width", j)
4456         k = find_end_of_inset(document.body, j)
4457         if k == -1:
4458             document.warning("Malformed LyX document: Can't find end of box inset")
4459             i += 1
4460             continue
4461         BeginLayout = find_token(document.body, "\\begin_layout Plain Layout", j)
4462         EndLayout = find_end_of_layout(document.body, BeginLayout)
4463         # replace if width is ""
4464         if (width == '""'):
4465             document.body[EndLayout:k + 1] = put_cmd_in_ert("}")
4466             if document.body[i] == "\\begin_inset Box Frameless":
4467                 document.body[i:BeginLayout + 1] = put_cmd_in_ert("\\mbox{")
4468             if document.body[i] == "\\begin_inset Box Boxed":
4469                 document.body[i:BeginLayout + 1] = put_cmd_in_ert("\\fbox{")
4470         i += 1
4471
4472
4473 def revert_starred_caption(document):
4474     " Reverts unnumbered longtable caption insets "
4475     
4476     i = 0
4477     while True:
4478       i = find_token(document.body, "\\begin_inset Caption LongTableNoNumber", i)
4479       if i == -1:
4480           return
4481       # This is not equivalent, but since the caption inset is a full blown
4482       # text inset a true conversion to ERT is too difficult.
4483       document.body[i] = "\\begin_inset Caption Standard"
4484       i += 1
4485
4486
4487 def revert_forced_local_layout(document):
4488     i = 0
4489     while True:
4490         i = find_token(document.header, "\\begin_forced_local_layout", i)
4491         if i == -1:
4492             return
4493         j = find_end_of(document.header, i, "\\begin_forced_local_layout", "\\end_forced_local_layout")
4494         if j == -1:
4495             # this should not happen
4496             break
4497         regexp = re.compile(r'\s*forcelocal', re.IGNORECASE)
4498         k = find_re(document.header, regexp, i, j)
4499         while k != -1:
4500             del document.header[k]
4501             j = j - 1
4502             k = find_re(document.header, regexp, i, j)
4503         k = find_token(document.header, "\\begin_local_layout", 0)
4504         if k == -1:
4505             document.header[i] = "\\begin_local_layout"
4506             document.header[j] = "\\end_local_layout"
4507         else:
4508             l = find_end_of(document.header, k, "\\begin_local_layout", "\\end_local_layout")
4509             if j == -1:
4510                 # this should not happen
4511                 break
4512             lines = document.header[i+1 : j]
4513             if k > i:
4514                 document.header[k+1 : k+1] = lines
4515                 document.header[i   : j  ] = []
4516             else:
4517                 document.header[i   : j  ] = []
4518                 document.header[k+1 : k+1] = lines
4519
4520
4521 def revert_aa1(document):
4522   " Reverts InsetArguments of aa to TeX-code "
4523   if document.textclass == "aa":
4524     i = 0
4525     while True:
4526       if i != -1:
4527         i = find_token(document.body, "\\begin_layout Abstract (structured)", i)
4528       if i != -1:
4529         revert_Argument_to_TeX_brace(document, i, 0, 1, 4, False, False)
4530         i += 1
4531       if i == -1:
4532         return
4533
4534
4535 def revert_aa2(document):
4536   " Reverts InsetArguments of aa to TeX-code "
4537   if document.textclass == "aa":
4538     i = 0
4539     while True:
4540       if i != -1:
4541         i = find_token(document.body, "\\begin_layout Abstract (structured)", i)
4542       if i != -1:
4543         document.body[i] = "\\begin_layout Abstract"
4544         i += 1
4545       if i == -1:
4546         return
4547
4548
4549 def revert_tibetan(document):
4550     "Set the document language for Tibetan to English" 
4551
4552     if document.language == "tibetan":
4553         document.language = "english"
4554         i = find_token(document.header, "\\language", 0) 
4555         if i != -1: 
4556             document.header[i] = "\\language english" 
4557     j = 0
4558     while j < len(document.body): 
4559         j = find_token(document.body, "\\lang tibetan", j)
4560         if j != -1:
4561             document.body[j] = document.body[j].replace("\\lang tibetan", "\\lang english")
4562             j += 1
4563         else:
4564             j = len(document.body)
4565
4566
4567 #############
4568 #
4569 # Chunk stuff
4570 #
4571 #############
4572
4573 # The idea here is that we will have a sequence of chunk paragraphs.
4574 # We want to convert them to paragraphs in one or several chunk insets.
4575 # Individual chunks are terminated by the character @ on the last line.
4576 # This line will be discarded, and following lines are treated as new
4577 # chunks, which go into their own insets.
4578 # The first line of a chunk should look like: <<CONTENT>>=
4579 # We will discard the delimiters, and put the CONTENT into the
4580 # optional argument of the inset, if the CONTENT is non-empty.
4581 def convert_chunks(document):
4582     first_re = re.compile(r'<<(.*)>>=(.*)')
4583     file_pos = 0
4584     while True:
4585         # find start of a block of chunks
4586         i = find_token(document.body, "\\begin_layout Chunk", file_pos)
4587         if i == -1:
4588             return
4589         start = i
4590         end = -1
4591         contents = []
4592         chunk_started = False
4593
4594         while True:
4595             # process the one we just found
4596             j = find_end_of_layout(document.body, i)
4597             if j == -1:
4598                 document.warning("Malformed LyX documents. Can't find end of Chunk layout!")
4599                 # there is no point continuing, as we will run into the same error again.
4600                 return
4601             this_chunk = "".join(document.body[i + 1:j])
4602             
4603             # there may be empty lines between chunks
4604             # we just skip them.
4605             if not chunk_started:
4606                 if this_chunk != "":
4607                     # new chunk starts
4608                     chunk_started = True
4609             
4610             if chunk_started:
4611                 contents.append(document.body[i + 1:j])
4612
4613             # look for potential chunk terminator
4614             # on the last line of the chunk paragraph
4615             if document.body[j - 1] == "@":
4616                 break
4617
4618             # look for subsequent chunk paragraph
4619             i = find_token(document.body, "\\begin_layout", j)
4620             if i == -1:
4621                 break
4622
4623             if get_value(document.body, "\\begin_layout", i) != "Chunk":
4624                 break
4625
4626         file_pos = end = j + 1
4627
4628         # The last chunk should simply have an "@" in it
4629         # or at least end with "@" (can happen if @ is
4630         # preceded by a newline)
4631         lastpar = ''
4632         if len(contents) > 0:
4633             lastpar = ''.join(contents[-1])
4634         if not lastpar.endswith("@"):
4635             document.warning("Unexpected chunk content: chunk not terminated by '@'!")
4636             if len(contents) == 0:
4637                 # convert empty chunk layouts to Standard
4638                 document.body[start] = "\\begin_layout Standard"
4639             continue
4640
4641         if lastpar == "@":
4642             # chunk par only contains "@". Just drop it.
4643             contents.pop()
4644         else:
4645             # chunk par contains more. Only drop the "@".
4646             contents[-1].pop()
4647
4648         # The first line should look like: <<CONTENT>>=
4649         # We want the CONTENT
4650         optarg = ' '.join(contents[0])
4651         optarg.strip()
4652         # We can already have real chunk content in
4653         # the first par (separated from the options by a newline).
4654         # We collect such stuff to re-insert it later.
4655         postoptstuff = []
4656         
4657         match = first_re.search(optarg)
4658         if match:
4659             optarg = match.groups()[0]
4660             if match.groups()[1] != "":
4661                 postopt = False
4662                 for c in contents[0]:
4663                     if c.endswith(">>="):
4664                         postopt = True
4665                         continue
4666                     if postopt:
4667                         postoptstuff.append(c)
4668             # We have stripped everything. This can be deleted.
4669             contents.pop(0)
4670
4671         newstuff = ['\\begin_layout Standard']
4672
4673         # Maintain paragraph parameters
4674         par_params = ["\\noindent", "\\indent", "\\indent-toggle", "\\leftindent",
4675                       "\\start_of_appendix", "\\paragraph_spacing", "\\align",
4676                       "\\labelwidthstring"]
4677         parms = start + 1
4678         while True:
4679             if document.body[parms].split(' ', 1)[0] not in par_params:
4680                 break
4681             newstuff.extend([document.body[parms]])
4682             parms += 1
4683
4684         newstuff.extend(
4685             ['\\begin_inset Flex Chunk',
4686              'status open', '',
4687              '\\begin_layout Plain Layout', ''])
4688
4689         # If we have a non-empty optional argument, insert it.
4690         if match and optarg != "":
4691             newstuff.extend(
4692                 ['\\begin_inset Argument 1',
4693                  'status open', '',
4694                  '\\begin_layout Plain Layout',
4695                  optarg,
4696                  '\\end_layout', '',
4697                  '\\end_inset', ''])
4698
4699         # Since we already opened a Plain layout, the first paragraph
4700         # does not need to do that.
4701         did_one_par = False
4702         if postoptstuff:
4703             # we need to replace newlines with new layouts
4704             start_newline = -1
4705             started_text = False
4706             for lno in range(0,len(postoptstuff)):
4707                 if postoptstuff[lno].startswith("\\begin_inset Newline newline"):
4708                     start_newline = lno
4709                 elif start_newline != -1:
4710                     if postoptstuff[lno].startswith("\\end_inset"):
4711                         # replace that bit, but only if we already have some text
4712                         # and we're not at the end except for a blank line
4713                         if started_text and \
4714                           (lno != len(postoptstuff) - 2 or postoptstuff[-1] != ""):
4715                             newstuff.extend(['\\end_layout', '\n', '\\begin_layout Plain Layout', '\n'])
4716                         start_newline = -1
4717                         started_text = True
4718                 else:
4719                     newstuff.extend([postoptstuff[lno]])
4720             newstuff.append('\\end_layout')
4721             did_one_par = True
4722         for c in contents:
4723             if did_one_par:
4724                 newstuff.extend(['', '\\begin_layout Plain Layout', ''])
4725             else:
4726                 did_one_par = True
4727             newstuff.extend(c)
4728             newstuff.append('\\end_layout')
4729
4730         newstuff.extend(['', '\\end_inset', '', '\\end_layout', ''])
4731
4732         document.body[start:end] = newstuff
4733
4734         file_pos += len(newstuff) - (end - start)
4735
4736
4737 def revert_chunks(document):
4738     i = 0
4739     while True:
4740         i = find_token(document.body, "\\begin_inset Flex Chunk", i)
4741         if i == -1:
4742             return
4743
4744         iend = find_end_of_inset(document.body, i)
4745         if iend == -1:
4746             document.warning("Can't find end of Chunk!")
4747             i += 1
4748             continue
4749
4750         # Look for optional argument
4751         optarg = ""
4752         ostart = find_token(document.body, "\\begin_inset Argument 1", i, iend)
4753         if ostart != -1:
4754             oend = find_end_of_inset(document.body, ostart)
4755             k = find_token(document.body, "\\begin_layout Plain Layout", ostart, oend)
4756             if k == -1:
4757                 document.warning("Malformed LyX document: Can't find argument contents!")
4758             else:
4759                 m = find_end_of_layout(document.body, k)
4760                 optarg = "".join(document.body[k+1:m])
4761
4762             # We now remove the optional argument, so we have something
4763             # uniform on which to work
4764             document.body[ostart : oend + 1] = []
4765             # iend is now invalid
4766             iend = find_end_of_inset(document.body, i)
4767
4768         retval = get_containing_layout(document.body, i)
4769         if not retval:
4770             document.warning("Can't find containing layout for Chunk!")
4771             i = iend
4772             continue
4773         (lname, lstart, lend, pstart)  = retval
4774         # we now want to work through the various paragraphs, and collect their contents
4775         parlist = []
4776         k = i
4777         while True:
4778             k = find_token(document.body, "\\begin_layout Plain Layout", k, lend)
4779             if k == -1:
4780                 break
4781             j = find_end_of_layout(document.body, k)
4782             if j == -1:
4783                 document.warning("Can't find end of layout inside chunk!")
4784                 break
4785             parlist.append(document.body[k+1:j])
4786             k = j
4787         # we now need to wrap all of these paragraphs in chunks
4788         newlines = []
4789         newlines.extend(["\\begin_layout Chunk", "", "<<" + optarg + ">>=", "\\end_layout", ""])
4790         for stuff in parlist:
4791             newlines.extend(["\\begin_layout Chunk"] + stuff + ["\\end_layout", ""])
4792         newlines.extend(["\\begin_layout Chunk", "", "@", "\\end_layout", ""])
4793         # replace old content with new content
4794         document.body[lstart : lend + 1] = newlines
4795         i = lstart + len(newlines)
4796         
4797
4798 ##
4799 # Conversion hub
4800 #
4801
4802 supported_versions = ["2.1.0","2.1"]
4803 convert = [
4804            [414, []],
4805            [415, [convert_undertilde]],
4806            [416, []],
4807            [417, [convert_japanese_encodings]],
4808            [418, [convert_justification]],
4809            [419, []],
4810            [420, [convert_biblio_style]],
4811            [421, [convert_longtable_captions]],
4812            [422, [convert_use_packages]],
4813            [423, [convert_use_mathtools]],
4814            [424, [convert_cite_engine_type]],
4815            # No convert_cancel, since cancel will be loaded automatically
4816            # in format 425 without any possibility to switch it off.
4817            # This has been fixed in format 464.
4818            [425, []],
4819            [426, []],
4820            [427, []],
4821            [428, [convert_cell_rotation]],
4822            [429, [convert_table_rotation]],
4823            [430, [convert_listoflistings]],
4824            [431, [convert_use_amssymb]],
4825            [432, []],
4826            [433, [convert_armenian]],
4827            [434, []],
4828            [435, []],
4829            [436, []],
4830            [437, []],
4831            [438, []],
4832            [439, []],
4833            [440, [convert_mathfonts]],
4834            [441, [convert_mdnomath]],
4835            [442, []],
4836            [443, []],
4837            [444, []],
4838            [445, []],
4839            [446, [convert_latexargs]],
4840            [447, [convert_IEEEtran, convert_AASTeX, convert_AGUTeX, convert_IJMP, convert_SIGPLAN, convert_SIGGRAPH, convert_EuropeCV, convert_Initials, convert_ModernCV]],
4841            [448, [convert_literate]],
4842            [449, []],
4843            [450, []],
4844            [451, [convert_beamerargs, convert_againframe_args, convert_corollary_args, convert_quote_args]],
4845            [452, [convert_beamerblocks]],
4846            [453, [convert_use_stmaryrd]],
4847            [454, [convert_overprint]],
4848            [455, []],
4849            [456, [convert_epigraph]],
4850            [457, [convert_use_stackrel]],
4851            [458, [convert_captioninsets, convert_captionlayouts]],
4852            [459, []],
4853            [460, []],
4854            [461, []],
4855            [462, []],
4856            [463, [convert_encodings]],
4857            [464, [convert_use_cancel]],
4858            [465, [convert_lyxframes, remove_endframes]],
4859            [466, []],
4860            [467, []],
4861            [468, []],
4862            [469, []],
4863            [470, []],
4864            [471, [convert_cite_engine_type_default]],
4865            [472, []],
4866            [473, []],
4867            [474, [convert_chunks, cleanup_beamerargs]],
4868           ]
4869
4870 revert =  [
4871            [473, [revert_chunks]],
4872            [472, [revert_tibetan]],
4873            [471, [revert_aa1,revert_aa2]],
4874            [470, [revert_cite_engine_type_default]],
4875            [469, [revert_forced_local_layout]],
4876            [468, [revert_starred_caption]],
4877            [467, [revert_mbox_fbox]],
4878            [466, [revert_iwona_fonts]],
4879            [465, [revert_powerdot_flexes, revert_powerdot_pause, revert_powerdot_itemargs, revert_powerdot_columns]],
4880            [464, []],
4881            [463, [revert_use_cancel]],
4882            [462, [revert_encodings]],
4883            [461, [revert_new_libertines]],
4884            [460, [revert_kurier_fonts]],
4885            [459, [revert_IEEEtran_3]],
4886            [458, [revert_fragileframe, revert_newframes]],
4887            [457, [revert_captioninsets, revert_captionlayouts]],
4888            [456, [revert_use_stackrel]],
4889            [455, [revert_epigraph]],
4890            [454, [revert_frametitle]],
4891            [453, [revert_overprint]],
4892            [452, [revert_use_stmaryrd]],
4893            [451, [revert_beamerblocks]],
4894            [450, [revert_beamerargs, revert_beamerargs2, revert_beamerargs3, revert_beamerflex]],
4895            [449, [revert_garamondx, revert_garamondx_newtxmath]],
4896            [448, [revert_itemargs]],
4897            [447, [revert_literate]],
4898            [446, [revert_IEEEtran, revert_IEEEtran_2, revert_AASTeX, revert_AGUTeX, revert_IJMP, revert_SIGPLAN, revert_SIGGRAPH, revert_EuropeCV, revert_Initials, revert_ModernCV_3, revert_ModernCV_4]],
4899            [445, [revert_latexargs]],
4900            [444, [revert_uop]],
4901            [443, [revert_biolinum]],
4902            [442, []],
4903            [441, [revert_newtxmath]],
4904            [440, [revert_mdnomath]],
4905            [439, [revert_mathfonts]],
4906            [438, [revert_minionpro]],
4907            [437, [revert_ipadeco, revert_ipachar]],
4908            [436, [revert_texgyre]],
4909            [435, [revert_mathdesign]],
4910            [434, [revert_txtt]],
4911            [433, [revert_libertine]],
4912            [432, [revert_armenian]],
4913            [431, [revert_languages, revert_ancientgreek]],
4914            [430, [revert_use_amssymb]],
4915            [429, [revert_listoflistings]],
4916            [428, [revert_table_rotation]],
4917            [427, [revert_cell_rotation]],
4918            [426, [revert_tipa]],
4919            [425, [revert_verbatim]],
4920            [424, [revert_cancel]],
4921            [423, [revert_cite_engine_type]],
4922            [422, [revert_use_mathtools]],
4923            [421, [revert_use_packages]],
4924            [420, [revert_longtable_captions]],
4925            [419, [revert_biblio_style]],
4926            [418, [revert_australian]],
4927            [417, [revert_justification]],
4928            [416, [revert_japanese_encodings]],
4929            [415, [revert_negative_space, revert_math_spaces]],
4930            [414, [revert_undertilde]],
4931            [413, [revert_visible_space]]
4932           ]
4933
4934
4935 if __name__ == "__main__":
4936     pass