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 lyx2lyx_tools import (add_to_preamble, put_cmd_in_ert, get_ert,
28 lyx2latex, lyx2verbatim, length_in_bp, convert_info_insets, insert_document_option)
30 from parser_tools import (check_token, del_complete_lines,
31 find_end_of_inset, find_end_of_layout, find_nonempty_line, find_re,
32 find_substring, find_token, find_token_backwards, get_containing_layout,
33 get_containing_inset, get_quoted_value, get_value, is_in_inset,
34 get_bool_value, set_bool_value)
37 ####################################################################
38 # Private helper functions
40 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt, nolastopt):
42 Reverts an InsetArgument to TeX-code
44 revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt, notLastOpt)
45 LineOfBegin is the line of the \begin_layout or \begin_inset statement
46 LineOfEnd is the line of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
47 StartArgument is the number of the first argument that needs to be converted
48 EndArgument is the number of the last argument that needs to be converted or the last defined one
49 isEnvironment must be true, if the layout is for a LaTeX environment
50 isOpt must be true, if the argument is an optional one
51 notLastOpt must be true if the argument is mandatory and followed by optional ones
55 while lineArg != -1 and n < nmax + 1:
56 lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
57 if lineArg > endline and endline != 0:
60 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
61 # we have to assure that no other inset is in the Argument
62 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
63 endInset = find_token(document.body, "\\end_inset", beginPlain)
66 while beginInset < endInset and beginInset != -1:
67 beginInset = find_token(document.body, "\\begin_inset", k)
68 endInset = find_token(document.body, "\\end_inset", l)
71 if environment == False:
73 if nolastopt == False:
74 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
76 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
77 del(document.body[lineArg : beginPlain + 1])
80 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
81 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
85 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
86 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
89 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
90 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
96 ###############################################################################
98 ### Conversion and reversion routines
100 ###############################################################################
102 def convert_longtable_label_internal(document, forward):
104 Convert reference to "LongTableNoNumber" into "Unnumbered" if forward is True
107 old_reference = "\\begin_inset Caption LongTableNoNumber"
108 new_reference = "\\begin_inset Caption Unnumbered"
110 # if the purpose is to revert swap the strings roles
112 old_reference, new_reference = new_reference, old_reference
116 i = find_token(document.body, old_reference, i)
121 document.body[i] = new_reference
124 def convert_longtable_label(document):
125 convert_longtable_label_internal(document, True)
128 def revert_longtable_label(document):
129 convert_longtable_label_internal(document, False)
132 def convert_separator(document):
134 Convert layout separators to separator insets and add (LaTeX) paragraph
135 breaks in order to mimic previous LaTeX export.
138 parins = ["\\begin_inset Separator parbreak", "\\end_inset", ""]
139 parlay = ["\\begin_layout Standard", "\\begin_inset Separator parbreak",
140 "\\end_inset", "", "\\end_layout", ""]
142 "family" : "default",
143 "series" : "default",
152 i = find_token(document.body, "\\begin_deeper", i)
156 j = find_token_backwards(document.body, "\\end_layout", i-1)
158 # reset any text style before inserting the inset
159 lay = get_containing_layout(document.body, j-1)
161 content = "\n".join(document.body[lay[1]:lay[2]])
162 for val in list(sty_dict.keys()):
163 if content.find("\\%s" % val) != -1:
164 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
167 document.body[j:j] = parins
168 i = i + len(parins) + 1
174 i = find_token(document.body, "\\align", i)
178 lay = get_containing_layout(document.body, i)
179 if lay != False and lay[0] == "Plain Layout":
183 j = find_token_backwards(document.body, "\\end_layout", i-1)
185 # Very old LyX files do not have Plain Layout in insets (but Standard).
186 # So we additionally check here if there is no inset boundary
187 # between the previous layout and this one.
188 n = find_token(document.body, "\\end_inset", j, lay[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":
623 i = find_substring(document.body, "--", i+1)
626 line = document.body[i]
627 # skip label width string (bug 10243):
628 if line.startswith("\\labelwidthstring"):
630 # Do not touch hyphens in some insets:
632 value, start, end = get_containing_inset(document.body, i)
634 # False means no (or malformed) containing inset
635 value, start, end = "no inset", -1, -1
636 # We must not replace anything in insets that store LaTeX contents in .lyx files
637 # (math and command insets without overridden read() and write() methods.
638 # Filtering out IPA and ERT makes Text::readParToken() more simple,
639 # Flex Code is logical markup, typically rendered as typewriter
640 if (value.split()[0] in ["CommandInset", "ERT", "External", "Formula",
641 "FormulaMacro", "Graphics", "IPA", "listings"]
642 or value in ["Flex Code", "Flex URL"]):
646 layout, start, end, j = get_containing_layout(document.body, i)
647 except TypeError: # no (or malformed) containing layout
648 document.warning("Malformed LyX document: "
649 "Can't find layout at line %d" % i)
651 if layout == "LyX-Code":
654 # We can have an arbitrary number of consecutive hyphens.
655 # Replace as LaTeX does: First try emdash, then endash
656 line = line.replace("---", "\\threehyphens\n")
657 line = line.replace("--", "\\twohyphens\n")
658 document.body[i:i+1] = line.split('\n')
660 # remove ligature breaks between dashes
663 i = find_substring(document.body,
664 r"-\SpecialChar \textcompwordmark{}", i+1)
667 if document.body[i+1].startswith("-"):
668 document.body[i] = document.body[i].replace(
669 r"\SpecialChar \textcompwordmark{}", document.body.pop(i+1))
672 def revert_dashes(document):
674 Remove preamble code from 2.3->2.2 conversion.
675 Prevent ligatures of existing --- and --.
676 Revert \\twohyphens and \\threehyphens to -- and ---.
678 del_complete_lines(document.preamble,
679 ['% Added by lyx2lyx',
680 r'\renewcommand{\textendash}{--}',
681 r'\renewcommand{\textemdash}{---}'])
683 # Insert ligature breaks to prevent ligation of hyphens to dashes:
686 i = find_substring(document.body, "--", i+1)
689 line = document.body[i]
690 # skip label width string (bug 10243):
691 if line.startswith("\\labelwidthstring"):
693 # do not touch hyphens in some insets (cf. convert_dashes):
695 value, start, end = get_containing_inset(document.body, i)
697 # False means no (or malformed) containing inset
698 value, start, end = "no inset", -1, -1
699 if (value.split()[0] in ["CommandInset", "ERT", "External", "Formula",
700 "FormulaMacro", "Graphics", "IPA", "listings"]
701 or value == "Flex URL"):
704 line = line.replace("--", "-\\SpecialChar \\textcompwordmark{}\n-")
705 document.body[i:i+1] = line.split('\n')
707 # Revert \twohyphens and \threehyphens:
709 while i < len(document.body):
710 line = document.body[i]
711 if not line.endswith("hyphens"):
713 elif line.endswith("\\twohyphens") or line.endswith("\\threehyphens"):
714 line = line.replace("\\twohyphens", "--")
715 line = line.replace("\\threehyphens", "---")
716 document.body[i] = line + document.body.pop(i+1)
721 # order is important for the last three!
722 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
724 def is_part_of_converted_phrase(line, j, phrase):
725 "is phrase part of an already converted phrase?"
727 converted = "\\SpecialCharNoPassThru \\" + p
728 pos = j + len(phrase) - len(converted)
730 if line[pos:pos+len(converted)] == converted:
735 def convert_phrases(document):
736 "convert special phrases from plain text to \\SpecialCharNoPassThru"
738 if document.backend != "latex":
741 for phrase in phrases:
743 while i < len(document.body):
744 words = document.body[i].split()
745 if len(words) > 1 and words[0] == "\\begin_inset" and \
746 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
747 # must not replace anything in insets that store LaTeX contents in .lyx files
748 # (math and command insets without overridden read() and write() methods)
749 j = find_end_of_inset(document.body, i)
751 document.warning("Malformed LyX document: Can't find end of inset at line " + str(i))
756 if document.body[i].find("\\") == 0:
759 j = document.body[i].find(phrase)
763 if not is_part_of_converted_phrase(document.body[i], j, phrase):
764 front = document.body[i][:j]
765 back = document.body[i][j+len(phrase):]
767 document.body.insert(i+1, back)
768 # We cannot use SpecialChar since we do not know whether we are outside passThru
769 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
773 def revert_phrases(document):
774 "convert special phrases to plain text"
777 while i < len(document.body):
778 words = document.body[i].split()
779 if len(words) > 1 and words[0] == "\\begin_inset" and \
780 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
781 # see convert_phrases
782 j = find_end_of_inset(document.body, i)
784 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
790 for phrase in phrases:
791 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
792 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
793 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
795 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
796 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
798 if replaced and i+1 < len(document.body) and \
799 (document.body[i+1].find("\\") != 0 or \
800 document.body[i+1].find("\\SpecialChar") == 0) and \
801 len(document.body[i]) + len(document.body[i+1]) <= 80:
802 document.body[i] = document.body[i] + document.body[i+1]
803 document.body[i+1:i+2] = []
808 def convert_specialchar_internal(document, forward):
809 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
810 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
811 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
812 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
813 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
814 "\\LaTeX":"LaTeX" # must be after LaTeX2e
818 while i < len(document.body):
819 words = document.body[i].split()
820 if len(words) > 1 and words[0] == "\\begin_inset" and \
821 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
822 # see convert_phrases
823 j = find_end_of_inset(document.body, i)
825 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
830 for key, value in specialchars.items():
832 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
833 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
835 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
836 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
840 def convert_specialchar(document):
841 "convert special characters to new syntax"
842 convert_specialchar_internal(document, True)
845 def revert_specialchar(document):
846 "convert special characters to old syntax"
847 convert_specialchar_internal(document, False)
850 def revert_georgian(document):
851 "Set the document language to English but assure Georgian output"
853 if document.language == "georgian":
854 document.language = "english"
855 i = find_token(document.header, "\\language georgian", 0)
857 document.header[i] = "\\language english"
858 j = find_token(document.header, "\\language_package default", 0)
860 document.header[j] = "\\language_package babel"
861 insert_document_option(document, "georgian")
864 def revert_sigplan_doi(document):
865 " Reverts sigplanconf DOI layout to ERT "
867 if document.textclass != "sigplanconf":
872 i = find_token(document.body, "\\begin_layout DOI", i)
875 j = find_end_of_layout(document.body, i)
877 document.warning("Malformed LyX document: Can't find end of DOI layout")
881 content = lyx2latex(document, document.body[i:j + 1])
882 add_to_preamble(document, ["\\doi{" + content + "}"])
883 del document.body[i:j + 1]
887 def revert_ex_itemargs(document):
888 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
890 if not "linguistics" in document.get_module_list():
894 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
896 i = find_token(document.body, "\\begin_inset Argument item:", i)
899 j = find_end_of_inset(document.body, i)
900 # Find containing paragraph layout
901 parent = get_containing_layout(document.body, i)
903 document.warning("Malformed LyX document: Can't find parent paragraph layout")
907 layoutname = parent[0]
908 if layoutname in example_layouts:
909 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
910 endPlain = find_end_of_layout(document.body, beginPlain)
911 content = document.body[beginPlain + 1 : endPlain]
912 del document.body[i:j+1]
913 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
914 document.body[parbeg : parbeg] = subst
918 def revert_forest(document):
919 " Reverts the forest environment (Linguistics module) to TeX-code "
921 if not "linguistics" in document.get_module_list():
926 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
929 j = find_end_of_inset(document.body, i)
931 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
935 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
936 endPlain = find_end_of_layout(document.body, beginPlain)
937 content = lyx2latex(document, document.body[beginPlain : endPlain])
939 add_to_preamble(document, ["\\usepackage{forest}"])
941 document.body[i:j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
945 def revert_glossgroup(document):
946 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
948 if not "linguistics" in document.get_module_list():
953 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
956 j = find_end_of_inset(document.body, i)
958 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
962 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
963 endPlain = find_end_of_layout(document.body, beginPlain)
964 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
966 document.body[i:j + 1] = ["{", "", content, "", "}"]
970 def revert_newgloss(document):
971 " Reverts the new Glosse insets (Linguistics module) to the old format "
973 if not "linguistics" in document.get_module_list():
976 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
977 for glosse in glosses:
980 i = find_token(document.body, glosse, i)
983 j = find_end_of_inset(document.body, i)
985 document.warning("Malformed LyX document: Can't find end of Glosse inset")
989 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
990 endarg = find_end_of_inset(document.body, arg)
993 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
994 if argbeginPlain == -1:
995 document.warning("Malformed LyX document: Can't find arg plain Layout")
998 argendPlain = find_end_of_inset(document.body, argbeginPlain)
999 argcontent = lyx2verbatim(document, document.body[argbeginPlain : argendPlain - 2])
1001 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
1002 argcontent, "\\end_layout"]
1004 # remove Arg insets and paragraph, if it only contains this inset
1005 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
1006 del document.body[arg - 1 : endarg + 4]
1008 del document.body[arg : endarg + 1]
1010 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1011 endPlain = find_end_of_layout(document.body, beginPlain)
1012 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
1014 document.body[beginPlain + 1:endPlain] = [content]
1017 # Dissolve ERT insets
1018 for glosse in glosses:
1021 i = find_token(document.body, glosse, i)
1024 j = find_end_of_inset(document.body, i)
1026 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1030 ert = find_token(document.body, "\\begin_inset ERT", i, j)
1033 ertend = find_end_of_inset(document.body, ert)
1035 document.warning("Malformed LyX document: Can't find end of ERT inset")
1038 ertcontent = get_ert(document.body, ert, True)
1039 document.body[ert : ertend + 1] = [ertcontent]
1043 def convert_newgloss(document):
1044 " Converts Glosse insets (Linguistics module) to the new format "
1046 if not "linguistics" in document.get_module_list():
1049 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1050 for glosse in glosses:
1053 i = find_token(document.body, glosse, i)
1056 j = find_end_of_inset(document.body, i)
1058 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1065 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
1066 if beginPlain == -1:
1068 endPlain = find_end_of_layout(document.body, beginPlain)
1070 document.warning("Malformed LyX document: Can't find end of Glosse layout")
1074 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
1075 if glt != -1 and document.body[glt + 1].startswith("glt"):
1076 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1077 argcontent = document.body[glt + 1 : endPlain]
1078 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
1079 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
1080 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
1081 "\\end_layout", "", "\\end_inset"]
1083 content = document.body[beginPlain + 1 : endPlain]
1084 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
1085 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
1087 endPlain = find_end_of_layout(document.body, beginPlain)
1089 j = find_end_of_inset(document.body, i)
1094 def convert_BoxFeatures(document):
1095 " adds new box features "
1099 i = find_token(document.body, "height_special", i)
1102 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1106 def revert_BoxFeatures(document):
1107 " outputs new box features as TeX code "
1111 defaultThick = "0.4pt"
1112 defaultShadow = "4pt"
1114 i = find_token(document.body, "thickness", i)
1117 binset = find_token(document.body, "\\begin_inset Box", i - 11)
1118 if binset == -1 or binset != i - 11:
1120 continue # then "thickness" is is just a word in the text
1121 einset = find_end_of_inset(document.body, binset)
1123 document.warning("Malformed LyX document: Can't find end of box inset!")
1126 # read out the values
1127 beg = document.body[i].find('"');
1128 end = document.body[i].rfind('"');
1129 thickness = document.body[i][beg+1:end];
1130 beg = document.body[i+1].find('"');
1131 end = document.body[i+1].rfind('"');
1132 separation = document.body[i+1][beg+1:end];
1133 beg = document.body[i+2].find('"');
1134 end = document.body[i+2].rfind('"');
1135 shadowsize = document.body[i+2][beg+1:end];
1136 # delete the specification
1137 del document.body[i:i+3]
1139 # first output the closing brace
1140 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1141 document.body[einset -1 : einset - 1] = put_cmd_in_ert("}")
1142 # we have now the problem that if there is already \(f)colorbox in ERT around the inset
1143 # the ERT from this routine must be around it
1144 regexp = re.compile(r'^.*colorbox{.*$')
1145 pos = find_re(document.body, regexp, binset - 4)
1146 if pos != -1 and pos == binset - 4:
1150 # now output the lengths
1151 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1152 document.body[pos : pos] = put_cmd_in_ert("{")
1153 if thickness != defaultThick:
1154 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness]
1155 if separation != defaultSep and thickness == defaultThick:
1156 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation]
1157 if separation != defaultSep and thickness != defaultThick:
1158 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1159 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1160 document.body[pos + 5 : pos +6] = ["{\\backslash shadowsize " + shadowsize]
1161 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1162 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1163 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1164 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1165 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1166 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1169 def convert_origin(document):
1170 " Insert the origin tag "
1172 i = find_token(document.header, "\\textclass ", 0)
1174 document.warning("Malformed LyX document: No \\textclass!!")
1176 if document.dir == u'':
1180 if document.systemlyxdir and document.systemlyxdir != u'':
1182 if os.path.isabs(document.dir):
1183 absdir = os.path.normpath(document.dir)
1185 absdir = os.path.normpath(os.path.abspath(document.dir))
1186 if os.path.isabs(document.systemlyxdir):
1187 abssys = os.path.normpath(document.systemlyxdir)
1189 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1190 relpath = os.path.relpath(absdir, abssys)
1191 if relpath.find(u'..') == 0:
1196 origin = document.dir.replace(u'\\', u'/') + u'/'
1198 origin = os.path.join(u"/systemlyxdir", relpath).replace(u'\\', u'/') + u'/'
1199 document.header[i:i] = ["\\origin " + origin]
1202 def revert_origin(document):
1203 " Remove the origin tag "
1205 i = find_token(document.header, "\\origin ", 0)
1207 document.warning("Malformed LyX document: No \\origin!!")
1209 del document.header[i]
1212 color_names = ["brown", "darkgray", "gray", \
1213 "lightgray", "lime", "olive", "orange", \
1214 "pink", "purple", "teal", "violet"]
1216 def revert_textcolor(document):
1217 " revert new \\textcolor colors to TeX code "
1223 i = find_token(document.body, "\\color ", i)
1227 for color in list(color_names):
1228 if document.body[i] == "\\color " + color:
1229 # register that xcolor must be loaded in the preamble
1232 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1233 # find the next \\color and/or the next \\end_layout
1234 j = find_token(document.body, "\\color", i + 1)
1235 k = find_token(document.body, "\\end_layout", i + 1)
1236 if j == -1 and k != -1:
1239 # first output the closing brace
1241 document.body[k: k] = put_cmd_in_ert("}")
1243 document.body[j: j] = put_cmd_in_ert("}")
1244 # now output the \textcolor command
1245 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1249 def convert_colorbox(document):
1250 "Add color settings for boxes."
1253 i = find_token(document.body, "shadowsize", i)
1256 # check whether this is really a LyX Box setting
1257 start, end = is_in_inset(document.body, i, "\\begin_inset Box")
1259 find_token(document.body, "\\begin_layout", start, i) != -1):
1262 document.body[i+1:i+1] = ['framecolor "black"',
1263 'backgroundcolor "none"']
1267 def revert_colorbox(document):
1268 """Change box color settings to LaTeX code."""
1272 i = find_token(document.body, "\\begin_inset Box", i+1)
1275 # Get and delete colour settings:
1276 framecolor = get_quoted_value(document.body, "framecolor", i+14, i+15, delete=True)
1277 backcolor = get_quoted_value(document.body, "backgroundcolor", i+14, i+15, delete=True)
1278 if not framecolor or not backcolor:
1279 document.warning("Malformed LyX document: color options not found in Box inset!")
1281 if framecolor == "black" and backcolor == "none": # default values
1282 i += 15 # skip box option lines
1285 # Emulate non-default colours with LaTeX code:
1286 einset = find_end_of_inset(document.body, i)
1288 document.warning("Malformed LyX document: Can't find end of box inset!")
1290 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1291 # insert the closing brace first (keeps indices 'i' and 'einset' valid)
1292 document.body[einset+1:einset+1] = put_cmd_in_ert("}") + [""]
1293 # now insert the (f)color box command
1294 if ("Box Boxed" in document.body[i]): # framed box, use \fcolorbox
1295 # change the box type (frame added by \fcolorbox)
1296 document.body[i] = "\\begin_inset Box Frameless"
1297 # ensure an inner box:
1299 if not set_bool_value(document.body, "has_inner_box", True, i+3, i+4):
1300 set_bool_value(document.body, "use_makebox", True, i+6, i+7)
1302 document.warning("Malformed LyX document: 'has_inner_box' or "
1303 "'use_makebox' option not found in box inset!")
1304 ertinset = put_cmd_in_ert("\\fcolorbox{%s}{%s}{"% (framecolor, backcolor))
1306 ertinset = put_cmd_in_ert("\\colorbox{%s}{" % backcolor)
1307 document.body[i:i] = ertinset + [""]
1308 i = einset # skip inset
1311 def revert_mathmulticol(document):
1312 " Convert formulas to ERT if they contain multicolumns "
1316 i = find_token(document.body, '\\begin_inset Formula', i)
1319 j = find_end_of_inset(document.body, i)
1321 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1324 lines = document.body[i:j]
1325 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1326 code = "\n".join(lines)
1331 n = code.find("\\multicolumn", k)
1332 # no need to convert degenerated multicolumn cells,
1333 # they work in old LyX versions as "math ERT"
1334 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1335 ert = put_cmd_in_ert(code)
1336 document.body[i:j+1] = ert
1342 i = find_end_of_inset(document.body, i)
1347 def revert_jss(document):
1348 " Reverts JSS In_Preamble commands to ERT in preamble "
1350 if document.textclass != "jss":
1359 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1360 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1363 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1365 endh = find_end_of_inset(document.body, h)
1366 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1367 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1371 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1373 endm = find_end_of_inset(document.body, m)
1374 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1375 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1379 j = find_token(document.body, "\\begin_inset Flex Code", j)
1381 # assure that we are not in a Code Chunk inset
1382 if document.body[j][-1] == "e":
1383 endj = find_end_of_inset(document.body, j)
1384 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1385 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1391 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1393 endk = find_end_of_inset(document.body, k)
1394 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1395 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1399 n = find_token(document.body, "\\begin_inset Flex URL", n)
1401 endn = find_end_of_inset(document.body, n)
1402 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1403 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1405 # now revert the In_Preamble layouts
1407 i = find_token(document.body, "\\begin_layout Title", 0)
1410 j = find_end_of_layout(document.body, i)
1412 document.warning("Malformed LyX document: Can't find end of Title layout")
1415 content = lyx2latex(document, document.body[i:j + 1])
1416 add_to_preamble(document, ["\\title{" + content + "}"])
1417 del document.body[i:j + 1]
1419 i = find_token(document.body, "\\begin_layout Author", 0)
1422 j = find_end_of_layout(document.body, i)
1424 document.warning("Malformed LyX document: Can't find end of Author layout")
1427 content = lyx2latex(document, document.body[i:j + 1])
1428 add_to_preamble(document, ["\\author{" + content + "}"])
1429 del document.body[i:j + 1]
1431 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1434 j = find_end_of_layout(document.body, i)
1436 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1439 content = lyx2latex(document, document.body[i:j + 1])
1440 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1441 del document.body[i:j + 1]
1443 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1446 j = find_end_of_layout(document.body, i)
1448 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1451 content = lyx2latex(document, document.body[i:j + 1])
1452 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1453 del document.body[i:j + 1]
1455 i = find_token(document.body, "\\begin_layout Short Title", 0)
1458 j = find_end_of_layout(document.body, i)
1460 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1463 content = lyx2latex(document, document.body[i:j + 1])
1464 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1465 del document.body[i:j + 1]
1467 i = find_token(document.body, "\\begin_layout Abstract", 0)
1470 j = find_end_of_layout(document.body, i)
1472 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1475 content = lyx2latex(document, document.body[i:j + 1])
1476 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1477 del document.body[i:j + 1]
1479 i = find_token(document.body, "\\begin_layout Keywords", 0)
1482 j = find_end_of_layout(document.body, i)
1484 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1487 content = lyx2latex(document, document.body[i:j + 1])
1488 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1489 del document.body[i:j + 1]
1491 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1494 j = find_end_of_layout(document.body, i)
1496 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1499 content = lyx2latex(document, document.body[i:j + 1])
1500 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1501 del document.body[i:j + 1]
1503 i = find_token(document.body, "\\begin_layout Address", 0)
1506 j = find_end_of_layout(document.body, i)
1508 document.warning("Malformed LyX document: Can't find end of Address layout")
1511 content = lyx2latex(document, document.body[i:j + 1])
1512 add_to_preamble(document, ["\\Address{" + content + "}"])
1513 del document.body[i:j + 1]
1514 # finally handle the code layouts
1519 while m != -1 or j != -1 or h != -1 or k != -1:
1522 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1524 endh = find_end_of_inset(document.body, h)
1525 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1526 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1527 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1531 j = find_token(document.body, "\\begin_layout Code Input", j)
1533 endj = find_end_of_layout(document.body, j)
1534 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1535 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1536 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1537 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1538 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1542 k = find_token(document.body, "\\begin_layout Code Output", k)
1544 endk = find_end_of_layout(document.body, k)
1545 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1546 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1547 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1548 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1549 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1553 m = find_token(document.body, "\\begin_layout Code", m)
1555 endm = find_end_of_layout(document.body, m)
1556 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1557 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1558 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1559 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1560 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1564 def convert_subref(document):
1565 " converts sub: ref prefixes to subref: "
1568 rx = re.compile(r'^name \"sub:(.+)$')
1571 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1574 j = find_end_of_inset(document.body, i)
1576 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1580 for p in range(i, j):
1581 m = rx.match(document.body[p])
1584 document.body[p] = "name \"subsec:" + label
1588 rx = re.compile(r'^reference \"sub:(.+)$')
1591 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1594 j = find_end_of_inset(document.body, i)
1596 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1600 for p in range(i, j):
1601 m = rx.match(document.body[p])
1604 document.body[p] = "reference \"subsec:" + label
1610 def revert_subref(document):
1611 " reverts subref: ref prefixes to sub: "
1614 rx = re.compile(r'^name \"subsec:(.+)$')
1617 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1620 j = find_end_of_inset(document.body, i)
1622 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1626 for p in range(i, j):
1627 m = rx.match(document.body[p])
1630 document.body[p] = "name \"sub:" + label
1635 rx = re.compile(r'^reference \"subsec:(.+)$')
1638 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1641 j = find_end_of_inset(document.body, i)
1643 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1647 for p in range(i, j):
1648 m = rx.match(document.body[p])
1651 document.body[p] = "reference \"sub:" + label
1656 def convert_nounzip(document):
1657 " remove the noUnzip parameter of graphics insets "
1659 rx = re.compile(r'\s*noUnzip\s*$')
1662 i = find_token(document.body, "\\begin_inset Graphics", i)
1665 j = find_end_of_inset(document.body, i)
1667 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1671 k = find_re(document.body, rx, i, j)
1673 del document.body[k]
1678 def convert_revert_external_bbox(document, forward):
1679 " add units to bounding box of external insets "
1681 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1684 i = find_token(document.body, "\\begin_inset External", i)
1687 j = find_end_of_inset(document.body, i)
1689 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1692 k = find_re(document.body, rx, i, j)
1696 tokens = document.body[k].split()
1698 for t in range(1, 5):
1701 for t in range(1, 5):
1702 tokens[t] = length_in_bp(tokens[t])
1703 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1704 tokens[3] + " " + tokens[4]
1708 def convert_external_bbox(document):
1709 convert_revert_external_bbox(document, True)
1712 def revert_external_bbox(document):
1713 convert_revert_external_bbox(document, False)
1716 def revert_tcolorbox_1(document):
1717 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1720 i = find_token(document.header, "tcolorbox", i)
1726 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1729 flexEnd = find_end_of_inset(document.body, flex)
1730 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1731 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1732 flexEnd = find_end_of_inset(document.body, flex)
1734 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1736 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1737 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1741 def revert_tcolorbox_2(document):
1742 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1745 i = find_token(document.header, "tcolorbox", i)
1751 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1754 flexEnd = find_end_of_inset(document.body, flex)
1755 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1756 flexEnd = find_end_of_inset(document.body, flex)
1757 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1758 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1762 def revert_tcolorbox_3(document):
1763 " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1766 i = find_token(document.header, "tcolorbox", i)
1772 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1775 flexEnd = find_end_of_inset(document.body, flex)
1776 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1777 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1778 flexEnd = find_end_of_inset(document.body, flex)
1779 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1780 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1784 def revert_tcolorbox_4(document):
1785 " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1788 i = find_token(document.header, "tcolorbox", i)
1794 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1797 flexEnd = find_end_of_inset(document.body, flex)
1798 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1799 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1800 flexEnd = find_end_of_inset(document.body, flex)
1801 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1802 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1806 def revert_tcolorbox_5(document):
1807 " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1810 i = find_token(document.header, "tcolorbox", i)
1816 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1819 flexEnd = find_end_of_inset(document.body, flex)
1820 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1821 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1822 flexEnd = find_end_of_inset(document.body, flex)
1823 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1824 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1828 def revert_tcolorbox_6(document):
1829 " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1832 i = find_token(document.header, "tcolorbox", i)
1838 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1841 flexEnd = find_end_of_inset(document.body, flex)
1842 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1843 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1844 flexEnd = find_end_of_inset(document.body, flex)
1845 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1846 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1850 def revert_tcolorbox_7(document):
1851 " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1854 i = find_token(document.header, "tcolorbox", i)
1860 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1863 flexEnd = find_end_of_inset(document.body, flex)
1864 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1865 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1866 flexEnd = find_end_of_inset(document.body, flex)
1867 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1868 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1872 def revert_tcolorbox_8(document):
1873 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1879 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1881 j = find_end_of_layout(document.body, i)
1882 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1883 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1884 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1885 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1887 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1889 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1890 k = find_end_of_inset(document.body, j)
1891 k = find_token(document.body, "\\end_inset", k + 1)
1892 k = find_token(document.body, "\\end_inset", k + 1)
1894 k = find_token(document.body, "\\end_inset", k + 1)
1895 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1896 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1897 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1903 def revert_moderncv_1(document):
1904 " Reverts the new inset of moderncv to TeX-code in preamble "
1906 if document.textclass != "moderncv":
1912 # at first revert the new styles
1914 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1917 j = find_end_of_layout(document.body, i)
1919 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1922 content = lyx2latex(document, document.body[i:j + 1])
1923 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1924 del document.body[i:j + 1]
1926 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1929 j = find_end_of_layout(document.body, i)
1931 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1934 content = lyx2latex(document, document.body[i:j + 1])
1935 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1936 del document.body[i:j + 1]
1937 # now change the new styles to the obsolete ones
1939 i = find_token(document.body, "\\begin_layout Name", 0)
1942 j = find_end_of_layout(document.body, i)
1944 document.warning("Malformed LyX document: Can't find end of Name layout")
1947 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1948 if lineArg > j and j != 0:
1951 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1952 # we have to assure that no other inset is in the Argument
1953 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1954 endInset = find_token(document.body, "\\end_inset", beginPlain)
1957 while beginInset < endInset and beginInset != -1:
1958 beginInset = find_token(document.body, "\\begin_inset", k)
1959 endInset = find_token(document.body, "\\end_inset", l)
1962 Arg2 = document.body[l + 5 : l + 6]
1964 document.body[i : i + 1]= ["\\begin_layout FirstName"]
1965 # delete the Argument inset
1966 del( document.body[endInset - 2 : endInset + 3])
1967 del( document.body[lineArg : beginPlain + 1])
1968 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1971 def revert_moderncv_2(document):
1972 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1974 if document.textclass != "moderncv":
1981 i = find_token(document.body, "\\begin_layout Phone", i)
1984 j = find_end_of_layout(document.body, i)
1986 document.warning("Malformed LyX document: Can't find end of Phone layout")
1989 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1990 if lineArg > j and j != 0:
1994 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1995 # we have to assure that no other inset is in the Argument
1996 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1997 endInset = find_token(document.body, "\\end_inset", beginPlain)
2000 while beginInset < endInset and beginInset != -1:
2001 beginInset = find_token(document.body, "\\begin_inset", k)
2002 endInset = find_token(document.body, "\\end_inset", l)
2005 Arg = document.body[beginPlain + 1 : beginPlain + 2]
2007 if Arg[0] == "mobile":
2008 document.body[i : i + 1]= ["\\begin_layout Mobile"]
2010 document.body[i : i + 1]= ["\\begin_layout Fax"]
2011 # delete the Argument inset
2012 del(document.body[endInset - 2 : endInset + 1])
2013 del(document.body[lineArg : beginPlain + 3])
2017 def convert_moderncv_phone(document):
2018 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
2020 if document.textclass != "moderncv":
2027 "Mobile" : "mobile",
2031 rx = re.compile(r'^\\begin_layout (\S+)$')
2033 # substitute \fax and \mobile by \phone[fax] and \phone[mobile], respectively
2034 i = find_token(document.body, "\\begin_layout", i)
2038 m = rx.match(document.body[i])
2042 if val not in list(phone_dict.keys()):
2045 j = find_end_of_layout(document.body, i)
2047 document.warning("Malformed LyX document: Can't find end of Mobile layout")
2051 document.body[i : i + 1] = ["\\begin_layout Phone", "\\begin_inset Argument 1", "status open", "",
2052 "\\begin_layout Plain Layout", phone_dict[val], "\\end_layout", "",
2056 def convert_moderncv_name(document):
2057 " Convert the FirstName and LastName layout of moderncv to the general Name layout "
2059 if document.textclass != "moderncv":
2062 fnb = 0 # Begin of FirstName inset
2063 fne = 0 # End of FirstName inset
2064 lnb = 0 # Begin of LastName (FamilyName) inset
2065 lne = 0 # End of LastName (FamilyName) inset
2066 nb = 0 # Begin of substituting Name inset
2067 ne = 0 # End of substituting Name inset
2068 FirstName = [] # FirstName content
2069 FamilyName = [] # LastName content
2073 fnb = find_token(document.body, "\\begin_layout FirstName", fnb)
2075 fne = find_end_of_layout(document.body, fnb)
2077 document.warning("Malformed LyX document: Can't find end of FirstName layout")
2079 FirstName = document.body[fnb + 1 : fne]
2081 lnb = find_token(document.body, "\\begin_layout FamilyName", lnb)
2083 lne = find_end_of_layout(document.body, lnb)
2085 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
2087 FamilyName = document.body[lnb + 1 : lne]
2088 # Determine the region for the substituting Name layout
2089 if fnb == -1 and lnb == -1: # Neither FirstName nor FamilyName exists -> Do nothing
2091 elif fnb == -1: # Only FamilyName exists -> New Name insets replaces that
2094 elif lnb == -1: # Only FirstName exists -> New Name insets replaces that
2097 elif fne > lne: # FirstName position before FamilyName -> New Name insets spans
2098 nb = lnb # from FamilyName begin
2099 ne = fne # to FirstName end
2100 else: # FirstName position before FamilyName -> New Name insets spans
2101 nb = fnb # from FirstName begin
2102 ne = lne # to FamilyName end
2104 # Insert the substituting layout now. If FirstName exists, use an otpional argument.
2106 document.body[nb : ne + 1] = ["\\begin_layout Name"] + FamilyName + ["\\end_layout", ""]
2108 document.body[nb : ne + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2109 "\\begin_layout Plain Layout"] + FirstName + ["\\end_layout", "",
2110 "\\end_inset", ""] + FamilyName + ["\\end_layout", ""]
2113 def revert_achemso(document):
2114 " Reverts the flex inset Latin to TeX code "
2116 if document.textclass != "achemso":
2121 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2123 j = find_end_of_inset(document.body, i)
2127 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2128 endPlain = find_end_of_layout(document.body, beginPlain)
2129 content = lyx2latex(document, document.body[beginPlain : endPlain])
2130 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2132 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2137 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2138 "\\font_sf_scale", "\\font_tt_scale"]
2139 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2140 fontquotes = [True, True, True, True, False, False]
2142 def convert_fontsettings(document):
2143 " Duplicate font settings "
2145 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2147 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2148 use_non_tex_fonts = "false"
2150 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2152 for f in fontsettings:
2153 i = find_token(document.header, f + " ", 0)
2155 document.warning("Malformed LyX document: No " + f + "!")
2157 # note that with i = -1, this will insert at the end
2159 value = fontdefaults[j]
2161 value = document.header[i][len(f):].strip()
2163 if use_non_tex_fonts == "true":
2164 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2166 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2168 if use_non_tex_fonts == "true":
2169 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2171 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2175 def revert_fontsettings(document):
2176 " Merge font settings "
2178 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2180 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2181 use_non_tex_fonts = "false"
2183 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2185 for f in fontsettings:
2186 i = find_token(document.header, f + " ", 0)
2188 document.warning("Malformed LyX document: No " + f + "!")
2191 line = get_value(document.header, f, i)
2194 q2 = line.find('"', q1+1)
2195 q3 = line.find('"', q2+1)
2196 q4 = line.find('"', q3+1)
2197 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2198 document.warning("Malformed LyX document: Missing quotes!")
2201 if use_non_tex_fonts == "true":
2202 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2204 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2206 if use_non_tex_fonts == "true":
2207 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2209 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2213 def revert_solution(document):
2214 " Reverts the solution environment of the theorem module to TeX code "
2216 # Do we use one of the modules that provides Solution?
2218 mods = document.get_module_list()
2220 if mod == "theorems-std" or mod == "theorems-bytype" \
2221 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2231 i = find_token(document.body, "\\begin_layout Solution", i)
2235 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2236 if is_starred == True:
2238 LyXName = "Solution*"
2239 theoremName = "newtheorem*"
2242 LyXName = "Solution"
2243 theoremName = "newtheorem"
2245 j = find_end_of_layout(document.body, i)
2247 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2251 # if this is not a consecutive env, add start command
2254 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2256 # has this a consecutive theorem of same type?
2257 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2259 # if this is not followed by a consecutive env, add end command
2261 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2263 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2265 add_to_preamble(document, "\\theoremstyle{definition}")
2266 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2267 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2268 (theoremName, LaTeXName))
2269 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2270 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2271 (theoremName, LaTeXName))
2273 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2277 def revert_verbatim_star(document):
2278 from lyx_2_1 import revert_verbatim
2279 revert_verbatim(document, True)
2282 def convert_save_props(document):
2283 " Add save_transient_properties parameter. "
2284 i = find_token(document.header, '\\begin_header', 0)
2286 document.warning("Malformed lyx document: Missing '\\begin_header'.")
2288 document.header.insert(i + 1, '\\save_transient_properties true')
2291 def revert_save_props(document):
2292 " Remove save_transient_properties parameter. "
2293 i = find_token(document.header, "\\save_transient_properties", 0)
2296 del document.header[i]
2299 def convert_info_tabular_feature(document):
2301 return arg.replace("inset-modify tabular", "tabular-feature")
2302 convert_info_insets(document, "shortcut(s)?|icon", f)
2305 def revert_info_tabular_feature(document):
2307 return arg.replace("tabular-feature", "inset-modify tabular")
2308 convert_info_insets(document, "shortcut(s)?|icon", f)
2315 supported_versions = ["2.2.0", "2.2"]
2317 [475, [convert_separator]],
2318 # nothing to do for 476: We consider it a bug that older versions
2319 # did not load amsmath automatically for these commands, and do not
2320 # want to hardcode amsmath off.
2326 [481, [convert_dashes]],
2327 [482, [convert_phrases]],
2328 [483, [convert_specialchar]],
2333 [488, [convert_newgloss]],
2334 [489, [convert_BoxFeatures]],
2335 [490, [convert_origin]],
2337 [492, [convert_colorbox]],
2340 [495, [convert_subref]],
2341 [496, [convert_nounzip]],
2342 [497, [convert_external_bbox]],
2344 [499, [convert_moderncv_phone, convert_moderncv_name]],
2346 [501, [convert_fontsettings]],
2349 [504, [convert_save_props]],
2351 [506, [convert_info_tabular_feature]],
2352 [507, [convert_longtable_label]],
2353 [508, [convert_parbreak]]
2357 [507, [revert_parbreak]],
2358 [506, [revert_longtable_label]],
2359 [505, [revert_info_tabular_feature]],
2361 [503, [revert_save_props]],
2362 [502, [revert_verbatim_star]],
2363 [501, [revert_solution]],
2364 [500, [revert_fontsettings]],
2365 [499, [revert_achemso]],
2366 [498, [revert_moderncv_1, revert_moderncv_2]],
2367 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2368 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2369 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2370 [496, [revert_external_bbox]],
2371 [495, []], # nothing to do since the noUnzip parameter was optional
2372 [494, [revert_subref]],
2373 [493, [revert_jss]],
2374 [492, [revert_mathmulticol]],
2375 [491, [revert_colorbox]],
2376 [490, [revert_textcolor]],
2377 [489, [revert_origin]],
2378 [488, [revert_BoxFeatures]],
2379 [487, [revert_newgloss, revert_glossgroup]],
2380 [486, [revert_forest]],
2381 [485, [revert_ex_itemargs]],
2382 [484, [revert_sigplan_doi]],
2383 [483, [revert_georgian]],
2384 [482, [revert_specialchar]],
2385 [481, [revert_phrases]],
2386 [480, [revert_dashes]],
2387 [479, [revert_question_env]],
2388 [478, [revert_beamer_lemma]],
2389 [477, [revert_xarrow]],
2390 [476, [revert_swissgerman]],
2391 [475, [revert_smash]],
2392 [474, [revert_separator]]
2396 if __name__ == "__main__":