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