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