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 (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_value, check_token)
42 ####################################################################
43 # Private helper functions
45 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt, nolastopt):
47 Reverts an InsetArgument to TeX-code
49 revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt, notLastOpt)
50 LineOfBegin is the line of the \begin_layout or \begin_inset statement
51 LineOfEnd is the line of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
52 StartArgument is the number of the first argument that needs to be converted
53 EndArgument is the number of the last argument that needs to be converted or the last defined one
54 isEnvironment must be true, if the layout is for a LaTeX environment
55 isOpt must be true, if the argument is an optional one
56 notLastOpt must be true if the argument is mandatory and followed by optional ones
60 while lineArg != -1 and n < nmax + 1:
61 lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
62 if lineArg > endline and endline != 0:
65 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
66 # we have to assure that no other inset is in the Argument
67 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
68 endInset = find_token(document.body, "\\end_inset", beginPlain)
71 while beginInset < endInset and beginInset != -1:
72 beginInset = find_token(document.body, "\\begin_inset", k)
73 endInset = find_token(document.body, "\\end_inset", l)
76 if environment == False:
78 if nolastopt == False:
79 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
81 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
82 del(document.body[lineArg : beginPlain + 1])
85 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
86 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
90 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
91 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
94 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
95 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
101 ###############################################################################
103 ### Conversion and reversion routines
105 ###############################################################################
107 def convert_longtable_label_internal(document, forward):
109 Convert reference to "LongTableNoNumber" into "Unnumbered" if forward is True
112 old_reference = "\\begin_inset Caption LongTableNoNumber"
113 new_reference = "\\begin_inset Caption Unnumbered"
115 # if the purpose is to revert swap the strings roles
117 old_reference, new_reference = new_reference, old_reference
121 i = find_token(document.body, old_reference, i)
126 document.body[i] = new_reference
129 def convert_longtable_label(document):
130 convert_longtable_label_internal(document, True)
133 def revert_longtable_label(document):
134 convert_longtable_label_internal(document, False)
137 def convert_separator(document):
139 Convert layout separators to separator insets and add (LaTeX) paragraph
140 breaks in order to mimic previous LaTeX export.
143 parins = ["\\begin_inset Separator parbreak", "\\end_inset", ""]
144 parlay = ["\\begin_layout Standard", "\\begin_inset Separator parbreak",
145 "\\end_inset", "", "\\end_layout", ""]
147 "family" : "default",
148 "series" : "default",
157 i = find_token(document.body, "\\begin_deeper", i)
161 j = find_token_backwards(document.body, "\\end_layout", i-1)
163 # reset any text style before inserting the inset
164 lay = get_containing_layout(document.body, j-1)
166 content = "\n".join(document.body[lay[1]:lay[2]])
167 for val in list(sty_dict.keys()):
168 if content.find("\\%s" % val) != -1:
169 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
172 document.body[j:j] = parins
173 i = i + len(parins) + 1
179 i = find_token(document.body, "\\align", i)
183 lay = get_containing_layout(document.body, i)
184 if lay != False and lay[0] == "Plain Layout":
188 j = find_token_backwards(document.body, "\\end_layout", i-1)
190 lay = get_containing_layout(document.body, j-1)
191 if lay != False and lay[0] == "Standard" \
192 and find_token(document.body, "\\align", lay[1], lay[2]) == -1 \
193 and find_token(document.body, "\\begin_inset VSpace", lay[1], lay[2]) == -1:
194 # reset any text style before inserting the inset
195 content = "\n".join(document.body[lay[1]:lay[2]])
196 for val in list(sty_dict.keys()):
197 if content.find("\\%s" % val) != -1:
198 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
201 document.body[j:j] = parins
202 i = i + len(parins) + 1
208 regexp = re.compile(r'^\\begin_layout (?:(-*)|(\s*))(Separator|EndOfSlide)(?:(-*)|(\s*))$', re.IGNORECASE)
212 i = find_re(document.body, regexp, i)
216 j = find_end_of_layout(document.body, i)
218 document.warning("Malformed LyX document: Missing `\\end_layout'.")
221 lay = get_containing_layout(document.body, j-1)
223 lines = document.body[lay[3]:lay[2]]
227 document.body[i:j+1] = parlay
229 document.body[i+1:i+1] = lines
231 i = i + len(parlay) + len(lines) + 1
234 def revert_separator(document):
235 " Revert separator insets to layout separators "
237 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
238 if document.textclass in beamer_classes:
239 beglaysep = "\\begin_layout Separator"
241 beglaysep = "\\begin_layout --Separator--"
243 parsep = [beglaysep, "", "\\end_layout", ""]
244 comert = ["\\begin_inset ERT", "status collapsed", "",
245 "\\begin_layout Plain Layout", "%", "\\end_layout",
246 "", "\\end_inset", ""]
247 empert = ["\\begin_inset ERT", "status collapsed", "",
248 "\\begin_layout Plain Layout", " ", "\\end_layout",
249 "", "\\end_inset", ""]
253 i = find_token(document.body, "\\begin_inset Separator", i)
257 lay = get_containing_layout(document.body, i)
259 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
266 kind = get_value(document.body, "\\begin_inset Separator", i, i+1, "plain").split()[1]
267 before = document.body[beg+1:i]
268 something_before = len(before) > 0 and len("".join(before)) > 0
269 j = find_end_of_inset(document.body, i)
270 after = document.body[j+1:end]
271 something_after = len(after) > 0 and len("".join(after)) > 0
273 beg = beg + len(before) + 1
274 elif something_before:
275 document.body[i:i] = ["\\end_layout", ""]
283 document.body[beg:j+1] = empert
286 document.body[beg:j+1] = comert
290 if layoutname == "Standard":
291 if not something_before:
292 document.body[beg:j+1] = parsep
294 document.body[i:i] = ["", "\\begin_layout Standard"]
297 document.body[beg:j+1] = ["\\begin_layout Standard"]
300 document.body[beg:j+1] = ["\\begin_deeper"]
302 end = end + 1 - (j + 1 - beg)
303 if not something_before:
304 document.body[i:i] = parsep
306 end = end + len(parsep)
307 document.body[i:i] = ["\\begin_layout Standard"]
308 document.body[end+2:end+2] = ["", "\\end_deeper", ""]
311 next_par_is_aligned = False
312 k = find_nonempty_line(document.body, end+1)
313 if k != -1 and check_token(document.body[k], "\\begin_layout"):
314 lay = get_containing_layout(document.body, k)
315 next_par_is_aligned = lay != False and \
316 find_token(document.body, "\\align", lay[1], lay[2]) != -1
317 if k != -1 and not next_par_is_aligned \
318 and not check_token(document.body[k], "\\end_deeper") \
319 and not check_token(document.body[k], "\\begin_deeper"):
320 if layoutname == "Standard":
321 document.body[beg:j+1] = [beglaysep]
324 document.body[beg:j+1] = ["\\begin_deeper", beglaysep]
325 end = end + 2 - (j + 1 - beg)
326 document.body[end+1:end+1] = ["", "\\end_deeper", ""]
330 del document.body[i:end+1]
332 del document.body[i:end-1]
337 def convert_parbreak(document):
339 Convert parbreak separators not specifically used to separate
340 environments to latexpar separators.
342 parbreakinset = "\\begin_inset Separator parbreak"
345 i = find_token(document.body, parbreakinset, i)
348 lay = get_containing_layout(document.body, i)
350 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
353 if lay[0] == "Standard":
354 # Convert only if not alone in the paragraph
355 k1 = find_nonempty_line(document.body, lay[1] + 1, i + 1)
356 k2 = find_nonempty_line(document.body, i + 1, lay[2])
357 if (k1 < i) or (k2 > i + 1) or not check_token(document.body[i], parbreakinset):
358 document.body[i] = document.body[i].replace("parbreak", "latexpar")
360 document.body[i] = document.body[i].replace("parbreak", "latexpar")
364 def revert_parbreak(document):
366 Revert latexpar separators to parbreak separators.
370 i = find_token(document.body, "\\begin_inset Separator latexpar", i)
373 document.body[i] = document.body[i].replace("latexpar", "parbreak")
377 def revert_smash(document):
378 " Set amsmath to on if smash commands are used "
380 commands = ["smash[t]", "smash[b]", "notag"]
381 i = find_token(document.header, "\\use_package amsmath", 0)
383 document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
385 value = get_value(document.header, "\\use_package amsmath", i).split()[1]
387 # nothing to do if package is not auto but on or off
391 j = find_token(document.body, '\\begin_inset Formula', j)
394 k = find_end_of_inset(document.body, j)
396 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
399 code = "\n".join(document.body[j:k])
401 if code.find("\\%s" % c) != -1:
402 # set amsmath to on, since it is loaded by the newer format
403 document.header[i] = "\\use_package amsmath 2"
408 def revert_swissgerman(document):
409 " Set language german-ch-old to german "
411 if document.language == "german-ch-old":
412 document.language = "german"
413 i = find_token(document.header, "\\language", 0)
415 document.header[i] = "\\language german"
418 j = find_token(document.body, "\\lang german-ch-old", j)
421 document.body[j] = document.body[j].replace("\\lang german-ch-old", "\\lang german")
425 def revert_use_package(document, pkg, commands, oldauto, supported):
426 # oldauto defines how the version we are reverting to behaves:
427 # if it is true, the old version uses the package automatically.
428 # if it is false, the old version never uses the package.
429 # If "supported" is true, the target version also supports this
431 regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
432 p = find_re(document.header, regexp, 0)
433 value = "1" # default is auto
435 value = get_value(document.header, "\\use_package" , p).split()[1]
437 del document.header[p]
438 if value == "2" and not supported: # on
439 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
440 elif value == "1" and not oldauto: # auto
443 i = find_token(document.body, '\\begin_inset Formula', i)
446 j = find_end_of_inset(document.body, i)
448 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
451 code = "\n".join(document.body[i:j])
453 if code.find("\\%s" % c) != -1:
455 document.header[p] = "\\use_package " + pkg + " 2"
457 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
462 mathtools_commands = ["xhookrightarrow", "xhookleftarrow", "xRightarrow", \
463 "xrightharpoondown", "xrightharpoonup", "xrightleftharpoons", \
464 "xLeftarrow", "xleftharpoondown", "xleftharpoonup", \
465 "xleftrightarrow", "xLeftrightarrow", "xleftrightharpoons", \
468 def revert_xarrow(document):
469 "remove use_package mathtools"
470 revert_use_package(document, "mathtools", mathtools_commands, False, True)
473 def revert_beamer_lemma(document):
474 " Reverts beamer lemma layout to ERT "
476 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
477 if document.textclass not in beamer_classes:
483 i = find_token(document.body, "\\begin_layout Lemma", i)
486 j = find_end_of_layout(document.body, i)
488 document.warning("Malformed LyX document: Can't find end of Lemma layout")
491 arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j)
492 endarg1 = find_end_of_inset(document.body, arg1)
493 arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j)
494 endarg2 = find_end_of_inset(document.body, arg2)
498 beginPlain1 = find_token(document.body, "\\begin_layout Plain Layout", arg1, endarg1)
499 if beginPlain1 == -1:
500 document.warning("Malformed LyX document: Can't find arg1 plain Layout")
503 endPlain1 = find_end_of_inset(document.body, beginPlain1)
504 content1 = document.body[beginPlain1 + 1 : endPlain1 - 2]
505 subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">")
507 beginPlain2 = find_token(document.body, "\\begin_layout Plain Layout", arg2, endarg2)
508 if beginPlain2 == -1:
509 document.warning("Malformed LyX document: Can't find arg2 plain Layout")
512 endPlain2 = find_end_of_inset(document.body, beginPlain2)
513 content2 = document.body[beginPlain2 + 1 : endPlain2 - 2]
514 subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]")
518 del document.body[arg2 : endarg2 + 1]
520 del document.body[arg1 : endarg1 + 1]
522 del document.body[arg1 : endarg1 + 1]
524 del document.body[arg2 : endarg2 + 1]
526 # index of end layout has probably changed
527 j = find_end_of_layout(document.body, i)
529 document.warning("Malformed LyX document: Can't find end of Lemma layout")
535 # if this is not a consecutive env, add start command
537 begcmd = put_cmd_in_ert("\\begin{lemma}")
539 # has this a consecutive lemma?
540 consecutive = document.body[j + 2] == "\\begin_layout Lemma"
542 # if this is not followed by a consecutive env, add end command
544 document.body[j : j + 1] = put_cmd_in_ert("\\end{lemma}") + ["\\end_layout"]
546 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd + subst1 + subst2
552 def revert_question_env(document):
554 Reverts question and question* environments of
555 theorems-ams-extended-bytype module to ERT
558 # Do we use theorems-ams-extended-bytype module?
559 if not "theorems-ams-extended-bytype" in document.get_module_list():
565 i = find_token(document.body, "\\begin_layout Question", i)
569 starred = document.body[i] == "\\begin_layout Question*"
571 j = find_end_of_layout(document.body, i)
573 document.warning("Malformed LyX document: Can't find end of Question layout")
577 # if this is not a consecutive env, add start command
581 begcmd = put_cmd_in_ert("\\begin{question*}")
583 begcmd = put_cmd_in_ert("\\begin{question}")
585 # has this a consecutive theorem of same type?
588 consecutive = document.body[j + 2] == "\\begin_layout Question*"
590 consecutive = document.body[j + 2] == "\\begin_layout Question"
592 # if this is not followed by a consecutive env, add end command
595 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
597 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
599 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
601 add_to_preamble(document, "\\providecommand{\questionname}{Question}")
604 add_to_preamble(document, "\\theoremstyle{plain}\n" \
605 "\\newtheorem*{question*}{\\protect\\questionname}")
607 add_to_preamble(document, "\\theoremstyle{plain}\n" \
608 "\\newtheorem{question}{\\protect\\questionname}")
613 def convert_dashes(document):
614 "convert -- and --- to \\twohyphens and \\threehyphens"
616 if document.backend != "latex":
619 lines = document.body
621 while i+1 < len(lines):
625 if (len(words) > 1 and words[0] == "\\begin_inset"
626 and (words[1] in ["CommandInset", "ERT", "External", "Formula",
627 "FormulaMacro", "Graphics", "IPA", "listings"]
628 or line.endswith("Flex Code"))):
629 # must not replace anything in insets that store LaTeX contents in .lyx files
630 # (math and command insets without overridden read() and write() methods
631 # filtering out IPA makes Text::readParToken() more simple
632 # skip ERT as well since it is not needed there
633 # Flex Code is logical markup, typically rendered as typewriter
634 j = find_end_of_inset(lines, i)
636 document.warning("Malformed LyX document: Can't find end of " +
637 words[1] + " inset at line " + str(i))
641 if lines[i] == "\\begin_layout LyX-Code":
642 j = find_end_of_layout(lines, i)
644 document.warning("Malformed LyX document: "
645 "Can't find end of %s layout at line %d" % (words[1],i))
649 if line.startswith("\\labelwidthstring"):
650 # skip label width string (bug 10243)
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 lines[i:i+1] = line.splitlines()
660 # remove ligature breaks between dashes
662 while i < len(lines):
664 if (line.endswith(r"-\SpecialChar \textcompwordmark{}") and
665 lines[i+1].startswith("-")):
666 lines[i] = line.replace(r"\SpecialChar \textcompwordmark{}",
672 def revert_dashes(document):
674 Prevent ligatures of existing --- and --.
675 Revert \\twohyphens and \\threehyphens to -- and ---.
676 Remove preamble code from 2.3->2.2 conversion.
678 del_complete_lines(document.preamble,
679 ['% Added by lyx2lyx',
680 r'\renewcommand{\textendash}{--}',
681 r'\renewcommand{\textemdash}{---}'])
682 # Insert ligature breaks to prevent ligation of hyphens to dashes:
683 lines = document.body
685 while i+1 < len(lines):
688 # skip label width string (bug 10243):
689 if line.startswith("\\labelwidthstring"):
691 # do not touch hyphens in some insets (cf. convert_dashes):
692 if line.startswith("\\begin_inset"):
694 if line.split()[1] in ["CommandInset", "ERT", "External",
695 "Formula", "FormulaMacro", "Graphics",
697 j = find_end_of_inset(lines, i)
699 document.warning("Malformed LyX document: Can't find "
700 "end of %s inset at line %d." % (itype, i))
706 line = line.replace("--", "-\\SpecialChar \\textcompwordmark{}\n-")
707 document.body[i:i+1] = line.split('\n')
708 # Revert \twohyphens and \threehyphens:
710 while i < len(lines):
712 if not line.endswith("hyphens"):
714 elif line.endswith("\\twohyphens") or line.endswith("\\threehyphens"):
715 line = line.replace("\\twohyphens", "--")
716 line = line.replace("\\threehyphens", "---")
717 lines[i] = line + lines.pop(i+1)
722 # order is important for the last three!
723 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
725 def is_part_of_converted_phrase(line, j, phrase):
726 "is phrase part of an already converted phrase?"
728 converted = "\\SpecialCharNoPassThru \\" + p
729 pos = j + len(phrase) - len(converted)
731 if line[pos:pos+len(converted)] == converted:
736 def convert_phrases(document):
737 "convert special phrases from plain text to \\SpecialCharNoPassThru"
739 if document.backend != "latex":
742 for phrase in phrases:
744 while i < len(document.body):
745 words = document.body[i].split()
746 if len(words) > 1 and words[0] == "\\begin_inset" and \
747 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
748 # must not replace anything in insets that store LaTeX contents in .lyx files
749 # (math and command insets withut overridden read() and write() methods
750 j = find_end_of_inset(document.body, i)
752 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
757 if document.body[i].find("\\") == 0:
760 j = document.body[i].find(phrase)
764 if not is_part_of_converted_phrase(document.body[i], j, phrase):
765 front = document.body[i][:j]
766 back = document.body[i][j+len(phrase):]
768 document.body.insert(i+1, back)
769 # We cannot use SpecialChar since we do not know whether we are outside passThru
770 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
774 def revert_phrases(document):
775 "convert special phrases to plain text"
778 while i < len(document.body):
779 words = document.body[i].split()
780 if len(words) > 1 and words[0] == "\\begin_inset" and \
781 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
782 # see convert_phrases
783 j = find_end_of_inset(document.body, i)
785 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
791 for phrase in phrases:
792 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
793 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
794 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
796 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
797 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
799 if replaced and i+1 < len(document.body) and \
800 (document.body[i+1].find("\\") != 0 or \
801 document.body[i+1].find("\\SpecialChar") == 0) and \
802 len(document.body[i]) + len(document.body[i+1]) <= 80:
803 document.body[i] = document.body[i] + document.body[i+1]
804 document.body[i+1:i+2] = []
809 def convert_specialchar_internal(document, forward):
810 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
811 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
812 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
813 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
814 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
815 "\\LaTeX":"LaTeX" # must be after LaTeX2e
819 while i < len(document.body):
820 words = document.body[i].split()
821 if len(words) > 1 and words[0] == "\\begin_inset" and \
822 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
823 # see convert_phrases
824 j = find_end_of_inset(document.body, i)
826 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
831 for key, value in specialchars.items():
833 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
834 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
836 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
837 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
841 def convert_specialchar(document):
842 "convert special characters to new syntax"
843 convert_specialchar_internal(document, True)
846 def revert_specialchar(document):
847 "convert special characters to old syntax"
848 convert_specialchar_internal(document, False)
851 def revert_georgian(document):
852 "Set the document language to English but assure Georgian output"
854 if document.language == "georgian":
855 document.language = "english"
856 i = find_token(document.header, "\\language georgian", 0)
858 document.header[i] = "\\language english"
859 j = find_token(document.header, "\\language_package default", 0)
861 document.header[j] = "\\language_package babel"
862 k = find_token(document.header, "\\options", 0)
864 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
866 l = find_token(document.header, "\\use_default_options", 0)
867 document.header.insert(l + 1, "\\options georgian")
870 def revert_sigplan_doi(document):
871 " Reverts sigplanconf DOI layout to ERT "
873 if document.textclass != "sigplanconf":
878 i = find_token(document.body, "\\begin_layout DOI", i)
881 j = find_end_of_layout(document.body, i)
883 document.warning("Malformed LyX document: Can't find end of DOI layout")
887 content = lyx2latex(document, document.body[i:j + 1])
888 add_to_preamble(document, ["\\doi{" + content + "}"])
889 del document.body[i:j + 1]
893 def revert_ex_itemargs(document):
894 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
896 if not "linguistics" in document.get_module_list():
900 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
902 i = find_token(document.body, "\\begin_inset Argument item:", i)
905 j = find_end_of_inset(document.body, i)
906 # Find containing paragraph layout
907 parent = get_containing_layout(document.body, i)
909 document.warning("Malformed LyX document: Can't find parent paragraph layout")
913 layoutname = parent[0]
914 if layoutname in example_layouts:
915 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
916 endPlain = find_end_of_layout(document.body, beginPlain)
917 content = document.body[beginPlain + 1 : endPlain]
918 del document.body[i:j+1]
919 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
920 document.body[parbeg : parbeg] = subst
924 def revert_forest(document):
925 " Reverts the forest environment (Linguistics module) to TeX-code "
927 if not "linguistics" in document.get_module_list():
932 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
935 j = find_end_of_inset(document.body, i)
937 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
941 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
942 endPlain = find_end_of_layout(document.body, beginPlain)
943 content = lyx2latex(document, document.body[beginPlain : endPlain])
945 add_to_preamble(document, ["\\usepackage{forest}"])
947 document.body[i:j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
951 def revert_glossgroup(document):
952 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
954 if not "linguistics" in document.get_module_list():
959 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
962 j = find_end_of_inset(document.body, i)
964 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
968 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
969 endPlain = find_end_of_layout(document.body, beginPlain)
970 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
972 document.body[i:j + 1] = ["{", "", content, "", "}"]
976 def revert_newgloss(document):
977 " Reverts the new Glosse insets (Linguistics module) to the old format "
979 if not "linguistics" in document.get_module_list():
982 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
983 for glosse in glosses:
986 i = find_token(document.body, glosse, i)
989 j = find_end_of_inset(document.body, i)
991 document.warning("Malformed LyX document: Can't find end of Glosse inset")
995 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
996 endarg = find_end_of_inset(document.body, arg)
999 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
1000 if argbeginPlain == -1:
1001 document.warning("Malformed LyX document: Can't find arg plain Layout")
1004 argendPlain = find_end_of_inset(document.body, argbeginPlain)
1005 argcontent = lyx2verbatim(document, document.body[argbeginPlain : argendPlain - 2])
1007 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
1008 argcontent, "\\end_layout"]
1010 # remove Arg insets and paragraph, if it only contains this inset
1011 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
1012 del document.body[arg - 1 : endarg + 4]
1014 del document.body[arg : endarg + 1]
1016 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1017 endPlain = find_end_of_layout(document.body, beginPlain)
1018 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
1020 document.body[beginPlain + 1:endPlain] = [content]
1023 # Dissolve ERT insets
1024 for glosse in glosses:
1027 i = find_token(document.body, glosse, i)
1030 j = find_end_of_inset(document.body, i)
1032 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1036 ert = find_token(document.body, "\\begin_inset ERT", i, j)
1039 ertend = find_end_of_inset(document.body, ert)
1041 document.warning("Malformed LyX document: Can't find end of ERT inset")
1044 ertcontent = get_ert(document.body, ert, True)
1045 document.body[ert : ertend + 1] = [ertcontent]
1049 def convert_newgloss(document):
1050 " Converts Glosse insets (Linguistics module) to the new format "
1052 if not "linguistics" in document.get_module_list():
1055 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1056 for glosse in glosses:
1059 i = find_token(document.body, glosse, i)
1062 j = find_end_of_inset(document.body, i)
1064 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1071 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
1072 if beginPlain == -1:
1074 endPlain = find_end_of_layout(document.body, beginPlain)
1076 document.warning("Malformed LyX document: Can't find end of Glosse layout")
1080 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
1081 if glt != -1 and document.body[glt + 1].startswith("glt"):
1082 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1083 argcontent = document.body[glt + 1 : endPlain]
1084 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
1085 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
1086 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
1087 "\\end_layout", "", "\\end_inset"]
1089 content = document.body[beginPlain + 1 : endPlain]
1090 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
1091 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
1093 endPlain = find_end_of_layout(document.body, beginPlain)
1095 j = find_end_of_inset(document.body, i)
1100 def convert_BoxFeatures(document):
1101 " adds new box features "
1105 i = find_token(document.body, "height_special", i)
1108 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1112 def revert_BoxFeatures(document):
1113 " outputs new box features as TeX code "
1117 defaultThick = "0.4pt"
1118 defaultShadow = "4pt"
1120 i = find_token(document.body, "thickness", i)
1123 binset = find_token(document.body, "\\begin_inset Box", i - 11)
1124 if binset == -1 or binset != i - 11:
1126 continue # then "thickness" is is just a word in the text
1127 einset = find_end_of_inset(document.body, binset)
1129 document.warning("Malformed LyX document: Can't find end of box inset!")
1132 # read out the values
1133 beg = document.body[i].find('"');
1134 end = document.body[i].rfind('"');
1135 thickness = document.body[i][beg+1:end];
1136 beg = document.body[i+1].find('"');
1137 end = document.body[i+1].rfind('"');
1138 separation = document.body[i+1][beg+1:end];
1139 beg = document.body[i+2].find('"');
1140 end = document.body[i+2].rfind('"');
1141 shadowsize = document.body[i+2][beg+1:end];
1142 # delete the specification
1143 del document.body[i:i+3]
1145 # first output the closing brace
1146 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1147 document.body[einset -1 : einset - 1] = put_cmd_in_ert("}")
1148 # we have now the problem that if there is already \(f)colorbox in ERT around the inset
1149 # the ERT from this routine must be around it
1150 regexp = re.compile(r'^.*colorbox{.*$')
1151 pos = find_re(document.body, regexp, binset - 4)
1152 if pos != -1 and pos == binset - 4:
1156 # now output the lengths
1157 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1158 document.body[pos : pos] = put_cmd_in_ert("{")
1159 if thickness != defaultThick:
1160 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness]
1161 if separation != defaultSep and thickness == defaultThick:
1162 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation]
1163 if separation != defaultSep and thickness != defaultThick:
1164 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1165 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1166 document.body[pos + 5 : pos +6] = ["{\\backslash shadowsize " + shadowsize]
1167 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1168 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1169 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1170 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1171 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1172 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1175 def convert_origin(document):
1176 " Insert the origin tag "
1178 i = find_token(document.header, "\\textclass ", 0)
1180 document.warning("Malformed LyX document: No \\textclass!!")
1182 if document.dir == u'':
1186 if document.systemlyxdir and document.systemlyxdir != u'':
1188 if os.path.isabs(document.dir):
1189 absdir = os.path.normpath(document.dir)
1191 absdir = os.path.normpath(os.path.abspath(document.dir))
1192 if os.path.isabs(document.systemlyxdir):
1193 abssys = os.path.normpath(document.systemlyxdir)
1195 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1196 relpath = os.path.relpath(absdir, abssys)
1197 if relpath.find(u'..') == 0:
1202 origin = document.dir.replace(u'\\', u'/') + u'/'
1204 origin = os.path.join(u"/systemlyxdir", relpath).replace(u'\\', u'/') + u'/'
1205 document.header[i:i] = ["\\origin " + origin]
1208 def revert_origin(document):
1209 " Remove the origin tag "
1211 i = find_token(document.header, "\\origin ", 0)
1213 document.warning("Malformed LyX document: No \\origin!!")
1215 del document.header[i]
1218 color_names = ["brown", "darkgray", "gray", \
1219 "lightgray", "lime", "olive", "orange", \
1220 "pink", "purple", "teal", "violet"]
1222 def revert_textcolor(document):
1223 " revert new \\textcolor colors to TeX code "
1229 i = find_token(document.body, "\\color ", i)
1233 for color in list(color_names):
1234 if document.body[i] == "\\color " + color:
1235 # register that xcolor must be loaded in the preamble
1238 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1239 # find the next \\color and/or the next \\end_layout
1240 j = find_token(document.body, "\\color", i + 1)
1241 k = find_token(document.body, "\\end_layout", i + 1)
1242 if j == -1 and k != -1:
1245 # first output the closing brace
1247 document.body[k: k] = put_cmd_in_ert("}")
1249 document.body[j: j] = put_cmd_in_ert("}")
1250 # now output the \textcolor command
1251 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1255 def convert_colorbox(document):
1256 " adds color settings for boxes "
1260 i = find_token(document.body, "shadowsize", i)
1263 document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1267 def revert_colorbox(document):
1268 " outputs color settings for boxes as TeX code "
1271 defaultframecolor = "black"
1272 defaultbackcolor = "none"
1274 i = find_token(document.body, "framecolor", i)
1277 binset = find_token(document.body, "\\begin_inset Box", i - 14)
1280 einset = find_end_of_inset(document.body, binset)
1282 document.warning("Malformed LyX document: Can't find end of box inset!")
1284 # read out the values
1285 beg = document.body[i].find('"');
1286 end = document.body[i].rfind('"');
1287 framecolor = document.body[i][beg+1:end];
1288 beg = document.body[i + 1].find('"');
1289 end = document.body[i + 1].rfind('"');
1290 backcolor = document.body[i+1][beg+1:end];
1291 # delete the specification
1292 del document.body[i:i + 2]
1294 # first output the closing brace
1295 if framecolor != defaultframecolor or backcolor != defaultbackcolor:
1296 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1297 document.body[einset : einset] = put_cmd_in_ert("}")
1298 # determine the box type
1299 isBox = find_token(document.body, "\\begin_inset Box Boxed", binset)
1300 # now output the box commands
1301 if (framecolor != defaultframecolor and isBox == binset) or (backcolor != defaultbackcolor and isBox == binset):
1302 document.body[i - 14 : i - 14] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1303 # in the case we must also change the box type because the ERT code adds a frame
1304 document.body[i - 4] = "\\begin_inset Box Frameless"
1305 # if has_inner_box 0 we must set it and use_makebox to 1
1306 ibox = find_token(document.body, "has_inner_box", i - 4)
1307 if ibox == -1 or ibox != i - 1:
1308 document.warning("Malformed LyX document: Can't find has_inner_box statement!")
1310 # read out the value
1311 innerbox = document.body[ibox][-1:];
1313 document.body[ibox] = "has_inner_box 1"
1314 document.body[ibox + 3] = "use_makebox 1"
1315 if backcolor != defaultbackcolor and isBox != binset:
1316 document.body[i - 14 : i - 14] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
1319 def revert_mathmulticol(document):
1320 " Convert formulas to ERT if they contain multicolumns "
1324 i = find_token(document.body, '\\begin_inset Formula', i)
1327 j = find_end_of_inset(document.body, i)
1329 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1332 lines = document.body[i:j]
1333 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1334 code = "\n".join(lines)
1339 n = code.find("\\multicolumn", k)
1340 # no need to convert degenerated multicolumn cells,
1341 # they work in old LyX versions as "math ERT"
1342 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1343 ert = put_cmd_in_ert(code)
1344 document.body[i:j+1] = ert
1350 i = find_end_of_inset(document.body, i)
1355 def revert_jss(document):
1356 " Reverts JSS In_Preamble commands to ERT in preamble "
1358 if document.textclass != "jss":
1367 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1368 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1371 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1373 endh = find_end_of_inset(document.body, h)
1374 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1375 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1379 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1381 endm = find_end_of_inset(document.body, m)
1382 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1383 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1387 j = find_token(document.body, "\\begin_inset Flex Code", j)
1389 # assure that we are not in a Code Chunk inset
1390 if document.body[j][-1] == "e":
1391 endj = find_end_of_inset(document.body, j)
1392 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1393 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1399 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1401 endk = find_end_of_inset(document.body, k)
1402 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1403 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1407 n = find_token(document.body, "\\begin_inset Flex URL", n)
1409 endn = find_end_of_inset(document.body, n)
1410 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1411 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1413 # now revert the In_Preamble layouts
1415 i = find_token(document.body, "\\begin_layout Title", 0)
1418 j = find_end_of_layout(document.body, i)
1420 document.warning("Malformed LyX document: Can't find end of Title layout")
1423 content = lyx2latex(document, document.body[i:j + 1])
1424 add_to_preamble(document, ["\\title{" + content + "}"])
1425 del document.body[i:j + 1]
1427 i = find_token(document.body, "\\begin_layout Author", 0)
1430 j = find_end_of_layout(document.body, i)
1432 document.warning("Malformed LyX document: Can't find end of Author layout")
1435 content = lyx2latex(document, document.body[i:j + 1])
1436 add_to_preamble(document, ["\\author{" + content + "}"])
1437 del document.body[i:j + 1]
1439 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1442 j = find_end_of_layout(document.body, i)
1444 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1447 content = lyx2latex(document, document.body[i:j + 1])
1448 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1449 del document.body[i:j + 1]
1451 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1454 j = find_end_of_layout(document.body, i)
1456 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1459 content = lyx2latex(document, document.body[i:j + 1])
1460 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1461 del document.body[i:j + 1]
1463 i = find_token(document.body, "\\begin_layout Short Title", 0)
1466 j = find_end_of_layout(document.body, i)
1468 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1471 content = lyx2latex(document, document.body[i:j + 1])
1472 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1473 del document.body[i:j + 1]
1475 i = find_token(document.body, "\\begin_layout Abstract", 0)
1478 j = find_end_of_layout(document.body, i)
1480 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1483 content = lyx2latex(document, document.body[i:j + 1])
1484 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1485 del document.body[i:j + 1]
1487 i = find_token(document.body, "\\begin_layout Keywords", 0)
1490 j = find_end_of_layout(document.body, i)
1492 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1495 content = lyx2latex(document, document.body[i:j + 1])
1496 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1497 del document.body[i:j + 1]
1499 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1502 j = find_end_of_layout(document.body, i)
1504 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1507 content = lyx2latex(document, document.body[i:j + 1])
1508 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1509 del document.body[i:j + 1]
1511 i = find_token(document.body, "\\begin_layout Address", 0)
1514 j = find_end_of_layout(document.body, i)
1516 document.warning("Malformed LyX document: Can't find end of Address layout")
1519 content = lyx2latex(document, document.body[i:j + 1])
1520 add_to_preamble(document, ["\\Address{" + content + "}"])
1521 del document.body[i:j + 1]
1522 # finally handle the code layouts
1527 while m != -1 or j != -1 or h != -1 or k != -1:
1530 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1532 endh = find_end_of_inset(document.body, h)
1533 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1534 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1535 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1539 j = find_token(document.body, "\\begin_layout Code Input", j)
1541 endj = find_end_of_layout(document.body, j)
1542 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1543 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1544 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1545 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1546 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1550 k = find_token(document.body, "\\begin_layout Code Output", k)
1552 endk = find_end_of_layout(document.body, k)
1553 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1554 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1555 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1556 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1557 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1561 m = find_token(document.body, "\\begin_layout Code", m)
1563 endm = find_end_of_layout(document.body, m)
1564 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1565 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1566 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1567 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1568 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1572 def convert_subref(document):
1573 " converts sub: ref prefixes to subref: "
1576 rx = re.compile(r'^name \"sub:(.+)$')
1579 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1582 j = find_end_of_inset(document.body, i)
1584 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1588 for p in range(i, j):
1589 m = rx.match(document.body[p])
1592 document.body[p] = "name \"subsec:" + label
1596 rx = re.compile(r'^reference \"sub:(.+)$')
1599 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1602 j = find_end_of_inset(document.body, i)
1604 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1608 for p in range(i, j):
1609 m = rx.match(document.body[p])
1612 document.body[p] = "reference \"subsec:" + label
1618 def revert_subref(document):
1619 " reverts subref: ref prefixes to sub: "
1622 rx = re.compile(r'^name \"subsec:(.+)$')
1625 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1628 j = find_end_of_inset(document.body, i)
1630 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1634 for p in range(i, j):
1635 m = rx.match(document.body[p])
1638 document.body[p] = "name \"sub:" + label
1643 rx = re.compile(r'^reference \"subsec:(.+)$')
1646 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1649 j = find_end_of_inset(document.body, i)
1651 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1655 for p in range(i, j):
1656 m = rx.match(document.body[p])
1659 document.body[p] = "reference \"sub:" + label
1664 def convert_nounzip(document):
1665 " remove the noUnzip parameter of graphics insets "
1667 rx = re.compile(r'\s*noUnzip\s*$')
1670 i = find_token(document.body, "\\begin_inset Graphics", i)
1673 j = find_end_of_inset(document.body, i)
1675 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1679 k = find_re(document.body, rx, i, j)
1681 del document.body[k]
1686 def convert_revert_external_bbox(document, forward):
1687 " add units to bounding box of external insets "
1689 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1692 i = find_token(document.body, "\\begin_inset External", i)
1695 j = find_end_of_inset(document.body, i)
1697 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1700 k = find_re(document.body, rx, i, j)
1704 tokens = document.body[k].split()
1706 for t in range(1, 5):
1709 for t in range(1, 5):
1710 tokens[t] = length_in_bp(tokens[t])
1711 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1712 tokens[3] + " " + tokens[4]
1716 def convert_external_bbox(document):
1717 convert_revert_external_bbox(document, True)
1720 def revert_external_bbox(document):
1721 convert_revert_external_bbox(document, False)
1724 def revert_tcolorbox_1(document):
1725 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1728 i = find_token(document.header, "tcolorbox", i)
1734 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1737 flexEnd = find_end_of_inset(document.body, flex)
1738 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1739 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1740 flexEnd = find_end_of_inset(document.body, flex)
1742 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1744 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1745 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1749 def revert_tcolorbox_2(document):
1750 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1753 i = find_token(document.header, "tcolorbox", i)
1759 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1762 flexEnd = find_end_of_inset(document.body, flex)
1763 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1764 flexEnd = find_end_of_inset(document.body, flex)
1765 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1766 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1770 def revert_tcolorbox_3(document):
1771 " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1774 i = find_token(document.header, "tcolorbox", i)
1780 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1783 flexEnd = find_end_of_inset(document.body, flex)
1784 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1785 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1786 flexEnd = find_end_of_inset(document.body, flex)
1787 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1788 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1792 def revert_tcolorbox_4(document):
1793 " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1796 i = find_token(document.header, "tcolorbox", i)
1802 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1805 flexEnd = find_end_of_inset(document.body, flex)
1806 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1807 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1808 flexEnd = find_end_of_inset(document.body, flex)
1809 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1810 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1814 def revert_tcolorbox_5(document):
1815 " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1818 i = find_token(document.header, "tcolorbox", i)
1824 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1827 flexEnd = find_end_of_inset(document.body, flex)
1828 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1829 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1830 flexEnd = find_end_of_inset(document.body, flex)
1831 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1832 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1836 def revert_tcolorbox_6(document):
1837 " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1840 i = find_token(document.header, "tcolorbox", i)
1846 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1849 flexEnd = find_end_of_inset(document.body, flex)
1850 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1851 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1852 flexEnd = find_end_of_inset(document.body, flex)
1853 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1854 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1858 def revert_tcolorbox_7(document):
1859 " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1862 i = find_token(document.header, "tcolorbox", i)
1868 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1871 flexEnd = find_end_of_inset(document.body, flex)
1872 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1873 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1874 flexEnd = find_end_of_inset(document.body, flex)
1875 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1876 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1880 def revert_tcolorbox_8(document):
1881 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1887 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1889 j = find_end_of_layout(document.body, i)
1890 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1891 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1892 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1893 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1895 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1897 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1898 k = find_end_of_inset(document.body, j)
1899 k = find_token(document.body, "\\end_inset", k + 1)
1900 k = find_token(document.body, "\\end_inset", k + 1)
1902 k = find_token(document.body, "\\end_inset", k + 1)
1903 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1904 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1905 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1911 def revert_moderncv_1(document):
1912 " Reverts the new inset of moderncv to TeX-code in preamble "
1914 if document.textclass != "moderncv":
1920 # at first revert the new styles
1922 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1925 j = find_end_of_layout(document.body, i)
1927 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1930 content = lyx2latex(document, document.body[i:j + 1])
1931 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1932 del document.body[i:j + 1]
1934 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1937 j = find_end_of_layout(document.body, i)
1939 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1942 content = lyx2latex(document, document.body[i:j + 1])
1943 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1944 del document.body[i:j + 1]
1945 # now change the new styles to the obsolete ones
1947 i = find_token(document.body, "\\begin_layout Name", 0)
1950 j = find_end_of_layout(document.body, i)
1952 document.warning("Malformed LyX document: Can't find end of Name layout")
1955 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1956 if lineArg > j and j != 0:
1959 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1960 # we have to assure that no other inset is in the Argument
1961 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1962 endInset = find_token(document.body, "\\end_inset", beginPlain)
1965 while beginInset < endInset and beginInset != -1:
1966 beginInset = find_token(document.body, "\\begin_inset", k)
1967 endInset = find_token(document.body, "\\end_inset", l)
1970 Arg2 = document.body[l + 5 : l + 6]
1972 document.body[i : i + 1]= ["\\begin_layout FirstName"]
1973 # delete the Argument inset
1974 del( document.body[endInset - 2 : endInset + 3])
1975 del( document.body[lineArg : beginPlain + 1])
1976 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1979 def revert_moderncv_2(document):
1980 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1982 if document.textclass != "moderncv":
1989 i = find_token(document.body, "\\begin_layout Phone", i)
1992 j = find_end_of_layout(document.body, i)
1994 document.warning("Malformed LyX document: Can't find end of Phone layout")
1997 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1998 if lineArg > j and j != 0:
2002 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
2003 # we have to assure that no other inset is in the Argument
2004 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
2005 endInset = find_token(document.body, "\\end_inset", beginPlain)
2008 while beginInset < endInset and beginInset != -1:
2009 beginInset = find_token(document.body, "\\begin_inset", k)
2010 endInset = find_token(document.body, "\\end_inset", l)
2013 Arg = document.body[beginPlain + 1 : beginPlain + 2]
2015 if Arg[0] == "mobile":
2016 document.body[i : i + 1]= ["\\begin_layout Mobile"]
2018 document.body[i : i + 1]= ["\\begin_layout Fax"]
2019 # delete the Argument inset
2020 del(document.body[endInset - 2 : endInset + 1])
2021 del(document.body[lineArg : beginPlain + 3])
2025 def convert_moderncv_phone(document):
2026 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
2028 if document.textclass != "moderncv":
2035 "Mobile" : "mobile",
2039 rx = re.compile(r'^\\begin_layout (\S+)$')
2041 # substitute \fax and \mobile by \phone[fax] and \phone[mobile], respectively
2042 i = find_token(document.body, "\\begin_layout", i)
2046 m = rx.match(document.body[i])
2050 if val not in list(phone_dict.keys()):
2053 j = find_end_of_layout(document.body, i)
2055 document.warning("Malformed LyX document: Can't find end of Mobile layout")
2059 document.body[i : i + 1] = ["\\begin_layout Phone", "\\begin_inset Argument 1", "status open", "",
2060 "\\begin_layout Plain Layout", phone_dict[val], "\\end_layout", "",
2064 def convert_moderncv_name(document):
2065 " Convert the FirstName and LastName layout of moderncv to the general Name layout "
2067 if document.textclass != "moderncv":
2070 fnb = 0 # Begin of FirstName inset
2071 fne = 0 # End of FirstName inset
2072 lnb = 0 # Begin of LastName (FamilyName) inset
2073 lne = 0 # End of LastName (FamilyName) inset
2074 nb = 0 # Begin of substituting Name inset
2075 ne = 0 # End of substituting Name inset
2076 FirstName = [] # FirstName content
2077 FamilyName = [] # LastName content
2081 fnb = find_token(document.body, "\\begin_layout FirstName", fnb)
2083 fne = find_end_of_layout(document.body, fnb)
2085 document.warning("Malformed LyX document: Can't find end of FirstName layout")
2087 FirstName = document.body[fnb + 1 : fne]
2089 lnb = find_token(document.body, "\\begin_layout FamilyName", lnb)
2091 lne = find_end_of_layout(document.body, lnb)
2093 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
2095 FamilyName = document.body[lnb + 1 : lne]
2096 # Determine the region for the substituting Name layout
2097 if fnb == -1 and lnb == -1: # Neither FirstName nor FamilyName exists -> Do nothing
2099 elif fnb == -1: # Only FamilyName exists -> New Name insets replaces that
2102 elif lnb == -1: # Only FirstName exists -> New Name insets replaces that
2105 elif fne > lne: # FirstName position before FamilyName -> New Name insets spans
2106 nb = lnb # from FamilyName begin
2107 ne = fne # to FirstName end
2108 else: # FirstName position before FamilyName -> New Name insets spans
2109 nb = fnb # from FirstName begin
2110 ne = lne # to FamilyName end
2112 # Insert the substituting layout now. If FirstName exists, use an otpional argument.
2114 document.body[nb : ne + 1] = ["\\begin_layout Name"] + FamilyName + ["\\end_layout", ""]
2116 document.body[nb : ne + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2117 "\\begin_layout Plain Layout"] + FirstName + ["\\end_layout", "",
2118 "\\end_inset", ""] + FamilyName + ["\\end_layout", ""]
2121 def revert_achemso(document):
2122 " Reverts the flex inset Latin to TeX code "
2124 if document.textclass != "achemso":
2129 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2131 j = find_end_of_inset(document.body, i)
2135 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2136 endPlain = find_end_of_layout(document.body, beginPlain)
2137 content = lyx2latex(document, document.body[beginPlain : endPlain])
2138 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2140 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2145 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2146 "\\font_sf_scale", "\\font_tt_scale"]
2147 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2148 fontquotes = [True, True, True, True, False, False]
2150 def convert_fontsettings(document):
2151 " Duplicate font settings "
2153 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2155 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2156 use_non_tex_fonts = "false"
2158 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2160 for f in fontsettings:
2161 i = find_token(document.header, f + " ", 0)
2163 document.warning("Malformed LyX document: No " + f + "!")
2165 # note that with i = -1, this will insert at the end
2167 value = fontdefaults[j]
2169 value = document.header[i][len(f):].strip()
2171 if use_non_tex_fonts == "true":
2172 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2174 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2176 if use_non_tex_fonts == "true":
2177 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2179 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2183 def revert_fontsettings(document):
2184 " Merge font settings "
2186 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2188 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2189 use_non_tex_fonts = "false"
2191 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2193 for f in fontsettings:
2194 i = find_token(document.header, f + " ", 0)
2196 document.warning("Malformed LyX document: No " + f + "!")
2199 line = get_value(document.header, f, i)
2202 q2 = line.find('"', q1+1)
2203 q3 = line.find('"', q2+1)
2204 q4 = line.find('"', q3+1)
2205 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2206 document.warning("Malformed LyX document: Missing quotes!")
2209 if use_non_tex_fonts == "true":
2210 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2212 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2214 if use_non_tex_fonts == "true":
2215 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2217 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2221 def revert_solution(document):
2222 " Reverts the solution environment of the theorem module to TeX code "
2224 # Do we use one of the modules that provides Solution?
2226 mods = document.get_module_list()
2228 if mod == "theorems-std" or mod == "theorems-bytype" \
2229 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2239 i = find_token(document.body, "\\begin_layout Solution", i)
2243 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2244 if is_starred == True:
2246 LyXName = "Solution*"
2247 theoremName = "newtheorem*"
2250 LyXName = "Solution"
2251 theoremName = "newtheorem"
2253 j = find_end_of_layout(document.body, i)
2255 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2259 # if this is not a consecutive env, add start command
2262 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2264 # has this a consecutive theorem of same type?
2265 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2267 # if this is not followed by a consecutive env, add end command
2269 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2271 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2273 add_to_preamble(document, "\\theoremstyle{definition}")
2274 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2275 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2276 (theoremName, LaTeXName))
2277 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2278 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2279 (theoremName, LaTeXName))
2281 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2285 def revert_verbatim_star(document):
2286 from lyx_2_1 import revert_verbatim
2287 revert_verbatim(document, True)
2290 def convert_save_props(document):
2291 " Add save_transient_properties parameter. "
2292 i = find_token(document.header, '\\begin_header', 0)
2294 document.warning("Malformed lyx document: Missing '\\begin_header'.")
2296 document.header.insert(i + 1, '\\save_transient_properties true')
2299 def revert_save_props(document):
2300 " Remove save_transient_properties parameter. "
2301 i = find_token(document.header, "\\save_transient_properties", 0)
2304 del document.header[i]
2307 def convert_info_tabular_feature(document):
2309 return arg.replace("inset-modify tabular", "tabular-feature")
2310 convert_info_insets(document, "shortcut(s)?|icon", f)
2313 def revert_info_tabular_feature(document):
2315 return arg.replace("tabular-feature", "inset-modify tabular")
2316 convert_info_insets(document, "shortcut(s)?|icon", f)
2323 supported_versions = ["2.2.0", "2.2"]
2325 [475, [convert_separator]],
2326 # nothing to do for 476: We consider it a bug that older versions
2327 # did not load amsmath automatically for these commands, and do not
2328 # want to hardcode amsmath off.
2334 [481, [convert_dashes]],
2335 [482, [convert_phrases]],
2336 [483, [convert_specialchar]],
2341 [488, [convert_newgloss]],
2342 [489, [convert_BoxFeatures]],
2343 [490, [convert_origin]],
2345 [492, [convert_colorbox]],
2348 [495, [convert_subref]],
2349 [496, [convert_nounzip]],
2350 [497, [convert_external_bbox]],
2352 [499, [convert_moderncv_phone, convert_moderncv_name]],
2354 [501, [convert_fontsettings]],
2357 [504, [convert_save_props]],
2359 [506, [convert_info_tabular_feature]],
2360 [507, [convert_longtable_label]],
2361 [508, [convert_parbreak]]
2365 [507, [revert_parbreak]],
2366 [506, [revert_longtable_label]],
2367 [505, [revert_info_tabular_feature]],
2369 [503, [revert_save_props]],
2370 [502, [revert_verbatim_star]],
2371 [501, [revert_solution]],
2372 [500, [revert_fontsettings]],
2373 [499, [revert_achemso]],
2374 [498, [revert_moderncv_1, revert_moderncv_2]],
2375 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2376 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2377 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2378 [496, [revert_external_bbox]],
2379 [495, []], # nothing to do since the noUnzip parameter was optional
2380 [494, [revert_subref]],
2381 [493, [revert_jss]],
2382 [492, [revert_mathmulticol]],
2383 [491, [revert_colorbox]],
2384 [490, [revert_textcolor]],
2385 [489, [revert_origin]],
2386 [488, [revert_BoxFeatures]],
2387 [487, [revert_newgloss, revert_glossgroup]],
2388 [486, [revert_forest]],
2389 [485, [revert_ex_itemargs]],
2390 [484, [revert_sigplan_doi]],
2391 [483, [revert_georgian]],
2392 [482, [revert_specialchar]],
2393 [481, [revert_phrases]],
2394 [480, [revert_dashes]],
2395 [479, [revert_question_env]],
2396 [478, [revert_beamer_lemma]],
2397 [477, [revert_xarrow]],
2398 [476, [revert_swissgerman]],
2399 [475, [revert_smash]],
2400 [474, [revert_separator]]
2404 if __name__ == "__main__":