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, lyx2latex, \
33 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 find_token, find_token_backwards, find_re, \
38 find_end_of_inset, find_end_of_layout, find_nonempty_line, \
39 get_containing_layout, get_value, check_token
41 ####################################################################
42 # Private helper functions
44 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt, nolastopt):
46 Reverts an InsetArgument to TeX-code
48 revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt, notLastOpt)
49 LineOfBegin is the line of the \begin_layout or \begin_inset statement
50 LineOfEnd is the line of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
51 StartArgument is the number of the first argument that needs to be converted
52 EndArgument is the number of the last argument that needs to be converted or the last defined one
53 isEnvironment must be true, if the layout is for a LaTeX environment
54 isOpt must be true, if the argument is an optional one
55 notLastOpt must be true if the argument is mandatory and followed by optional ones
59 while lineArg != -1 and n < nmax + 1:
60 lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
61 if lineArg > endline and endline != 0:
64 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
65 # we have to assure that no other inset is in the Argument
66 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
67 endInset = find_token(document.body, "\\end_inset", beginPlain)
70 while beginInset < endInset and beginInset != -1:
71 beginInset = find_token(document.body, "\\begin_inset", k)
72 endInset = find_token(document.body, "\\end_inset", l)
75 if environment == False:
77 if nolastopt == False:
78 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
80 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
81 del(document.body[lineArg : beginPlain + 1])
84 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
85 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("{")
93 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
94 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
100 ###############################################################################
102 ### Conversion and reversion routines
104 ###############################################################################
106 def convert_longtable_label_internal(document, forward):
108 Convert reference to "LongTableNoNumber" into "Unnumbered" if forward is True
111 old_reference = "\\begin_inset Caption LongTableNoNumber"
112 new_reference = "\\begin_inset Caption Unnumbered"
114 # if the purpose is to revert swap the strings roles
116 old_reference, new_reference = new_reference, old_reference
120 i = find_token(document.body, old_reference, i)
125 document.body[i] = new_reference
128 def convert_longtable_label(document):
129 convert_longtable_label_internal(document, True)
132 def revert_longtable_label(document):
133 convert_longtable_label_internal(document, False)
136 def convert_separator(document):
138 Convert layout separators to separator insets and add (LaTeX) paragraph
139 breaks in order to mimic previous LaTeX export.
142 parins = ["\\begin_inset Separator parbreak", "\\end_inset", ""]
143 parlay = ["\\begin_layout Standard", "\\begin_inset Separator parbreak",
144 "\\end_inset", "", "\\end_layout", ""]
146 "family" : "default",
147 "series" : "default",
156 i = find_token(document.body, "\\begin_deeper", i)
160 j = find_token_backwards(document.body, "\\end_layout", i-1)
162 # reset any text style before inserting the inset
163 lay = get_containing_layout(document.body, j-1)
165 content = "\n".join(document.body[lay[1]:lay[2]])
166 for val in list(sty_dict.keys()):
167 if content.find("\\%s" % val) != -1:
168 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
171 document.body[j:j] = parins
172 i = i + len(parins) + 1
178 i = find_token(document.body, "\\align", i)
182 lay = get_containing_layout(document.body, i)
183 if lay != False and lay[0] == "Plain Layout":
187 j = find_token_backwards(document.body, "\\end_layout", i-1)
189 lay = get_containing_layout(document.body, j-1)
190 if lay != False and lay[0] == "Standard" \
191 and find_token(document.body, "\\align", lay[1], lay[2]) == -1 \
192 and find_token(document.body, "\\begin_inset VSpace", lay[1], lay[2]) == -1:
193 # reset any text style before inserting the inset
194 content = "\n".join(document.body[lay[1]:lay[2]])
195 for val in list(sty_dict.keys()):
196 if content.find("\\%s" % val) != -1:
197 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
200 document.body[j:j] = parins
201 i = i + len(parins) + 1
207 regexp = re.compile(r'^\\begin_layout (?:(-*)|(\s*))(Separator|EndOfSlide)(?:(-*)|(\s*))$', re.IGNORECASE)
211 i = find_re(document.body, regexp, i)
215 j = find_end_of_layout(document.body, i)
217 document.warning("Malformed LyX document: Missing `\\end_layout'.")
220 lay = get_containing_layout(document.body, j-1)
222 lines = document.body[lay[3]:lay[2]]
226 document.body[i:j+1] = parlay
228 document.body[i+1:i+1] = lines
230 i = i + len(parlay) + len(lines) + 1
233 def revert_separator(document):
234 " Revert separator insets to layout separators "
236 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
237 if document.textclass in beamer_classes:
238 beglaysep = "\\begin_layout Separator"
240 beglaysep = "\\begin_layout --Separator--"
242 parsep = [beglaysep, "", "\\end_layout", ""]
243 comert = ["\\begin_inset ERT", "status collapsed", "",
244 "\\begin_layout Plain Layout", "%", "\\end_layout",
245 "", "\\end_inset", ""]
246 empert = ["\\begin_inset ERT", "status collapsed", "",
247 "\\begin_layout Plain Layout", " ", "\\end_layout",
248 "", "\\end_inset", ""]
252 i = find_token(document.body, "\\begin_inset Separator", i)
256 lay = get_containing_layout(document.body, i)
258 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
265 kind = get_value(document.body, "\\begin_inset Separator", i, i+1, "plain").split()[1]
266 before = document.body[beg+1:i]
267 something_before = len(before) > 0 and len("".join(before)) > 0
268 j = find_end_of_inset(document.body, i)
269 after = document.body[j+1:end]
270 something_after = len(after) > 0 and len("".join(after)) > 0
272 beg = beg + len(before) + 1
273 elif something_before:
274 document.body[i:i] = ["\\end_layout", ""]
282 document.body[beg:j+1] = empert
285 document.body[beg:j+1] = comert
289 if layoutname == "Standard":
290 if not something_before:
291 document.body[beg:j+1] = parsep
293 document.body[i:i] = ["", "\\begin_layout Standard"]
296 document.body[beg:j+1] = ["\\begin_layout Standard"]
299 document.body[beg:j+1] = ["\\begin_deeper"]
301 end = end + 1 - (j + 1 - beg)
302 if not something_before:
303 document.body[i:i] = parsep
305 end = end + len(parsep)
306 document.body[i:i] = ["\\begin_layout Standard"]
307 document.body[end+2:end+2] = ["", "\\end_deeper", ""]
310 next_par_is_aligned = False
311 k = find_nonempty_line(document.body, end+1)
312 if k != -1 and check_token(document.body[k], "\\begin_layout"):
313 lay = get_containing_layout(document.body, k)
314 next_par_is_aligned = lay != False and \
315 find_token(document.body, "\\align", lay[1], lay[2]) != -1
316 if k != -1 and not next_par_is_aligned \
317 and not check_token(document.body[k], "\\end_deeper") \
318 and not check_token(document.body[k], "\\begin_deeper"):
319 if layoutname == "Standard":
320 document.body[beg:j+1] = [beglaysep]
323 document.body[beg:j+1] = ["\\begin_deeper", beglaysep]
324 end = end + 2 - (j + 1 - beg)
325 document.body[end+1:end+1] = ["", "\\end_deeper", ""]
329 del document.body[i:end+1]
331 del document.body[i:end-1]
336 def convert_parbreak(document):
338 Convert parbreak separators not specifically used to separate
339 environments to latexpar separators.
341 parbreakinset = "\\begin_inset Separator parbreak"
344 i = find_token(document.body, parbreakinset, i)
347 lay = get_containing_layout(document.body, i)
349 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
352 if lay[0] == "Standard":
353 # Convert only if not alone in the paragraph
354 k1 = find_nonempty_line(document.body, lay[1] + 1, i + 1)
355 k2 = find_nonempty_line(document.body, i + 1, lay[2])
356 if (k1 < i) or (k2 > i + 1) or not check_token(document.body[i], parbreakinset):
357 document.body[i] = document.body[i].replace("parbreak", "latexpar")
359 document.body[i] = document.body[i].replace("parbreak", "latexpar")
363 def revert_parbreak(document):
365 Revert latexpar separators to parbreak separators.
369 i = find_token(document.body, "\\begin_inset Separator latexpar", i)
372 document.body[i] = document.body[i].replace("latexpar", "parbreak")
376 def revert_smash(document):
377 " Set amsmath to on if smash commands are used "
379 commands = ["smash[t]", "smash[b]", "notag"]
380 i = find_token(document.header, "\\use_package amsmath", 0)
382 document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
384 value = get_value(document.header, "\\use_package amsmath", i).split()[1]
386 # nothing to do if package is not auto but on or off
390 j = find_token(document.body, '\\begin_inset Formula', j)
393 k = find_end_of_inset(document.body, j)
395 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
398 code = "\n".join(document.body[j:k])
400 if code.find("\\%s" % c) != -1:
401 # set amsmath to on, since it is loaded by the newer format
402 document.header[i] = "\\use_package amsmath 2"
407 def revert_swissgerman(document):
408 " Set language german-ch-old to german "
410 if document.language == "german-ch-old":
411 document.language = "german"
412 i = find_token(document.header, "\\language", 0)
414 document.header[i] = "\\language german"
417 j = find_token(document.body, "\\lang german-ch-old", j)
420 document.body[j] = document.body[j].replace("\\lang german-ch-old", "\\lang german")
424 def revert_use_package(document, pkg, commands, oldauto, supported):
425 # oldauto defines how the version we are reverting to behaves:
426 # if it is true, the old version uses the package automatically.
427 # if it is false, the old version never uses the package.
428 # If "supported" is true, the target version also supports this
430 regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
431 p = find_re(document.header, regexp, 0)
432 value = "1" # default is auto
434 value = get_value(document.header, "\\use_package" , p).split()[1]
436 del document.header[p]
437 if value == "2" and not supported: # on
438 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
439 elif value == "1" and not oldauto: # auto
442 i = find_token(document.body, '\\begin_inset Formula', i)
445 j = find_end_of_inset(document.body, i)
447 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
450 code = "\n".join(document.body[i:j])
452 if code.find("\\%s" % c) != -1:
454 document.header[p] = "\\use_package " + pkg + " 2"
456 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
461 mathtools_commands = ["xhookrightarrow", "xhookleftarrow", "xRightarrow", \
462 "xrightharpoondown", "xrightharpoonup", "xrightleftharpoons", \
463 "xLeftarrow", "xleftharpoondown", "xleftharpoonup", \
464 "xleftrightarrow", "xLeftrightarrow", "xleftrightharpoons", \
467 def revert_xarrow(document):
468 "remove use_package mathtools"
469 revert_use_package(document, "mathtools", mathtools_commands, False, True)
472 def revert_beamer_lemma(document):
473 " Reverts beamer lemma layout to ERT "
475 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
476 if document.textclass not in beamer_classes:
482 i = find_token(document.body, "\\begin_layout Lemma", i)
485 j = find_end_of_layout(document.body, i)
487 document.warning("Malformed LyX document: Can't find end of Lemma layout")
490 arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j)
491 endarg1 = find_end_of_inset(document.body, arg1)
492 arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j)
493 endarg2 = find_end_of_inset(document.body, arg2)
497 beginPlain1 = find_token(document.body, "\\begin_layout Plain Layout", arg1, endarg1)
498 if beginPlain1 == -1:
499 document.warning("Malformed LyX document: Can't find arg1 plain Layout")
502 endPlain1 = find_end_of_inset(document.body, beginPlain1)
503 content1 = document.body[beginPlain1 + 1 : endPlain1 - 2]
504 subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">")
506 beginPlain2 = find_token(document.body, "\\begin_layout Plain Layout", arg2, endarg2)
507 if beginPlain2 == -1:
508 document.warning("Malformed LyX document: Can't find arg2 plain Layout")
511 endPlain2 = find_end_of_inset(document.body, beginPlain2)
512 content2 = document.body[beginPlain2 + 1 : endPlain2 - 2]
513 subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]")
517 del document.body[arg2 : endarg2 + 1]
519 del document.body[arg1 : endarg1 + 1]
521 del document.body[arg1 : endarg1 + 1]
523 del document.body[arg2 : endarg2 + 1]
525 # index of end layout has probably changed
526 j = find_end_of_layout(document.body, i)
528 document.warning("Malformed LyX document: Can't find end of Lemma layout")
534 # if this is not a consecutive env, add start command
536 begcmd = put_cmd_in_ert("\\begin{lemma}")
538 # has this a consecutive lemma?
539 consecutive = document.body[j + 2] == "\\begin_layout Lemma"
541 # if this is not followed by a consecutive env, add end command
543 document.body[j : j + 1] = put_cmd_in_ert("\\end{lemma}") + ["\\end_layout"]
545 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd + subst1 + subst2
551 def revert_question_env(document):
553 Reverts question and question* environments of
554 theorems-ams-extended-bytype module to ERT
557 # Do we use theorems-ams-extended-bytype module?
558 if not "theorems-ams-extended-bytype" in document.get_module_list():
564 i = find_token(document.body, "\\begin_layout Question", i)
568 starred = document.body[i] == "\\begin_layout Question*"
570 j = find_end_of_layout(document.body, i)
572 document.warning("Malformed LyX document: Can't find end of Question layout")
576 # if this is not a consecutive env, add start command
580 begcmd = put_cmd_in_ert("\\begin{question*}")
582 begcmd = put_cmd_in_ert("\\begin{question}")
584 # has this a consecutive theorem of same type?
587 consecutive = document.body[j + 2] == "\\begin_layout Question*"
589 consecutive = document.body[j + 2] == "\\begin_layout Question"
591 # if this is not followed by a consecutive env, add end command
594 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
596 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
598 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
600 add_to_preamble(document, "\\providecommand{\questionname}{Question}")
603 add_to_preamble(document, "\\theoremstyle{plain}\n" \
604 "\\newtheorem*{question*}{\\protect\\questionname}")
606 add_to_preamble(document, "\\theoremstyle{plain}\n" \
607 "\\newtheorem{question}{\\protect\\questionname}")
612 def convert_dashes(document):
613 "convert -- and --- to \\twohyphens and \\threehyphens"
615 if document.backend != "latex":
619 while i < len(document.body):
620 words = document.body[i].split()
621 if len(words) > 1 and words[0] == "\\begin_inset" and \
622 words[1] in ["CommandInset", "ERT", "External", "Formula", "FormulaMacro", "Graphics", "IPA", "listings"]:
623 # must not replace anything in insets that store LaTeX contents in .lyx files
624 # (math and command insets withut overridden read() and write() methods
625 # filtering out IPA makes Text::readParToken() more simple
626 # skip ERT as well since it is not needed there
627 j = find_end_of_inset(document.body, i)
629 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
634 if len(words) > 0 and words[0] in ["\\leftindent", "\\paragraph_spacing", "\\align", "\\labelwidthstring"]:
635 # skip paragraph parameters (bug 10243)
639 j = document.body[i].find("--")
642 front = document.body[i][:j]
643 back = document.body[i][j+2:]
644 # We can have an arbitrary number of consecutive hyphens.
645 # These must be split into the corresponding number of two and three hyphens
646 # We must match what LaTeX does: First try emdash, then endash, then single hyphen
647 if back.find("-") == 0:
650 document.body.insert(i+1, back)
651 document.body[i] = front + "\\threehyphens"
654 document.body.insert(i+1, back)
655 document.body[i] = front + "\\twohyphens"
659 def revert_dashes(document):
660 "convert \\twohyphens and \\threehyphens to -- and ---"
662 # eventually remove preamble code from 2.3->2.2 conversion:
663 for i, line in enumerate(document.preamble):
664 if i > 1 and line == r'\renewcommand{\textemdash}{---}':
665 if (document.preamble[i-1] == r'\renewcommand{\textendash}{--}'
666 and document.preamble[i-2] == '% Added by lyx2lyx'):
667 del document.preamble[i-2:i+1]
669 while i < len(document.body):
670 words = document.body[i].split()
671 if len(words) > 1 and words[0] == "\\begin_inset" and \
672 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
674 j = find_end_of_inset(document.body, i)
676 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
682 if document.body[i].find("\\twohyphens") >= 0:
683 document.body[i] = document.body[i].replace("\\twohyphens", "--")
685 if document.body[i].find("\\threehyphens") >= 0:
686 document.body[i] = document.body[i].replace("\\threehyphens", "---")
688 if replaced and i+1 < len(document.body) and \
689 (document.body[i+1].find("\\") != 0 or \
690 document.body[i+1].find("\\twohyphens") == 0 or
691 document.body[i+1].find("\\threehyphens") == 0) and \
692 len(document.body[i]) + len(document.body[i+1]) <= 80:
693 document.body[i] = document.body[i] + document.body[i+1]
694 document.body[i+1:i+2] = []
699 # order is important for the last three!
700 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
702 def is_part_of_converted_phrase(line, j, phrase):
703 "is phrase part of an already converted phrase?"
705 converted = "\\SpecialCharNoPassThru \\" + p
706 pos = j + len(phrase) - len(converted)
708 if line[pos:pos+len(converted)] == converted:
713 def convert_phrases(document):
714 "convert special phrases from plain text to \\SpecialCharNoPassThru"
716 if document.backend != "latex":
719 for phrase in phrases:
721 while i < len(document.body):
722 words = document.body[i].split()
723 if len(words) > 1 and words[0] == "\\begin_inset" and \
724 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
725 # must not replace anything in insets that store LaTeX contents in .lyx files
726 # (math and command insets withut overridden read() and write() methods
727 j = find_end_of_inset(document.body, i)
729 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
734 if document.body[i].find("\\") == 0:
737 j = document.body[i].find(phrase)
741 if not is_part_of_converted_phrase(document.body[i], j, phrase):
742 front = document.body[i][:j]
743 back = document.body[i][j+len(phrase):]
745 document.body.insert(i+1, back)
746 # We cannot use SpecialChar since we do not know whether we are outside passThru
747 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
751 def revert_phrases(document):
752 "convert special phrases to plain text"
755 while i < len(document.body):
756 words = document.body[i].split()
757 if len(words) > 1 and words[0] == "\\begin_inset" and \
758 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
759 # see convert_phrases
760 j = find_end_of_inset(document.body, i)
762 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
768 for phrase in phrases:
769 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
770 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
771 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
773 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
774 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
776 if replaced and i+1 < len(document.body) and \
777 (document.body[i+1].find("\\") != 0 or \
778 document.body[i+1].find("\\SpecialChar") == 0) and \
779 len(document.body[i]) + len(document.body[i+1]) <= 80:
780 document.body[i] = document.body[i] + document.body[i+1]
781 document.body[i+1:i+2] = []
786 def convert_specialchar_internal(document, forward):
787 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
788 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
789 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
790 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
791 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
792 "\\LaTeX":"LaTeX" # must be after LaTeX2e
796 while i < len(document.body):
797 words = document.body[i].split()
798 if len(words) > 1 and words[0] == "\\begin_inset" and \
799 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
800 # see convert_phrases
801 j = find_end_of_inset(document.body, i)
803 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
808 for key, value in specialchars.items():
810 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
811 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
813 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
814 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
818 def convert_specialchar(document):
819 "convert special characters to new syntax"
820 convert_specialchar_internal(document, True)
823 def revert_specialchar(document):
824 "convert special characters to old syntax"
825 convert_specialchar_internal(document, False)
828 def revert_georgian(document):
829 "Set the document language to English but assure Georgian output"
831 if document.language == "georgian":
832 document.language = "english"
833 i = find_token(document.header, "\\language georgian", 0)
835 document.header[i] = "\\language english"
836 j = find_token(document.header, "\\language_package default", 0)
838 document.header[j] = "\\language_package babel"
839 k = find_token(document.header, "\\options", 0)
841 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
843 l = find_token(document.header, "\\use_default_options", 0)
844 document.header.insert(l + 1, "\\options georgian")
847 def revert_sigplan_doi(document):
848 " Reverts sigplanconf DOI layout to ERT "
850 if document.textclass != "sigplanconf":
855 i = find_token(document.body, "\\begin_layout DOI", i)
858 j = find_end_of_layout(document.body, i)
860 document.warning("Malformed LyX document: Can't find end of DOI layout")
864 content = lyx2latex(document, document.body[i:j + 1])
865 add_to_preamble(document, ["\\doi{" + content + "}"])
866 del document.body[i:j + 1]
870 def revert_ex_itemargs(document):
871 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
873 if not "linguistics" in document.get_module_list():
877 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
879 i = find_token(document.body, "\\begin_inset Argument item:", i)
882 j = find_end_of_inset(document.body, i)
883 # Find containing paragraph layout
884 parent = get_containing_layout(document.body, i)
886 document.warning("Malformed LyX document: Can't find parent paragraph layout")
890 layoutname = parent[0]
891 if layoutname in example_layouts:
892 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
893 endPlain = find_end_of_layout(document.body, beginPlain)
894 content = document.body[beginPlain + 1 : endPlain]
895 del document.body[i:j+1]
896 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
897 document.body[parbeg : parbeg] = subst
901 def revert_forest(document):
902 " Reverts the forest environment (Linguistics module) to TeX-code "
904 if not "linguistics" in document.get_module_list():
909 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
912 j = find_end_of_inset(document.body, i)
914 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
918 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
919 endPlain = find_end_of_layout(document.body, beginPlain)
920 content = lyx2latex(document, document.body[beginPlain : endPlain])
922 add_to_preamble(document, ["\\usepackage{forest}"])
924 document.body[i:j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
928 def revert_glossgroup(document):
929 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
931 if not "linguistics" in document.get_module_list():
936 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
939 j = find_end_of_inset(document.body, i)
941 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
945 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
946 endPlain = find_end_of_layout(document.body, beginPlain)
947 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
949 document.body[i:j + 1] = ["{", "", content, "", "}"]
953 def revert_newgloss(document):
954 " Reverts the new Glosse insets (Linguistics module) to the old format "
956 if not "linguistics" in document.get_module_list():
959 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
960 for glosse in glosses:
963 i = find_token(document.body, glosse, i)
966 j = find_end_of_inset(document.body, i)
968 document.warning("Malformed LyX document: Can't find end of Glosse inset")
972 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
973 endarg = find_end_of_inset(document.body, arg)
976 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
977 if argbeginPlain == -1:
978 document.warning("Malformed LyX document: Can't find arg plain Layout")
981 argendPlain = find_end_of_inset(document.body, argbeginPlain)
982 argcontent = lyx2verbatim(document, document.body[argbeginPlain : argendPlain - 2])
984 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
985 argcontent, "\\end_layout"]
987 # remove Arg insets and paragraph, if it only contains this inset
988 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
989 del document.body[arg - 1 : endarg + 4]
991 del document.body[arg : endarg + 1]
993 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
994 endPlain = find_end_of_layout(document.body, beginPlain)
995 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
997 document.body[beginPlain + 1:endPlain] = [content]
1000 # Dissolve ERT insets
1001 for glosse in glosses:
1004 i = find_token(document.body, glosse, i)
1007 j = find_end_of_inset(document.body, i)
1009 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1013 ert = find_token(document.body, "\\begin_inset ERT", i, j)
1016 ertend = find_end_of_inset(document.body, ert)
1018 document.warning("Malformed LyX document: Can't find end of ERT inset")
1021 ertcontent = get_ert(document.body, ert, True)
1022 document.body[ert : ertend + 1] = [ertcontent]
1026 def convert_newgloss(document):
1027 " Converts Glosse insets (Linguistics module) to the new format "
1029 if not "linguistics" in document.get_module_list():
1032 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1033 for glosse in glosses:
1036 i = find_token(document.body, glosse, i)
1039 j = find_end_of_inset(document.body, i)
1041 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1048 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
1049 if beginPlain == -1:
1051 endPlain = find_end_of_layout(document.body, beginPlain)
1053 document.warning("Malformed LyX document: Can't find end of Glosse layout")
1057 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
1058 if glt != -1 and document.body[glt + 1].startswith("glt"):
1059 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1060 argcontent = document.body[glt + 1 : endPlain]
1061 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
1062 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
1063 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
1064 "\\end_layout", "", "\\end_inset"]
1066 content = document.body[beginPlain + 1 : endPlain]
1067 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
1068 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
1070 endPlain = find_end_of_layout(document.body, beginPlain)
1072 j = find_end_of_inset(document.body, i)
1077 def convert_BoxFeatures(document):
1078 " adds new box features "
1082 i = find_token(document.body, "height_special", i)
1085 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1089 def revert_BoxFeatures(document):
1090 " outputs new box features as TeX code "
1094 defaultThick = "0.4pt"
1095 defaultShadow = "4pt"
1097 i = find_token(document.body, "thickness", i)
1100 binset = find_token(document.body, "\\begin_inset Box", i - 11)
1101 if binset == -1 or binset != i - 11:
1103 continue # then "thickness" is is just a word in the text
1104 einset = find_end_of_inset(document.body, binset)
1106 document.warning("Malformed LyX document: Can't find end of box inset!")
1109 # read out the values
1110 beg = document.body[i].find('"');
1111 end = document.body[i].rfind('"');
1112 thickness = document.body[i][beg+1:end];
1113 beg = document.body[i+1].find('"');
1114 end = document.body[i+1].rfind('"');
1115 separation = document.body[i+1][beg+1:end];
1116 beg = document.body[i+2].find('"');
1117 end = document.body[i+2].rfind('"');
1118 shadowsize = document.body[i+2][beg+1:end];
1119 # delete the specification
1120 del document.body[i:i+3]
1122 # first output the closing brace
1123 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1124 document.body[einset -1 : einset - 1] = put_cmd_in_ert("}")
1125 # we have now the problem that if there is already \(f)colorbox in ERT around the inset
1126 # the ERT from this routine must be around it
1127 regexp = re.compile(r'^.*colorbox{.*$')
1128 pos = find_re(document.body, regexp, binset - 4)
1129 if pos != -1 and pos == binset - 4:
1133 # now output the lengths
1134 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1135 document.body[pos : pos] = put_cmd_in_ert("{")
1136 if thickness != defaultThick:
1137 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness]
1138 if separation != defaultSep and thickness == defaultThick:
1139 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation]
1140 if separation != defaultSep and thickness != defaultThick:
1141 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1142 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1143 document.body[pos + 5 : pos +6] = ["{\\backslash shadowsize " + shadowsize]
1144 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1145 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1146 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1147 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1148 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1149 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1152 def convert_origin(document):
1153 " Insert the origin tag "
1155 i = find_token(document.header, "\\textclass ", 0)
1157 document.warning("Malformed LyX document: No \\textclass!!")
1159 if document.dir == u'':
1163 if document.systemlyxdir and document.systemlyxdir != u'':
1165 if os.path.isabs(document.dir):
1166 absdir = os.path.normpath(document.dir)
1168 absdir = os.path.normpath(os.path.abspath(document.dir))
1169 if os.path.isabs(document.systemlyxdir):
1170 abssys = os.path.normpath(document.systemlyxdir)
1172 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1173 relpath = os.path.relpath(absdir, abssys)
1174 if relpath.find(u'..') == 0:
1179 origin = document.dir.replace(u'\\', u'/') + u'/'
1181 origin = os.path.join(u"/systemlyxdir", relpath).replace(u'\\', u'/') + u'/'
1182 document.header[i:i] = ["\\origin " + origin]
1185 def revert_origin(document):
1186 " Remove the origin tag "
1188 i = find_token(document.header, "\\origin ", 0)
1190 document.warning("Malformed LyX document: No \\origin!!")
1192 del document.header[i]
1195 color_names = ["brown", "darkgray", "gray", \
1196 "lightgray", "lime", "olive", "orange", \
1197 "pink", "purple", "teal", "violet"]
1199 def revert_textcolor(document):
1200 " revert new \\textcolor colors to TeX code "
1206 i = find_token(document.body, "\\color ", i)
1210 for color in list(color_names):
1211 if document.body[i] == "\\color " + color:
1212 # register that xcolor must be loaded in the preamble
1215 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1216 # find the next \\color and/or the next \\end_layout
1217 j = find_token(document.body, "\\color", i + 1)
1218 k = find_token(document.body, "\\end_layout", i + 1)
1219 if j == -1 and k != -1:
1222 # first output the closing brace
1224 document.body[k: k] = put_cmd_in_ert("}")
1226 document.body[j: j] = put_cmd_in_ert("}")
1227 # now output the \textcolor command
1228 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1232 def convert_colorbox(document):
1233 " adds color settings for boxes "
1237 i = find_token(document.body, "shadowsize", i)
1240 document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1244 def revert_colorbox(document):
1245 " outputs color settings for boxes as TeX code "
1248 defaultframecolor = "black"
1249 defaultbackcolor = "none"
1251 i = find_token(document.body, "framecolor", i)
1254 binset = find_token(document.body, "\\begin_inset Box", i - 14)
1257 einset = find_end_of_inset(document.body, binset)
1259 document.warning("Malformed LyX document: Can't find end of box inset!")
1261 # read out the values
1262 beg = document.body[i].find('"');
1263 end = document.body[i].rfind('"');
1264 framecolor = document.body[i][beg+1:end];
1265 beg = document.body[i + 1].find('"');
1266 end = document.body[i + 1].rfind('"');
1267 backcolor = document.body[i+1][beg+1:end];
1268 # delete the specification
1269 del document.body[i:i + 2]
1271 # first output the closing brace
1272 if framecolor != defaultframecolor or backcolor != defaultbackcolor:
1273 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1274 document.body[einset : einset] = put_cmd_in_ert("}")
1275 # determine the box type
1276 isBox = find_token(document.body, "\\begin_inset Box Boxed", binset)
1277 # now output the box commands
1278 if (framecolor != defaultframecolor and isBox == binset) or (backcolor != defaultbackcolor and isBox == binset):
1279 document.body[i - 14 : i - 14] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1280 # in the case we must also change the box type because the ERT code adds a frame
1281 document.body[i - 4] = "\\begin_inset Box Frameless"
1282 # if has_inner_box 0 we must set it and use_makebox to 1
1283 ibox = find_token(document.body, "has_inner_box", i - 4)
1284 if ibox == -1 or ibox != i - 1:
1285 document.warning("Malformed LyX document: Can't find has_inner_box statement!")
1287 # read out the value
1288 innerbox = document.body[ibox][-1:];
1290 document.body[ibox] = "has_inner_box 1"
1291 document.body[ibox + 3] = "use_makebox 1"
1292 if backcolor != defaultbackcolor and isBox != binset:
1293 document.body[i - 14 : i - 14] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
1296 def revert_mathmulticol(document):
1297 " Convert formulas to ERT if they contain multicolumns "
1301 i = find_token(document.body, '\\begin_inset Formula', i)
1304 j = find_end_of_inset(document.body, i)
1306 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1309 lines = document.body[i:j]
1310 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1311 code = "\n".join(lines)
1316 n = code.find("\\multicolumn", k)
1317 # no need to convert degenerated multicolumn cells,
1318 # they work in old LyX versions as "math ERT"
1319 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1320 ert = put_cmd_in_ert(code)
1321 document.body[i:j+1] = ert
1327 i = find_end_of_inset(document.body, i)
1332 def revert_jss(document):
1333 " Reverts JSS In_Preamble commands to ERT in preamble "
1335 if document.textclass != "jss":
1344 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1345 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1348 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1350 endh = find_end_of_inset(document.body, h)
1351 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1352 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1356 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1358 endm = find_end_of_inset(document.body, m)
1359 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1360 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1364 j = find_token(document.body, "\\begin_inset Flex Code", j)
1366 # assure that we are not in a Code Chunk inset
1367 if document.body[j][-1] == "e":
1368 endj = find_end_of_inset(document.body, j)
1369 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1370 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1376 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1378 endk = find_end_of_inset(document.body, k)
1379 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1380 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1384 n = find_token(document.body, "\\begin_inset Flex URL", n)
1386 endn = find_end_of_inset(document.body, n)
1387 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1388 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1390 # now revert the In_Preamble layouts
1392 i = find_token(document.body, "\\begin_layout Title", 0)
1395 j = find_end_of_layout(document.body, i)
1397 document.warning("Malformed LyX document: Can't find end of Title layout")
1400 content = lyx2latex(document, document.body[i:j + 1])
1401 add_to_preamble(document, ["\\title{" + content + "}"])
1402 del document.body[i:j + 1]
1404 i = find_token(document.body, "\\begin_layout Author", 0)
1407 j = find_end_of_layout(document.body, i)
1409 document.warning("Malformed LyX document: Can't find end of Author layout")
1412 content = lyx2latex(document, document.body[i:j + 1])
1413 add_to_preamble(document, ["\\author{" + content + "}"])
1414 del document.body[i:j + 1]
1416 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1419 j = find_end_of_layout(document.body, i)
1421 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1424 content = lyx2latex(document, document.body[i:j + 1])
1425 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1426 del document.body[i:j + 1]
1428 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1431 j = find_end_of_layout(document.body, i)
1433 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1436 content = lyx2latex(document, document.body[i:j + 1])
1437 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1438 del document.body[i:j + 1]
1440 i = find_token(document.body, "\\begin_layout Short Title", 0)
1443 j = find_end_of_layout(document.body, i)
1445 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1448 content = lyx2latex(document, document.body[i:j + 1])
1449 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1450 del document.body[i:j + 1]
1452 i = find_token(document.body, "\\begin_layout Abstract", 0)
1455 j = find_end_of_layout(document.body, i)
1457 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1460 content = lyx2latex(document, document.body[i:j + 1])
1461 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1462 del document.body[i:j + 1]
1464 i = find_token(document.body, "\\begin_layout Keywords", 0)
1467 j = find_end_of_layout(document.body, i)
1469 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1472 content = lyx2latex(document, document.body[i:j + 1])
1473 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1474 del document.body[i:j + 1]
1476 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1479 j = find_end_of_layout(document.body, i)
1481 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1484 content = lyx2latex(document, document.body[i:j + 1])
1485 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1486 del document.body[i:j + 1]
1488 i = find_token(document.body, "\\begin_layout Address", 0)
1491 j = find_end_of_layout(document.body, i)
1493 document.warning("Malformed LyX document: Can't find end of Address layout")
1496 content = lyx2latex(document, document.body[i:j + 1])
1497 add_to_preamble(document, ["\\Address{" + content + "}"])
1498 del document.body[i:j + 1]
1499 # finally handle the code layouts
1504 while m != -1 or j != -1 or h != -1 or k != -1:
1507 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1509 endh = find_end_of_inset(document.body, h)
1510 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1511 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1512 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1516 j = find_token(document.body, "\\begin_layout Code Input", j)
1518 endj = find_end_of_layout(document.body, j)
1519 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1520 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1521 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1522 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1523 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1527 k = find_token(document.body, "\\begin_layout Code Output", k)
1529 endk = find_end_of_layout(document.body, k)
1530 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1531 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1532 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1533 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1534 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1538 m = find_token(document.body, "\\begin_layout Code", m)
1540 endm = find_end_of_layout(document.body, m)
1541 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1542 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1543 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1544 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1545 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1549 def convert_subref(document):
1550 " converts sub: ref prefixes to subref: "
1553 rx = re.compile(r'^name \"sub:(.+)$')
1556 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1559 j = find_end_of_inset(document.body, i)
1561 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1565 for p in range(i, j):
1566 m = rx.match(document.body[p])
1569 document.body[p] = "name \"subsec:" + label
1573 rx = re.compile(r'^reference \"sub:(.+)$')
1576 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1579 j = find_end_of_inset(document.body, i)
1581 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1585 for p in range(i, j):
1586 m = rx.match(document.body[p])
1589 document.body[p] = "reference \"subsec:" + label
1595 def revert_subref(document):
1596 " reverts subref: ref prefixes to sub: "
1599 rx = re.compile(r'^name \"subsec:(.+)$')
1602 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1605 j = find_end_of_inset(document.body, i)
1607 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1611 for p in range(i, j):
1612 m = rx.match(document.body[p])
1615 document.body[p] = "name \"sub:" + label
1620 rx = re.compile(r'^reference \"subsec:(.+)$')
1623 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1626 j = find_end_of_inset(document.body, i)
1628 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1632 for p in range(i, j):
1633 m = rx.match(document.body[p])
1636 document.body[p] = "reference \"sub:" + label
1641 def convert_nounzip(document):
1642 " remove the noUnzip parameter of graphics insets "
1644 rx = re.compile(r'\s*noUnzip\s*$')
1647 i = find_token(document.body, "\\begin_inset Graphics", i)
1650 j = find_end_of_inset(document.body, i)
1652 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1656 k = find_re(document.body, rx, i, j)
1658 del document.body[k]
1663 def convert_revert_external_bbox(document, forward):
1664 " add units to bounding box of external insets "
1666 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1669 i = find_token(document.body, "\\begin_inset External", i)
1672 j = find_end_of_inset(document.body, i)
1674 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1677 k = find_re(document.body, rx, i, j)
1681 tokens = document.body[k].split()
1683 for t in range(1, 5):
1686 for t in range(1, 5):
1687 tokens[t] = length_in_bp(tokens[t])
1688 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1689 tokens[3] + " " + tokens[4]
1693 def convert_external_bbox(document):
1694 convert_revert_external_bbox(document, True)
1697 def revert_external_bbox(document):
1698 convert_revert_external_bbox(document, False)
1701 def revert_tcolorbox_1(document):
1702 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1705 i = find_token(document.header, "tcolorbox", i)
1711 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1714 flexEnd = find_end_of_inset(document.body, flex)
1715 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1716 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1717 flexEnd = find_end_of_inset(document.body, flex)
1719 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1721 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1722 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1726 def revert_tcolorbox_2(document):
1727 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1730 i = find_token(document.header, "tcolorbox", i)
1736 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1739 flexEnd = find_end_of_inset(document.body, flex)
1740 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1741 flexEnd = find_end_of_inset(document.body, flex)
1742 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1743 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1747 def revert_tcolorbox_3(document):
1748 " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1751 i = find_token(document.header, "tcolorbox", i)
1757 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1760 flexEnd = find_end_of_inset(document.body, flex)
1761 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1762 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1763 flexEnd = find_end_of_inset(document.body, flex)
1764 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1765 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1769 def revert_tcolorbox_4(document):
1770 " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1773 i = find_token(document.header, "tcolorbox", i)
1779 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1782 flexEnd = find_end_of_inset(document.body, flex)
1783 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1784 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1785 flexEnd = find_end_of_inset(document.body, flex)
1786 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1787 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1791 def revert_tcolorbox_5(document):
1792 " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1795 i = find_token(document.header, "tcolorbox", i)
1801 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1804 flexEnd = find_end_of_inset(document.body, flex)
1805 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1806 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1807 flexEnd = find_end_of_inset(document.body, flex)
1808 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1809 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1813 def revert_tcolorbox_6(document):
1814 " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1817 i = find_token(document.header, "tcolorbox", i)
1823 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1826 flexEnd = find_end_of_inset(document.body, flex)
1827 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1828 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1829 flexEnd = find_end_of_inset(document.body, flex)
1830 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1831 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1835 def revert_tcolorbox_7(document):
1836 " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1839 i = find_token(document.header, "tcolorbox", i)
1845 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1848 flexEnd = find_end_of_inset(document.body, flex)
1849 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1850 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1851 flexEnd = find_end_of_inset(document.body, flex)
1852 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1853 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1857 def revert_tcolorbox_8(document):
1858 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1864 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1866 j = find_end_of_layout(document.body, i)
1867 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1868 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1869 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1870 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1872 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1874 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1875 k = find_end_of_inset(document.body, j)
1876 k = find_token(document.body, "\\end_inset", k + 1)
1877 k = find_token(document.body, "\\end_inset", k + 1)
1879 k = find_token(document.body, "\\end_inset", k + 1)
1880 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1881 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1882 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1888 def revert_moderncv_1(document):
1889 " Reverts the new inset of moderncv to TeX-code in preamble "
1891 if document.textclass != "moderncv":
1897 # at first revert the new styles
1899 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1902 j = find_end_of_layout(document.body, i)
1904 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1907 content = lyx2latex(document, document.body[i:j + 1])
1908 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1909 del document.body[i:j + 1]
1911 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1914 j = find_end_of_layout(document.body, i)
1916 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1919 content = lyx2latex(document, document.body[i:j + 1])
1920 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1921 del document.body[i:j + 1]
1922 # now change the new styles to the obsolete ones
1924 i = find_token(document.body, "\\begin_layout Name", 0)
1927 j = find_end_of_layout(document.body, i)
1929 document.warning("Malformed LyX document: Can't find end of Name layout")
1932 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1933 if lineArg > j and j != 0:
1936 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1937 # we have to assure that no other inset is in the Argument
1938 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1939 endInset = find_token(document.body, "\\end_inset", beginPlain)
1942 while beginInset < endInset and beginInset != -1:
1943 beginInset = find_token(document.body, "\\begin_inset", k)
1944 endInset = find_token(document.body, "\\end_inset", l)
1947 Arg2 = document.body[l + 5 : l + 6]
1949 document.body[i : i + 1]= ["\\begin_layout FirstName"]
1950 # delete the Argument inset
1951 del( document.body[endInset - 2 : endInset + 3])
1952 del( document.body[lineArg : beginPlain + 1])
1953 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1956 def revert_moderncv_2(document):
1957 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1959 if document.textclass != "moderncv":
1966 i = find_token(document.body, "\\begin_layout Phone", i)
1969 j = find_end_of_layout(document.body, i)
1971 document.warning("Malformed LyX document: Can't find end of Phone layout")
1974 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1975 if lineArg > j and j != 0:
1979 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1980 # we have to assure that no other inset is in the Argument
1981 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1982 endInset = find_token(document.body, "\\end_inset", beginPlain)
1985 while beginInset < endInset and beginInset != -1:
1986 beginInset = find_token(document.body, "\\begin_inset", k)
1987 endInset = find_token(document.body, "\\end_inset", l)
1990 Arg = document.body[beginPlain + 1 : beginPlain + 2]
1992 if Arg[0] == "mobile":
1993 document.body[i : i + 1]= ["\\begin_layout Mobile"]
1995 document.body[i : i + 1]= ["\\begin_layout Fax"]
1996 # delete the Argument inset
1997 del(document.body[endInset - 2 : endInset + 1])
1998 del(document.body[lineArg : beginPlain + 3])
2002 def convert_moderncv_phone(document):
2003 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
2005 if document.textclass != "moderncv":
2012 "Mobile" : "mobile",
2016 rx = re.compile(r'^\\begin_layout (\S+)$')
2018 # substitute \fax and \mobile by \phone[fax] and \phone[mobile], respectively
2019 i = find_token(document.body, "\\begin_layout", i)
2023 m = rx.match(document.body[i])
2027 if val not in list(phone_dict.keys()):
2030 j = find_end_of_layout(document.body, i)
2032 document.warning("Malformed LyX document: Can't find end of Mobile layout")
2036 document.body[i : i + 1] = ["\\begin_layout Phone", "\\begin_inset Argument 1", "status open", "",
2037 "\\begin_layout Plain Layout", phone_dict[val], "\\end_layout", "",
2041 def convert_moderncv_name(document):
2042 " Convert the FirstName and LastName layout of moderncv to the general Name layout "
2044 if document.textclass != "moderncv":
2047 fnb = 0 # Begin of FirstName inset
2048 fne = 0 # End of FirstName inset
2049 lnb = 0 # Begin of LastName (FamilyName) inset
2050 lne = 0 # End of LastName (FamilyName) inset
2051 nb = 0 # Begin of substituting Name inset
2052 ne = 0 # End of substituting Name inset
2053 FirstName = [] # FirstName content
2054 FamilyName = [] # LastName content
2058 fnb = find_token(document.body, "\\begin_layout FirstName", fnb)
2060 fne = find_end_of_layout(document.body, fnb)
2062 document.warning("Malformed LyX document: Can't find end of FirstName layout")
2064 FirstName = document.body[fnb + 1 : fne]
2066 lnb = find_token(document.body, "\\begin_layout FamilyName", lnb)
2068 lne = find_end_of_layout(document.body, lnb)
2070 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
2072 FamilyName = document.body[lnb + 1 : lne]
2073 # Determine the region for the substituting Name layout
2074 if fnb == -1 and lnb == -1: # Neither FirstName nor FamilyName exists -> Do nothing
2076 elif fnb == -1: # Only FamilyName exists -> New Name insets replaces that
2079 elif lnb == -1: # Only FirstName exists -> New Name insets replaces that
2082 elif fne > lne: # FirstName position before FamilyName -> New Name insets spans
2083 nb = lnb # from FamilyName begin
2084 ne = fne # to FirstName end
2085 else: # FirstName position before FamilyName -> New Name insets spans
2086 nb = fnb # from FirstName begin
2087 ne = lne # to FamilyName end
2089 # Insert the substituting layout now. If FirstName exists, use an otpional argument.
2091 document.body[nb : ne + 1] = ["\\begin_layout Name"] + FamilyName + ["\\end_layout", ""]
2093 document.body[nb : ne + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2094 "\\begin_layout Plain Layout"] + FirstName + ["\\end_layout", "",
2095 "\\end_inset", ""] + FamilyName + ["\\end_layout", ""]
2098 def revert_achemso(document):
2099 " Reverts the flex inset Latin to TeX code "
2101 if document.textclass != "achemso":
2106 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2108 j = find_end_of_inset(document.body, i)
2112 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2113 endPlain = find_end_of_layout(document.body, beginPlain)
2114 content = lyx2latex(document, document.body[beginPlain : endPlain])
2115 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2117 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2122 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2123 "\\font_sf_scale", "\\font_tt_scale"]
2124 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2125 fontquotes = [True, True, True, True, False, False]
2127 def convert_fontsettings(document):
2128 " Duplicate font settings "
2130 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2132 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2133 use_non_tex_fonts = "false"
2135 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2137 for f in fontsettings:
2138 i = find_token(document.header, f + " ", 0)
2140 document.warning("Malformed LyX document: No " + f + "!")
2142 # note that with i = -1, this will insert at the end
2144 value = fontdefaults[j]
2146 value = document.header[i][len(f):].strip()
2148 if use_non_tex_fonts == "true":
2149 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2151 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2153 if use_non_tex_fonts == "true":
2154 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2156 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2160 def revert_fontsettings(document):
2161 " Merge font settings "
2163 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2165 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2166 use_non_tex_fonts = "false"
2168 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2170 for f in fontsettings:
2171 i = find_token(document.header, f + " ", 0)
2173 document.warning("Malformed LyX document: No " + f + "!")
2176 line = get_value(document.header, f, i)
2179 q2 = line.find('"', q1+1)
2180 q3 = line.find('"', q2+1)
2181 q4 = line.find('"', q3+1)
2182 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2183 document.warning("Malformed LyX document: Missing quotes!")
2186 if use_non_tex_fonts == "true":
2187 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2189 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2191 if use_non_tex_fonts == "true":
2192 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2194 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2198 def revert_solution(document):
2199 " Reverts the solution environment of the theorem module to TeX code "
2201 # Do we use one of the modules that provides Solution?
2203 mods = document.get_module_list()
2205 if mod == "theorems-std" or mod == "theorems-bytype" \
2206 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2216 i = find_token(document.body, "\\begin_layout Solution", i)
2220 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2221 if is_starred == True:
2223 LyXName = "Solution*"
2224 theoremName = "newtheorem*"
2227 LyXName = "Solution"
2228 theoremName = "newtheorem"
2230 j = find_end_of_layout(document.body, i)
2232 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2236 # if this is not a consecutive env, add start command
2239 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2241 # has this a consecutive theorem of same type?
2242 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2244 # if this is not followed by a consecutive env, add end command
2246 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2248 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2250 add_to_preamble(document, "\\theoremstyle{definition}")
2251 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2252 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2253 (theoremName, LaTeXName))
2254 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2255 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2256 (theoremName, LaTeXName))
2258 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2262 def revert_verbatim_star(document):
2263 from lyx_2_1 import revert_verbatim
2264 revert_verbatim(document, True)
2267 def convert_save_props(document):
2268 " Add save_transient_properties parameter. "
2269 i = find_token(document.header, '\\begin_header', 0)
2271 document.warning("Malformed lyx document: Missing '\\begin_header'.")
2273 document.header.insert(i + 1, '\\save_transient_properties true')
2276 def revert_save_props(document):
2277 " Remove save_transient_properties parameter. "
2278 i = find_token(document.header, "\\save_transient_properties", 0)
2281 del document.header[i]
2284 def convert_info_tabular_feature(document):
2286 return arg.replace("inset-modify tabular", "tabular-feature")
2287 convert_info_insets(document, "shortcut(s)?|icon", f)
2290 def revert_info_tabular_feature(document):
2292 return arg.replace("tabular-feature", "inset-modify tabular")
2293 convert_info_insets(document, "shortcut(s)?|icon", f)
2300 supported_versions = ["2.2.0", "2.2"]
2302 [475, [convert_separator]],
2303 # nothing to do for 476: We consider it a bug that older versions
2304 # did not load amsmath automatically for these commands, and do not
2305 # want to hardcode amsmath off.
2311 [481, [convert_dashes]],
2312 [482, [convert_phrases]],
2313 [483, [convert_specialchar]],
2318 [488, [convert_newgloss]],
2319 [489, [convert_BoxFeatures]],
2320 [490, [convert_origin]],
2322 [492, [convert_colorbox]],
2325 [495, [convert_subref]],
2326 [496, [convert_nounzip]],
2327 [497, [convert_external_bbox]],
2329 [499, [convert_moderncv_phone, convert_moderncv_name]],
2331 [501, [convert_fontsettings]],
2334 [504, [convert_save_props]],
2336 [506, [convert_info_tabular_feature]],
2337 [507, [convert_longtable_label]],
2338 [508, [convert_parbreak]]
2342 [507, [revert_parbreak]],
2343 [506, [revert_longtable_label]],
2344 [505, [revert_info_tabular_feature]],
2346 [503, [revert_save_props]],
2347 [502, [revert_verbatim_star]],
2348 [501, [revert_solution]],
2349 [500, [revert_fontsettings]],
2350 [499, [revert_achemso]],
2351 [498, [revert_moderncv_1, revert_moderncv_2]],
2352 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2353 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2354 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2355 [496, [revert_external_bbox]],
2356 [495, []], # nothing to do since the noUnzip parameter was optional
2357 [494, [revert_subref]],
2358 [493, [revert_jss]],
2359 [492, [revert_mathmulticol]],
2360 [491, [revert_colorbox]],
2361 [490, [revert_textcolor]],
2362 [489, [revert_origin]],
2363 [488, [revert_BoxFeatures]],
2364 [487, [revert_newgloss, revert_glossgroup]],
2365 [486, [revert_forest]],
2366 [485, [revert_ex_itemargs]],
2367 [484, [revert_sigplan_doi]],
2368 [483, [revert_georgian]],
2369 [482, [revert_specialchar]],
2370 [481, [revert_phrases]],
2371 [480, [revert_dashes]],
2372 [479, [revert_question_env]],
2373 [478, [revert_beamer_lemma]],
2374 [477, [revert_xarrow]],
2375 [476, [revert_swissgerman]],
2376 [475, [revert_smash]],
2377 [474, [revert_separator]]
2381 if __name__ == "__main__":