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