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