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