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