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