1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # Copyright (C) 2015 The LyX team
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 """ Convert files to the file format generated by lyx 2.2"""
25 # Uncomment only what you need to import, please.
27 #from parser_tools import find_token, find_end_of, find_tokens, \
28 # find_token_exact, find_end_of_inset, find_end_of_layout, \
29 # find_token_backwards, is_in_inset, get_value, get_quoted_value, \
30 # del_token, check_token, get_option_value
32 from lyx2lyx_tools import (add_to_preamble, put_cmd_in_ert, get_ert,
33 lyx2latex, lyx2verbatim, length_in_bp, convert_info_insets)
34 # insert_to_preamble, latex_length, revert_flex_inset,
35 # revert_font_attrs, hex2ratio, str2bool
37 from parser_tools import (check_token, del_complete_lines,
38 find_end_of_inset, find_end_of_layout, find_nonempty_line, find_re,
39 find_token, find_token_backwards, get_containing_layout,
40 get_containing_inset, get_quoted_value, get_value, is_in_inset,
41 get_bool_value, set_bool_value)
44 ####################################################################
45 # Private helper functions
47 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt, nolastopt):
49 Reverts an InsetArgument to TeX-code
51 revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt, notLastOpt)
52 LineOfBegin is the line of the \begin_layout or \begin_inset statement
53 LineOfEnd is the line of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
54 StartArgument is the number of the first argument that needs to be converted
55 EndArgument is the number of the last argument that needs to be converted or the last defined one
56 isEnvironment must be true, if the layout is for a LaTeX environment
57 isOpt must be true, if the argument is an optional one
58 notLastOpt must be true if the argument is mandatory and followed by optional ones
62 while lineArg != -1 and n < nmax + 1:
63 lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
64 if lineArg > endline and endline != 0:
67 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
68 # we have to assure that no other inset is in the Argument
69 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
70 endInset = find_token(document.body, "\\end_inset", beginPlain)
73 while beginInset < endInset and beginInset != -1:
74 beginInset = find_token(document.body, "\\begin_inset", k)
75 endInset = find_token(document.body, "\\end_inset", l)
78 if environment == False:
80 if nolastopt == False:
81 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
83 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
84 del(document.body[lineArg : beginPlain + 1])
87 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
88 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
92 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
93 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
96 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
97 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
103 ###############################################################################
105 ### Conversion and reversion routines
107 ###############################################################################
109 def convert_longtable_label_internal(document, forward):
111 Convert reference to "LongTableNoNumber" into "Unnumbered" if forward is True
114 old_reference = "\\begin_inset Caption LongTableNoNumber"
115 new_reference = "\\begin_inset Caption Unnumbered"
117 # if the purpose is to revert swap the strings roles
119 old_reference, new_reference = new_reference, old_reference
123 i = find_token(document.body, old_reference, i)
128 document.body[i] = new_reference
131 def convert_longtable_label(document):
132 convert_longtable_label_internal(document, True)
135 def revert_longtable_label(document):
136 convert_longtable_label_internal(document, False)
139 def convert_separator(document):
141 Convert layout separators to separator insets and add (LaTeX) paragraph
142 breaks in order to mimic previous LaTeX export.
145 parins = ["\\begin_inset Separator parbreak", "\\end_inset", ""]
146 parlay = ["\\begin_layout Standard", "\\begin_inset Separator parbreak",
147 "\\end_inset", "", "\\end_layout", ""]
149 "family" : "default",
150 "series" : "default",
159 i = find_token(document.body, "\\begin_deeper", i)
163 j = find_token_backwards(document.body, "\\end_layout", i-1)
165 # reset any text style before inserting the inset
166 lay = get_containing_layout(document.body, j-1)
168 content = "\n".join(document.body[lay[1]:lay[2]])
169 for val in list(sty_dict.keys()):
170 if content.find("\\%s" % val) != -1:
171 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
174 document.body[j:j] = parins
175 i = i + len(parins) + 1
181 i = find_token(document.body, "\\align", i)
185 lay = get_containing_layout(document.body, i)
186 if lay != False and lay[0] == "Plain Layout":
190 j = find_token_backwards(document.body, "\\end_layout", i-1)
192 lay = get_containing_layout(document.body, j-1)
193 if lay != False and lay[0] == "Standard" \
194 and find_token(document.body, "\\align", lay[1], lay[2]) == -1 \
195 and find_token(document.body, "\\begin_inset VSpace", lay[1], lay[2]) == -1:
196 # reset any text style before inserting the inset
197 content = "\n".join(document.body[lay[1]:lay[2]])
198 for val in list(sty_dict.keys()):
199 if content.find("\\%s" % val) != -1:
200 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
203 document.body[j:j] = parins
204 i = i + len(parins) + 1
210 regexp = re.compile(r'^\\begin_layout (?:(-*)|(\s*))(Separator|EndOfSlide)(?:(-*)|(\s*))$', re.IGNORECASE)
214 i = find_re(document.body, regexp, i)
218 j = find_end_of_layout(document.body, i)
220 document.warning("Malformed LyX document: Missing `\\end_layout'.")
223 lay = get_containing_layout(document.body, j-1)
225 lines = document.body[lay[3]:lay[2]]
229 document.body[i:j+1] = parlay
231 document.body[i+1:i+1] = lines
233 i = i + len(parlay) + len(lines) + 1
236 def revert_separator(document):
237 " Revert separator insets to layout separators "
239 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
240 if document.textclass in beamer_classes:
241 beglaysep = "\\begin_layout Separator"
243 beglaysep = "\\begin_layout --Separator--"
245 parsep = [beglaysep, "", "\\end_layout", ""]
246 comert = ["\\begin_inset ERT", "status collapsed", "",
247 "\\begin_layout Plain Layout", "%", "\\end_layout",
248 "", "\\end_inset", ""]
249 empert = ["\\begin_inset ERT", "status collapsed", "",
250 "\\begin_layout Plain Layout", " ", "\\end_layout",
251 "", "\\end_inset", ""]
255 i = find_token(document.body, "\\begin_inset Separator", i)
259 lay = get_containing_layout(document.body, i)
261 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
268 kind = get_value(document.body, "\\begin_inset Separator", i, i+1, "plain").split()[1]
269 before = document.body[beg+1:i]
270 something_before = len(before) > 0 and len("".join(before)) > 0
271 j = find_end_of_inset(document.body, i)
272 after = document.body[j+1:end]
273 something_after = len(after) > 0 and len("".join(after)) > 0
275 beg = beg + len(before) + 1
276 elif something_before:
277 document.body[i:i] = ["\\end_layout", ""]
285 document.body[beg:j+1] = empert
288 document.body[beg:j+1] = comert
292 if layoutname == "Standard":
293 if not something_before:
294 document.body[beg:j+1] = parsep
296 document.body[i:i] = ["", "\\begin_layout Standard"]
299 document.body[beg:j+1] = ["\\begin_layout Standard"]
302 document.body[beg:j+1] = ["\\begin_deeper"]
304 end = end + 1 - (j + 1 - beg)
305 if not something_before:
306 document.body[i:i] = parsep
308 end = end + len(parsep)
309 document.body[i:i] = ["\\begin_layout Standard"]
310 document.body[end+2:end+2] = ["", "\\end_deeper", ""]
313 next_par_is_aligned = False
314 k = find_nonempty_line(document.body, end+1)
315 if k != -1 and check_token(document.body[k], "\\begin_layout"):
316 lay = get_containing_layout(document.body, k)
317 next_par_is_aligned = lay != False and \
318 find_token(document.body, "\\align", lay[1], lay[2]) != -1
319 if k != -1 and not next_par_is_aligned \
320 and not check_token(document.body[k], "\\end_deeper") \
321 and not check_token(document.body[k], "\\begin_deeper"):
322 if layoutname == "Standard":
323 document.body[beg:j+1] = [beglaysep]
326 document.body[beg:j+1] = ["\\begin_deeper", beglaysep]
327 end = end + 2 - (j + 1 - beg)
328 document.body[end+1:end+1] = ["", "\\end_deeper", ""]
332 del document.body[i:end+1]
334 del document.body[i:end-1]
339 def convert_parbreak(document):
341 Convert parbreak separators not specifically used to separate
342 environments to latexpar separators.
344 parbreakinset = "\\begin_inset Separator parbreak"
347 i = find_token(document.body, parbreakinset, i)
350 lay = get_containing_layout(document.body, i)
352 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
355 if lay[0] == "Standard":
356 # Convert only if not alone in the paragraph
357 k1 = find_nonempty_line(document.body, lay[1] + 1, i + 1)
358 k2 = find_nonempty_line(document.body, i + 1, lay[2])
359 if (k1 < i) or (k2 > i + 1) or not check_token(document.body[i], parbreakinset):
360 document.body[i] = document.body[i].replace("parbreak", "latexpar")
362 document.body[i] = document.body[i].replace("parbreak", "latexpar")
366 def revert_parbreak(document):
368 Revert latexpar separators to parbreak separators.
372 i = find_token(document.body, "\\begin_inset Separator latexpar", i)
375 document.body[i] = document.body[i].replace("latexpar", "parbreak")
379 def revert_smash(document):
380 " Set amsmath to on if smash commands are used "
382 commands = ["smash[t]", "smash[b]", "notag"]
383 i = find_token(document.header, "\\use_package amsmath", 0)
385 document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
387 value = get_value(document.header, "\\use_package amsmath", i).split()[1]
389 # nothing to do if package is not auto but on or off
393 j = find_token(document.body, '\\begin_inset Formula', j)
396 k = find_end_of_inset(document.body, j)
398 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
401 code = "\n".join(document.body[j:k])
403 if code.find("\\%s" % c) != -1:
404 # set amsmath to on, since it is loaded by the newer format
405 document.header[i] = "\\use_package amsmath 2"
410 def revert_swissgerman(document):
411 " Set language german-ch-old to german "
413 if document.language == "german-ch-old":
414 document.language = "german"
415 i = find_token(document.header, "\\language", 0)
417 document.header[i] = "\\language german"
420 j = find_token(document.body, "\\lang german-ch-old", j)
423 document.body[j] = document.body[j].replace("\\lang german-ch-old", "\\lang german")
427 def revert_use_package(document, pkg, commands, oldauto, supported):
428 # oldauto defines how the version we are reverting to behaves:
429 # if it is true, the old version uses the package automatically.
430 # if it is false, the old version never uses the package.
431 # If "supported" is true, the target version also supports this
433 regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
434 p = find_re(document.header, regexp, 0)
435 value = "1" # default is auto
437 value = get_value(document.header, "\\use_package" , p).split()[1]
439 del document.header[p]
440 if value == "2" and not supported: # on
441 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
442 elif value == "1" and not oldauto: # auto
445 i = find_token(document.body, '\\begin_inset Formula', i)
448 j = find_end_of_inset(document.body, i)
450 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
453 code = "\n".join(document.body[i:j])
455 if code.find("\\%s" % c) != -1:
457 document.header[p] = "\\use_package " + pkg + " 2"
459 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
464 mathtools_commands = ["xhookrightarrow", "xhookleftarrow", "xRightarrow", \
465 "xrightharpoondown", "xrightharpoonup", "xrightleftharpoons", \
466 "xLeftarrow", "xleftharpoondown", "xleftharpoonup", \
467 "xleftrightarrow", "xLeftrightarrow", "xleftrightharpoons", \
470 def revert_xarrow(document):
471 "remove use_package mathtools"
472 revert_use_package(document, "mathtools", mathtools_commands, False, True)
475 def revert_beamer_lemma(document):
476 " Reverts beamer lemma layout to ERT "
478 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
479 if document.textclass not in beamer_classes:
485 i = find_token(document.body, "\\begin_layout Lemma", i)
488 j = find_end_of_layout(document.body, i)
490 document.warning("Malformed LyX document: Can't find end of Lemma layout")
493 arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j)
494 endarg1 = find_end_of_inset(document.body, arg1)
495 arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j)
496 endarg2 = find_end_of_inset(document.body, arg2)
500 beginPlain1 = find_token(document.body, "\\begin_layout Plain Layout", arg1, endarg1)
501 if beginPlain1 == -1:
502 document.warning("Malformed LyX document: Can't find arg1 plain Layout")
505 endPlain1 = find_end_of_inset(document.body, beginPlain1)
506 content1 = document.body[beginPlain1 + 1 : endPlain1 - 2]
507 subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">")
509 beginPlain2 = find_token(document.body, "\\begin_layout Plain Layout", arg2, endarg2)
510 if beginPlain2 == -1:
511 document.warning("Malformed LyX document: Can't find arg2 plain Layout")
514 endPlain2 = find_end_of_inset(document.body, beginPlain2)
515 content2 = document.body[beginPlain2 + 1 : endPlain2 - 2]
516 subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]")
520 del document.body[arg2 : endarg2 + 1]
522 del document.body[arg1 : endarg1 + 1]
524 del document.body[arg1 : endarg1 + 1]
526 del document.body[arg2 : endarg2 + 1]
528 # index of end layout has probably changed
529 j = find_end_of_layout(document.body, i)
531 document.warning("Malformed LyX document: Can't find end of Lemma layout")
537 # if this is not a consecutive env, add start command
539 begcmd = put_cmd_in_ert("\\begin{lemma}")
541 # has this a consecutive lemma?
542 consecutive = document.body[j + 2] == "\\begin_layout Lemma"
544 # if this is not followed by a consecutive env, add end command
546 document.body[j : j + 1] = put_cmd_in_ert("\\end{lemma}") + ["\\end_layout"]
548 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd + subst1 + subst2
554 def revert_question_env(document):
556 Reverts question and question* environments of
557 theorems-ams-extended-bytype module to ERT
560 # Do we use theorems-ams-extended-bytype module?
561 if not "theorems-ams-extended-bytype" in document.get_module_list():
567 i = find_token(document.body, "\\begin_layout Question", i)
571 starred = document.body[i] == "\\begin_layout Question*"
573 j = find_end_of_layout(document.body, i)
575 document.warning("Malformed LyX document: Can't find end of Question layout")
579 # if this is not a consecutive env, add start command
583 begcmd = put_cmd_in_ert("\\begin{question*}")
585 begcmd = put_cmd_in_ert("\\begin{question}")
587 # has this a consecutive theorem of same type?
590 consecutive = document.body[j + 2] == "\\begin_layout Question*"
592 consecutive = document.body[j + 2] == "\\begin_layout Question"
594 # if this is not followed by a consecutive env, add end command
597 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
599 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
601 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
603 add_to_preamble(document, "\\providecommand{\questionname}{Question}")
606 add_to_preamble(document, "\\theoremstyle{plain}\n" \
607 "\\newtheorem*{question*}{\\protect\\questionname}")
609 add_to_preamble(document, "\\theoremstyle{plain}\n" \
610 "\\newtheorem{question}{\\protect\\questionname}")
615 def convert_dashes(document):
616 "convert -- and --- to \\twohyphens and \\threehyphens"
618 if document.backend != "latex":
621 lines = document.body
623 while i+1 < len(lines):
628 # skip label width string (bug 10243):
629 if line.startswith("\\labelwidthstring"):
631 # Do not touch hyphens in some insets:
633 value, start, end = get_containing_inset(lines, i)
635 # False means no (or malformed) containing inset
636 value, start, end = "no inset", -1, -1
637 # We must not replace anything in insets that store LaTeX contents in .lyx files
638 # (math and command insets without overridden read() and write() methods.
639 # Filtering out IPA and ERT makes Text::readParToken() more simple,
640 # Flex Code is logical markup, typically rendered as typewriter
641 if (value.split()[0] in ["CommandInset", "ERT", "External", "Formula",
642 "FormulaMacro", "Graphics", "IPA", "listings"]
643 or value in ["Flex Code", "Flex URL"]):
647 layout, start, end, j = get_containing_layout(lines, i)
648 except TypeError: # no (or malformed) containing layout
649 document.warning("Malformed LyX document: "
650 "Can't find layout at line %d" % i)
652 if layout == "LyX-Code":
655 # We can have an arbitrary number of consecutive hyphens.
656 # Replace as LaTeX does: First try emdash, then endash
657 line = line.replace("---", "\\threehyphens\n")
658 line = line.replace("--", "\\twohyphens\n")
659 lines[i:i+1] = line.splitlines()
661 # remove ligature breaks between dashes
663 while i < len(lines):
665 if (line.endswith(r"-\SpecialChar \textcompwordmark{}") and
666 lines[i+1].startswith("-")):
667 lines[i] = line.replace(r"\SpecialChar \textcompwordmark{}",
673 def revert_dashes(document):
675 Remove preamble code from 2.3->2.2 conversion.
676 Prevent ligatures of existing --- and --.
677 Revert \\twohyphens and \\threehyphens to -- and ---.
679 del_complete_lines(document.preamble,
680 ['% Added by lyx2lyx',
681 r'\renewcommand{\textendash}{--}',
682 r'\renewcommand{\textemdash}{---}'])
684 # Insert ligature breaks to prevent ligation of hyphens to dashes:
685 lines = document.body
687 while i+1 < len(lines):
692 # skip label width string (bug 10243):
693 if line.startswith("\\labelwidthstring"):
695 # do not touch hyphens in some insets (cf. convert_dashes):
697 value, start, end = get_containing_inset(lines, i)
699 # False means no (or malformed) containing inset
700 value, start, end = "no inset", -1, -1
701 if (value.split()[0] in ["CommandInset", "ERT", "External", "Formula",
702 "FormulaMacro", "Graphics", "IPA", "listings"]
703 or value == "Flex URL"):
706 line = line.replace("--", "-\\SpecialChar \\textcompwordmark{}\n-")
707 document.body[i:i+1] = line.split('\n')
709 # Revert \twohyphens and \threehyphens:
711 while i < len(lines):
713 if not line.endswith("hyphens"):
715 elif line.endswith("\\twohyphens") or line.endswith("\\threehyphens"):
716 line = line.replace("\\twohyphens", "--")
717 line = line.replace("\\threehyphens", "---")
718 lines[i] = line + lines.pop(i+1)
723 # order is important for the last three!
724 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
726 def is_part_of_converted_phrase(line, j, phrase):
727 "is phrase part of an already converted phrase?"
729 converted = "\\SpecialCharNoPassThru \\" + p
730 pos = j + len(phrase) - len(converted)
732 if line[pos:pos+len(converted)] == converted:
737 def convert_phrases(document):
738 "convert special phrases from plain text to \\SpecialCharNoPassThru"
740 if document.backend != "latex":
743 for phrase in phrases:
745 while i < len(document.body):
746 words = document.body[i].split()
747 if len(words) > 1 and words[0] == "\\begin_inset" and \
748 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
749 # must not replace anything in insets that store LaTeX contents in .lyx files
750 # (math and command insets without overridden read() and write() methods)
751 j = find_end_of_inset(document.body, i)
753 document.warning("Malformed LyX document: Can't find end of inset at line " + str(i))
758 if document.body[i].find("\\") == 0:
761 j = document.body[i].find(phrase)
765 if not is_part_of_converted_phrase(document.body[i], j, phrase):
766 front = document.body[i][:j]
767 back = document.body[i][j+len(phrase):]
769 document.body.insert(i+1, back)
770 # We cannot use SpecialChar since we do not know whether we are outside passThru
771 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
775 def revert_phrases(document):
776 "convert special phrases to plain text"
779 while i < len(document.body):
780 words = document.body[i].split()
781 if len(words) > 1 and words[0] == "\\begin_inset" and \
782 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
783 # see convert_phrases
784 j = find_end_of_inset(document.body, i)
786 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
792 for phrase in phrases:
793 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
794 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
795 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
797 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
798 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
800 if replaced and i+1 < len(document.body) and \
801 (document.body[i+1].find("\\") != 0 or \
802 document.body[i+1].find("\\SpecialChar") == 0) and \
803 len(document.body[i]) + len(document.body[i+1]) <= 80:
804 document.body[i] = document.body[i] + document.body[i+1]
805 document.body[i+1:i+2] = []
810 def convert_specialchar_internal(document, forward):
811 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
812 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
813 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
814 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
815 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
816 "\\LaTeX":"LaTeX" # must be after LaTeX2e
820 while i < len(document.body):
821 words = document.body[i].split()
822 if len(words) > 1 and words[0] == "\\begin_inset" and \
823 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
824 # see convert_phrases
825 j = find_end_of_inset(document.body, i)
827 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
832 for key, value in specialchars.items():
834 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
835 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
837 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
838 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
842 def convert_specialchar(document):
843 "convert special characters to new syntax"
844 convert_specialchar_internal(document, True)
847 def revert_specialchar(document):
848 "convert special characters to old syntax"
849 convert_specialchar_internal(document, False)
852 def revert_georgian(document):
853 "Set the document language to English but assure Georgian output"
855 if document.language == "georgian":
856 document.language = "english"
857 i = find_token(document.header, "\\language georgian", 0)
859 document.header[i] = "\\language english"
860 j = find_token(document.header, "\\language_package default", 0)
862 document.header[j] = "\\language_package babel"
863 k = find_token(document.header, "\\options", 0)
865 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
867 l = find_token(document.header, "\\use_default_options", 0)
868 document.header.insert(l + 1, "\\options georgian")
871 def revert_sigplan_doi(document):
872 " Reverts sigplanconf DOI layout to ERT "
874 if document.textclass != "sigplanconf":
879 i = find_token(document.body, "\\begin_layout DOI", i)
882 j = find_end_of_layout(document.body, i)
884 document.warning("Malformed LyX document: Can't find end of DOI layout")
888 content = lyx2latex(document, document.body[i:j + 1])
889 add_to_preamble(document, ["\\doi{" + content + "}"])
890 del document.body[i:j + 1]
894 def revert_ex_itemargs(document):
895 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
897 if not "linguistics" in document.get_module_list():
901 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
903 i = find_token(document.body, "\\begin_inset Argument item:", i)
906 j = find_end_of_inset(document.body, i)
907 # Find containing paragraph layout
908 parent = get_containing_layout(document.body, i)
910 document.warning("Malformed LyX document: Can't find parent paragraph layout")
914 layoutname = parent[0]
915 if layoutname in example_layouts:
916 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
917 endPlain = find_end_of_layout(document.body, beginPlain)
918 content = document.body[beginPlain + 1 : endPlain]
919 del document.body[i:j+1]
920 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
921 document.body[parbeg : parbeg] = subst
925 def revert_forest(document):
926 " Reverts the forest environment (Linguistics module) to TeX-code "
928 if not "linguistics" in document.get_module_list():
933 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
936 j = find_end_of_inset(document.body, i)
938 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
942 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
943 endPlain = find_end_of_layout(document.body, beginPlain)
944 content = lyx2latex(document, document.body[beginPlain : endPlain])
946 add_to_preamble(document, ["\\usepackage{forest}"])
948 document.body[i:j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
952 def revert_glossgroup(document):
953 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
955 if not "linguistics" in document.get_module_list():
960 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
963 j = find_end_of_inset(document.body, i)
965 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
969 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
970 endPlain = find_end_of_layout(document.body, beginPlain)
971 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
973 document.body[i:j + 1] = ["{", "", content, "", "}"]
977 def revert_newgloss(document):
978 " Reverts the new Glosse insets (Linguistics module) to the old format "
980 if not "linguistics" in document.get_module_list():
983 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
984 for glosse in glosses:
987 i = find_token(document.body, glosse, i)
990 j = find_end_of_inset(document.body, i)
992 document.warning("Malformed LyX document: Can't find end of Glosse inset")
996 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
997 endarg = find_end_of_inset(document.body, arg)
1000 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
1001 if argbeginPlain == -1:
1002 document.warning("Malformed LyX document: Can't find arg plain Layout")
1005 argendPlain = find_end_of_inset(document.body, argbeginPlain)
1006 argcontent = lyx2verbatim(document, document.body[argbeginPlain : argendPlain - 2])
1008 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
1009 argcontent, "\\end_layout"]
1011 # remove Arg insets and paragraph, if it only contains this inset
1012 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
1013 del document.body[arg - 1 : endarg + 4]
1015 del document.body[arg : endarg + 1]
1017 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1018 endPlain = find_end_of_layout(document.body, beginPlain)
1019 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
1021 document.body[beginPlain + 1:endPlain] = [content]
1024 # Dissolve ERT insets
1025 for glosse in glosses:
1028 i = find_token(document.body, glosse, i)
1031 j = find_end_of_inset(document.body, i)
1033 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1037 ert = find_token(document.body, "\\begin_inset ERT", i, j)
1040 ertend = find_end_of_inset(document.body, ert)
1042 document.warning("Malformed LyX document: Can't find end of ERT inset")
1045 ertcontent = get_ert(document.body, ert, True)
1046 document.body[ert : ertend + 1] = [ertcontent]
1050 def convert_newgloss(document):
1051 " Converts Glosse insets (Linguistics module) to the new format "
1053 if not "linguistics" in document.get_module_list():
1056 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1057 for glosse in glosses:
1060 i = find_token(document.body, glosse, i)
1063 j = find_end_of_inset(document.body, i)
1065 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1072 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
1073 if beginPlain == -1:
1075 endPlain = find_end_of_layout(document.body, beginPlain)
1077 document.warning("Malformed LyX document: Can't find end of Glosse layout")
1081 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
1082 if glt != -1 and document.body[glt + 1].startswith("glt"):
1083 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1084 argcontent = document.body[glt + 1 : endPlain]
1085 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
1086 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
1087 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
1088 "\\end_layout", "", "\\end_inset"]
1090 content = document.body[beginPlain + 1 : endPlain]
1091 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
1092 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
1094 endPlain = find_end_of_layout(document.body, beginPlain)
1096 j = find_end_of_inset(document.body, i)
1101 def convert_BoxFeatures(document):
1102 " adds new box features "
1106 i = find_token(document.body, "height_special", i)
1109 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1113 def revert_BoxFeatures(document):
1114 " outputs new box features as TeX code "
1118 defaultThick = "0.4pt"
1119 defaultShadow = "4pt"
1121 i = find_token(document.body, "thickness", i)
1124 binset = find_token(document.body, "\\begin_inset Box", i - 11)
1125 if binset == -1 or binset != i - 11:
1127 continue # then "thickness" is is just a word in the text
1128 einset = find_end_of_inset(document.body, binset)
1130 document.warning("Malformed LyX document: Can't find end of box inset!")
1133 # read out the values
1134 beg = document.body[i].find('"');
1135 end = document.body[i].rfind('"');
1136 thickness = document.body[i][beg+1:end];
1137 beg = document.body[i+1].find('"');
1138 end = document.body[i+1].rfind('"');
1139 separation = document.body[i+1][beg+1:end];
1140 beg = document.body[i+2].find('"');
1141 end = document.body[i+2].rfind('"');
1142 shadowsize = document.body[i+2][beg+1:end];
1143 # delete the specification
1144 del document.body[i:i+3]
1146 # first output the closing brace
1147 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1148 document.body[einset -1 : einset - 1] = put_cmd_in_ert("}")
1149 # we have now the problem that if there is already \(f)colorbox in ERT around the inset
1150 # the ERT from this routine must be around it
1151 regexp = re.compile(r'^.*colorbox{.*$')
1152 pos = find_re(document.body, regexp, binset - 4)
1153 if pos != -1 and pos == binset - 4:
1157 # now output the lengths
1158 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1159 document.body[pos : pos] = put_cmd_in_ert("{")
1160 if thickness != defaultThick:
1161 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness]
1162 if separation != defaultSep and thickness == defaultThick:
1163 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation]
1164 if separation != defaultSep and thickness != defaultThick:
1165 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1166 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1167 document.body[pos + 5 : pos +6] = ["{\\backslash shadowsize " + shadowsize]
1168 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1169 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1170 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1171 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1172 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1173 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1176 def convert_origin(document):
1177 " Insert the origin tag "
1179 i = find_token(document.header, "\\textclass ", 0)
1181 document.warning("Malformed LyX document: No \\textclass!!")
1183 if document.dir == u'':
1187 if document.systemlyxdir and document.systemlyxdir != u'':
1189 if os.path.isabs(document.dir):
1190 absdir = os.path.normpath(document.dir)
1192 absdir = os.path.normpath(os.path.abspath(document.dir))
1193 if os.path.isabs(document.systemlyxdir):
1194 abssys = os.path.normpath(document.systemlyxdir)
1196 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1197 relpath = os.path.relpath(absdir, abssys)
1198 if relpath.find(u'..') == 0:
1203 origin = document.dir.replace(u'\\', u'/') + u'/'
1205 origin = os.path.join(u"/systemlyxdir", relpath).replace(u'\\', u'/') + u'/'
1206 document.header[i:i] = ["\\origin " + origin]
1209 def revert_origin(document):
1210 " Remove the origin tag "
1212 i = find_token(document.header, "\\origin ", 0)
1214 document.warning("Malformed LyX document: No \\origin!!")
1216 del document.header[i]
1219 color_names = ["brown", "darkgray", "gray", \
1220 "lightgray", "lime", "olive", "orange", \
1221 "pink", "purple", "teal", "violet"]
1223 def revert_textcolor(document):
1224 " revert new \\textcolor colors to TeX code "
1230 i = find_token(document.body, "\\color ", i)
1234 for color in list(color_names):
1235 if document.body[i] == "\\color " + color:
1236 # register that xcolor must be loaded in the preamble
1239 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1240 # find the next \\color and/or the next \\end_layout
1241 j = find_token(document.body, "\\color", i + 1)
1242 k = find_token(document.body, "\\end_layout", i + 1)
1243 if j == -1 and k != -1:
1246 # first output the closing brace
1248 document.body[k: k] = put_cmd_in_ert("}")
1250 document.body[j: j] = put_cmd_in_ert("}")
1251 # now output the \textcolor command
1252 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1256 def convert_colorbox(document):
1257 "Add color settings for boxes."
1260 i = find_token(document.body, "shadowsize", i)
1263 # check whether this is really a LyX Box setting
1264 start, end = is_in_inset(document.body, i, "\\begin_inset Box")
1266 find_token(document.body, "\\begin_layout", start, i) != -1):
1269 document.body[i+1:i+1] = ['framecolor "black"',
1270 'backgroundcolor "none"']
1274 def revert_colorbox(document):
1275 """Change box color settings to LaTeX code."""
1279 i = find_token(document.body, "\\begin_inset Box", i+1)
1282 # Get and delete colour settings:
1283 framecolor = get_quoted_value(document.body, "framecolor", i+14, i+15, delete=True)
1284 backcolor = get_quoted_value(document.body, "backgroundcolor", i+14, i+15, delete=True)
1285 if not framecolor or not backcolor:
1286 document.warning("Malformed LyX document: color options not found in Box inset!")
1288 if framecolor == "black" and backcolor == "none": # default values
1289 i += 15 # skip box option lines
1292 # Emulate non-default colours with LaTeX code:
1293 einset = find_end_of_inset(document.body, i)
1295 document.warning("Malformed LyX document: Can't find end of box inset!")
1297 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1298 # insert the closing brace first (keeps indices 'i' and 'einset' valid)
1299 document.body[einset+1:einset+1] = put_cmd_in_ert("}") + [""]
1300 # now insert the (f)color box command
1301 if ("Box Boxed" in document.body[i]): # framed box, use \fcolorbox
1302 # change the box type (frame added by \fcolorbox)
1303 document.body[i] = "\\begin_inset Box Frameless"
1304 # ensure an inner box:
1306 if not set_bool_value(document.body, "has_inner_box", True, i+3, i+4):
1307 set_bool_value(document.body, "use_makebox", True, i+6, i+7)
1309 document.warning("Malformed LyX document: 'has_inner_box' or "
1310 "'use_makebox' option not found in box inset!")
1311 ertinset = put_cmd_in_ert("\\fcolorbox{%s}{%s}{"% (framecolor, backcolor))
1313 ertinset = put_cmd_in_ert("\\colorbox{%s}{" % backcolor)
1314 document.body[i:i] = ertinset + [""]
1315 i = einset # skip inset
1318 def revert_mathmulticol(document):
1319 " Convert formulas to ERT if they contain multicolumns "
1323 i = find_token(document.body, '\\begin_inset Formula', i)
1326 j = find_end_of_inset(document.body, i)
1328 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1331 lines = document.body[i:j]
1332 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1333 code = "\n".join(lines)
1338 n = code.find("\\multicolumn", k)
1339 # no need to convert degenerated multicolumn cells,
1340 # they work in old LyX versions as "math ERT"
1341 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1342 ert = put_cmd_in_ert(code)
1343 document.body[i:j+1] = ert
1349 i = find_end_of_inset(document.body, i)
1354 def revert_jss(document):
1355 " Reverts JSS In_Preamble commands to ERT in preamble "
1357 if document.textclass != "jss":
1366 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1367 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1370 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1372 endh = find_end_of_inset(document.body, h)
1373 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1374 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1378 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1380 endm = find_end_of_inset(document.body, m)
1381 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1382 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1386 j = find_token(document.body, "\\begin_inset Flex Code", j)
1388 # assure that we are not in a Code Chunk inset
1389 if document.body[j][-1] == "e":
1390 endj = find_end_of_inset(document.body, j)
1391 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1392 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1398 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1400 endk = find_end_of_inset(document.body, k)
1401 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1402 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1406 n = find_token(document.body, "\\begin_inset Flex URL", n)
1408 endn = find_end_of_inset(document.body, n)
1409 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1410 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1412 # now revert the In_Preamble layouts
1414 i = find_token(document.body, "\\begin_layout Title", 0)
1417 j = find_end_of_layout(document.body, i)
1419 document.warning("Malformed LyX document: Can't find end of Title layout")
1422 content = lyx2latex(document, document.body[i:j + 1])
1423 add_to_preamble(document, ["\\title{" + content + "}"])
1424 del document.body[i:j + 1]
1426 i = find_token(document.body, "\\begin_layout Author", 0)
1429 j = find_end_of_layout(document.body, i)
1431 document.warning("Malformed LyX document: Can't find end of Author layout")
1434 content = lyx2latex(document, document.body[i:j + 1])
1435 add_to_preamble(document, ["\\author{" + content + "}"])
1436 del document.body[i:j + 1]
1438 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1441 j = find_end_of_layout(document.body, i)
1443 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1446 content = lyx2latex(document, document.body[i:j + 1])
1447 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1448 del document.body[i:j + 1]
1450 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1453 j = find_end_of_layout(document.body, i)
1455 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1458 content = lyx2latex(document, document.body[i:j + 1])
1459 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1460 del document.body[i:j + 1]
1462 i = find_token(document.body, "\\begin_layout Short Title", 0)
1465 j = find_end_of_layout(document.body, i)
1467 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1470 content = lyx2latex(document, document.body[i:j + 1])
1471 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1472 del document.body[i:j + 1]
1474 i = find_token(document.body, "\\begin_layout Abstract", 0)
1477 j = find_end_of_layout(document.body, i)
1479 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1482 content = lyx2latex(document, document.body[i:j + 1])
1483 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1484 del document.body[i:j + 1]
1486 i = find_token(document.body, "\\begin_layout Keywords", 0)
1489 j = find_end_of_layout(document.body, i)
1491 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1494 content = lyx2latex(document, document.body[i:j + 1])
1495 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1496 del document.body[i:j + 1]
1498 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1501 j = find_end_of_layout(document.body, i)
1503 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1506 content = lyx2latex(document, document.body[i:j + 1])
1507 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1508 del document.body[i:j + 1]
1510 i = find_token(document.body, "\\begin_layout Address", 0)
1513 j = find_end_of_layout(document.body, i)
1515 document.warning("Malformed LyX document: Can't find end of Address layout")
1518 content = lyx2latex(document, document.body[i:j + 1])
1519 add_to_preamble(document, ["\\Address{" + content + "}"])
1520 del document.body[i:j + 1]
1521 # finally handle the code layouts
1526 while m != -1 or j != -1 or h != -1 or k != -1:
1529 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1531 endh = find_end_of_inset(document.body, h)
1532 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1533 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1534 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1538 j = find_token(document.body, "\\begin_layout Code Input", j)
1540 endj = find_end_of_layout(document.body, j)
1541 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1542 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1543 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1544 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1545 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1549 k = find_token(document.body, "\\begin_layout Code Output", k)
1551 endk = find_end_of_layout(document.body, k)
1552 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1553 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1554 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1555 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1556 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1560 m = find_token(document.body, "\\begin_layout Code", m)
1562 endm = find_end_of_layout(document.body, m)
1563 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1564 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1565 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1566 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1567 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1571 def convert_subref(document):
1572 " converts sub: ref prefixes to subref: "
1575 rx = re.compile(r'^name \"sub:(.+)$')
1578 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1581 j = find_end_of_inset(document.body, i)
1583 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1587 for p in range(i, j):
1588 m = rx.match(document.body[p])
1591 document.body[p] = "name \"subsec:" + label
1595 rx = re.compile(r'^reference \"sub:(.+)$')
1598 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1601 j = find_end_of_inset(document.body, i)
1603 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1607 for p in range(i, j):
1608 m = rx.match(document.body[p])
1611 document.body[p] = "reference \"subsec:" + label
1617 def revert_subref(document):
1618 " reverts subref: ref prefixes to sub: "
1621 rx = re.compile(r'^name \"subsec:(.+)$')
1624 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1627 j = find_end_of_inset(document.body, i)
1629 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1633 for p in range(i, j):
1634 m = rx.match(document.body[p])
1637 document.body[p] = "name \"sub:" + label
1642 rx = re.compile(r'^reference \"subsec:(.+)$')
1645 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1648 j = find_end_of_inset(document.body, i)
1650 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1654 for p in range(i, j):
1655 m = rx.match(document.body[p])
1658 document.body[p] = "reference \"sub:" + label
1663 def convert_nounzip(document):
1664 " remove the noUnzip parameter of graphics insets "
1666 rx = re.compile(r'\s*noUnzip\s*$')
1669 i = find_token(document.body, "\\begin_inset Graphics", i)
1672 j = find_end_of_inset(document.body, i)
1674 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1678 k = find_re(document.body, rx, i, j)
1680 del document.body[k]
1685 def convert_revert_external_bbox(document, forward):
1686 " add units to bounding box of external insets "
1688 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1691 i = find_token(document.body, "\\begin_inset External", i)
1694 j = find_end_of_inset(document.body, i)
1696 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1699 k = find_re(document.body, rx, i, j)
1703 tokens = document.body[k].split()
1705 for t in range(1, 5):
1708 for t in range(1, 5):
1709 tokens[t] = length_in_bp(tokens[t])
1710 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1711 tokens[3] + " " + tokens[4]
1715 def convert_external_bbox(document):
1716 convert_revert_external_bbox(document, True)
1719 def revert_external_bbox(document):
1720 convert_revert_external_bbox(document, False)
1723 def revert_tcolorbox_1(document):
1724 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1727 i = find_token(document.header, "tcolorbox", i)
1733 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1736 flexEnd = find_end_of_inset(document.body, flex)
1737 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1738 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1739 flexEnd = find_end_of_inset(document.body, flex)
1741 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1743 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1744 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1748 def revert_tcolorbox_2(document):
1749 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1752 i = find_token(document.header, "tcolorbox", i)
1758 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1761 flexEnd = find_end_of_inset(document.body, flex)
1762 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1763 flexEnd = find_end_of_inset(document.body, flex)
1764 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1765 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1769 def revert_tcolorbox_3(document):
1770 " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1773 i = find_token(document.header, "tcolorbox", i)
1779 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1782 flexEnd = find_end_of_inset(document.body, flex)
1783 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1784 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1785 flexEnd = find_end_of_inset(document.body, flex)
1786 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1787 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1791 def revert_tcolorbox_4(document):
1792 " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1795 i = find_token(document.header, "tcolorbox", i)
1801 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1804 flexEnd = find_end_of_inset(document.body, flex)
1805 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1806 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1807 flexEnd = find_end_of_inset(document.body, flex)
1808 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1809 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1813 def revert_tcolorbox_5(document):
1814 " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1817 i = find_token(document.header, "tcolorbox", i)
1823 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1826 flexEnd = find_end_of_inset(document.body, flex)
1827 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1828 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1829 flexEnd = find_end_of_inset(document.body, flex)
1830 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1831 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1835 def revert_tcolorbox_6(document):
1836 " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1839 i = find_token(document.header, "tcolorbox", i)
1845 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1848 flexEnd = find_end_of_inset(document.body, flex)
1849 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1850 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1851 flexEnd = find_end_of_inset(document.body, flex)
1852 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1853 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1857 def revert_tcolorbox_7(document):
1858 " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1861 i = find_token(document.header, "tcolorbox", i)
1867 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1870 flexEnd = find_end_of_inset(document.body, flex)
1871 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1872 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1873 flexEnd = find_end_of_inset(document.body, flex)
1874 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1875 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1879 def revert_tcolorbox_8(document):
1880 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1886 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1888 j = find_end_of_layout(document.body, i)
1889 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1890 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1891 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1892 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1894 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1896 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1897 k = find_end_of_inset(document.body, j)
1898 k = find_token(document.body, "\\end_inset", k + 1)
1899 k = find_token(document.body, "\\end_inset", k + 1)
1901 k = find_token(document.body, "\\end_inset", k + 1)
1902 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1903 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1904 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1910 def revert_moderncv_1(document):
1911 " Reverts the new inset of moderncv to TeX-code in preamble "
1913 if document.textclass != "moderncv":
1919 # at first revert the new styles
1921 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1924 j = find_end_of_layout(document.body, i)
1926 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1929 content = lyx2latex(document, document.body[i:j + 1])
1930 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1931 del document.body[i:j + 1]
1933 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1936 j = find_end_of_layout(document.body, i)
1938 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1941 content = lyx2latex(document, document.body[i:j + 1])
1942 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1943 del document.body[i:j + 1]
1944 # now change the new styles to the obsolete ones
1946 i = find_token(document.body, "\\begin_layout Name", 0)
1949 j = find_end_of_layout(document.body, i)
1951 document.warning("Malformed LyX document: Can't find end of Name layout")
1954 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1955 if lineArg > j and j != 0:
1958 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1959 # we have to assure that no other inset is in the Argument
1960 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1961 endInset = find_token(document.body, "\\end_inset", beginPlain)
1964 while beginInset < endInset and beginInset != -1:
1965 beginInset = find_token(document.body, "\\begin_inset", k)
1966 endInset = find_token(document.body, "\\end_inset", l)
1969 Arg2 = document.body[l + 5 : l + 6]
1971 document.body[i : i + 1]= ["\\begin_layout FirstName"]
1972 # delete the Argument inset
1973 del( document.body[endInset - 2 : endInset + 3])
1974 del( document.body[lineArg : beginPlain + 1])
1975 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1978 def revert_moderncv_2(document):
1979 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1981 if document.textclass != "moderncv":
1988 i = find_token(document.body, "\\begin_layout Phone", i)
1991 j = find_end_of_layout(document.body, i)
1993 document.warning("Malformed LyX document: Can't find end of Phone layout")
1996 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1997 if lineArg > j and j != 0:
2001 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
2002 # we have to assure that no other inset is in the Argument
2003 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
2004 endInset = find_token(document.body, "\\end_inset", beginPlain)
2007 while beginInset < endInset and beginInset != -1:
2008 beginInset = find_token(document.body, "\\begin_inset", k)
2009 endInset = find_token(document.body, "\\end_inset", l)
2012 Arg = document.body[beginPlain + 1 : beginPlain + 2]
2014 if Arg[0] == "mobile":
2015 document.body[i : i + 1]= ["\\begin_layout Mobile"]
2017 document.body[i : i + 1]= ["\\begin_layout Fax"]
2018 # delete the Argument inset
2019 del(document.body[endInset - 2 : endInset + 1])
2020 del(document.body[lineArg : beginPlain + 3])
2024 def convert_moderncv_phone(document):
2025 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
2027 if document.textclass != "moderncv":
2034 "Mobile" : "mobile",
2038 rx = re.compile(r'^\\begin_layout (\S+)$')
2040 # substitute \fax and \mobile by \phone[fax] and \phone[mobile], respectively
2041 i = find_token(document.body, "\\begin_layout", i)
2045 m = rx.match(document.body[i])
2049 if val not in list(phone_dict.keys()):
2052 j = find_end_of_layout(document.body, i)
2054 document.warning("Malformed LyX document: Can't find end of Mobile layout")
2058 document.body[i : i + 1] = ["\\begin_layout Phone", "\\begin_inset Argument 1", "status open", "",
2059 "\\begin_layout Plain Layout", phone_dict[val], "\\end_layout", "",
2063 def convert_moderncv_name(document):
2064 " Convert the FirstName and LastName layout of moderncv to the general Name layout "
2066 if document.textclass != "moderncv":
2069 fnb = 0 # Begin of FirstName inset
2070 fne = 0 # End of FirstName inset
2071 lnb = 0 # Begin of LastName (FamilyName) inset
2072 lne = 0 # End of LastName (FamilyName) inset
2073 nb = 0 # Begin of substituting Name inset
2074 ne = 0 # End of substituting Name inset
2075 FirstName = [] # FirstName content
2076 FamilyName = [] # LastName content
2080 fnb = find_token(document.body, "\\begin_layout FirstName", fnb)
2082 fne = find_end_of_layout(document.body, fnb)
2084 document.warning("Malformed LyX document: Can't find end of FirstName layout")
2086 FirstName = document.body[fnb + 1 : fne]
2088 lnb = find_token(document.body, "\\begin_layout FamilyName", lnb)
2090 lne = find_end_of_layout(document.body, lnb)
2092 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
2094 FamilyName = document.body[lnb + 1 : lne]
2095 # Determine the region for the substituting Name layout
2096 if fnb == -1 and lnb == -1: # Neither FirstName nor FamilyName exists -> Do nothing
2098 elif fnb == -1: # Only FamilyName exists -> New Name insets replaces that
2101 elif lnb == -1: # Only FirstName exists -> New Name insets replaces that
2104 elif fne > lne: # FirstName position before FamilyName -> New Name insets spans
2105 nb = lnb # from FamilyName begin
2106 ne = fne # to FirstName end
2107 else: # FirstName position before FamilyName -> New Name insets spans
2108 nb = fnb # from FirstName begin
2109 ne = lne # to FamilyName end
2111 # Insert the substituting layout now. If FirstName exists, use an otpional argument.
2113 document.body[nb : ne + 1] = ["\\begin_layout Name"] + FamilyName + ["\\end_layout", ""]
2115 document.body[nb : ne + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2116 "\\begin_layout Plain Layout"] + FirstName + ["\\end_layout", "",
2117 "\\end_inset", ""] + FamilyName + ["\\end_layout", ""]
2120 def revert_achemso(document):
2121 " Reverts the flex inset Latin to TeX code "
2123 if document.textclass != "achemso":
2128 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2130 j = find_end_of_inset(document.body, i)
2134 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2135 endPlain = find_end_of_layout(document.body, beginPlain)
2136 content = lyx2latex(document, document.body[beginPlain : endPlain])
2137 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2139 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2144 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2145 "\\font_sf_scale", "\\font_tt_scale"]
2146 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2147 fontquotes = [True, True, True, True, False, False]
2149 def convert_fontsettings(document):
2150 " Duplicate font settings "
2152 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2154 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2155 use_non_tex_fonts = "false"
2157 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2159 for f in fontsettings:
2160 i = find_token(document.header, f + " ", 0)
2162 document.warning("Malformed LyX document: No " + f + "!")
2164 # note that with i = -1, this will insert at the end
2166 value = fontdefaults[j]
2168 value = document.header[i][len(f):].strip()
2170 if use_non_tex_fonts == "true":
2171 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2173 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2175 if use_non_tex_fonts == "true":
2176 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2178 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2182 def revert_fontsettings(document):
2183 " Merge font settings "
2185 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2187 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2188 use_non_tex_fonts = "false"
2190 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2192 for f in fontsettings:
2193 i = find_token(document.header, f + " ", 0)
2195 document.warning("Malformed LyX document: No " + f + "!")
2198 line = get_value(document.header, f, i)
2201 q2 = line.find('"', q1+1)
2202 q3 = line.find('"', q2+1)
2203 q4 = line.find('"', q3+1)
2204 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2205 document.warning("Malformed LyX document: Missing quotes!")
2208 if use_non_tex_fonts == "true":
2209 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2211 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2213 if use_non_tex_fonts == "true":
2214 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2216 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2220 def revert_solution(document):
2221 " Reverts the solution environment of the theorem module to TeX code "
2223 # Do we use one of the modules that provides Solution?
2225 mods = document.get_module_list()
2227 if mod == "theorems-std" or mod == "theorems-bytype" \
2228 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2238 i = find_token(document.body, "\\begin_layout Solution", i)
2242 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2243 if is_starred == True:
2245 LyXName = "Solution*"
2246 theoremName = "newtheorem*"
2249 LyXName = "Solution"
2250 theoremName = "newtheorem"
2252 j = find_end_of_layout(document.body, i)
2254 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2258 # if this is not a consecutive env, add start command
2261 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2263 # has this a consecutive theorem of same type?
2264 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2266 # if this is not followed by a consecutive env, add end command
2268 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2270 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2272 add_to_preamble(document, "\\theoremstyle{definition}")
2273 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2274 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2275 (theoremName, LaTeXName))
2276 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2277 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2278 (theoremName, LaTeXName))
2280 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2284 def revert_verbatim_star(document):
2285 from lyx_2_1 import revert_verbatim
2286 revert_verbatim(document, True)
2289 def convert_save_props(document):
2290 " Add save_transient_properties parameter. "
2291 i = find_token(document.header, '\\begin_header', 0)
2293 document.warning("Malformed lyx document: Missing '\\begin_header'.")
2295 document.header.insert(i + 1, '\\save_transient_properties true')
2298 def revert_save_props(document):
2299 " Remove save_transient_properties parameter. "
2300 i = find_token(document.header, "\\save_transient_properties", 0)
2303 del document.header[i]
2306 def convert_info_tabular_feature(document):
2308 return arg.replace("inset-modify tabular", "tabular-feature")
2309 convert_info_insets(document, "shortcut(s)?|icon", f)
2312 def revert_info_tabular_feature(document):
2314 return arg.replace("tabular-feature", "inset-modify tabular")
2315 convert_info_insets(document, "shortcut(s)?|icon", f)
2322 supported_versions = ["2.2.0", "2.2"]
2324 [475, [convert_separator]],
2325 # nothing to do for 476: We consider it a bug that older versions
2326 # did not load amsmath automatically for these commands, and do not
2327 # want to hardcode amsmath off.
2333 [481, [convert_dashes]],
2334 [482, [convert_phrases]],
2335 [483, [convert_specialchar]],
2340 [488, [convert_newgloss]],
2341 [489, [convert_BoxFeatures]],
2342 [490, [convert_origin]],
2344 [492, [convert_colorbox]],
2347 [495, [convert_subref]],
2348 [496, [convert_nounzip]],
2349 [497, [convert_external_bbox]],
2351 [499, [convert_moderncv_phone, convert_moderncv_name]],
2353 [501, [convert_fontsettings]],
2356 [504, [convert_save_props]],
2358 [506, [convert_info_tabular_feature]],
2359 [507, [convert_longtable_label]],
2360 [508, [convert_parbreak]]
2364 [507, [revert_parbreak]],
2365 [506, [revert_longtable_label]],
2366 [505, [revert_info_tabular_feature]],
2368 [503, [revert_save_props]],
2369 [502, [revert_verbatim_star]],
2370 [501, [revert_solution]],
2371 [500, [revert_fontsettings]],
2372 [499, [revert_achemso]],
2373 [498, [revert_moderncv_1, revert_moderncv_2]],
2374 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2375 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2376 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2377 [496, [revert_external_bbox]],
2378 [495, []], # nothing to do since the noUnzip parameter was optional
2379 [494, [revert_subref]],
2380 [493, [revert_jss]],
2381 [492, [revert_mathmulticol]],
2382 [491, [revert_colorbox]],
2383 [490, [revert_textcolor]],
2384 [489, [revert_origin]],
2385 [488, [revert_BoxFeatures]],
2386 [487, [revert_newgloss, revert_glossgroup]],
2387 [486, [revert_forest]],
2388 [485, [revert_ex_itemargs]],
2389 [484, [revert_sigplan_doi]],
2390 [483, [revert_georgian]],
2391 [482, [revert_specialchar]],
2392 [481, [revert_phrases]],
2393 [480, [revert_dashes]],
2394 [479, [revert_question_env]],
2395 [478, [revert_beamer_lemma]],
2396 [477, [revert_xarrow]],
2397 [476, [revert_swissgerman]],
2398 [475, [revert_smash]],
2399 [474, [revert_separator]]
2403 if __name__ == "__main__":