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 ---"
663 while i < len(document.body):
664 words = document.body[i].split()
665 if len(words) > 1 and words[0] == "\\begin_inset" and \
666 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
668 j = find_end_of_inset(document.body, i)
670 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
676 if document.body[i].find("\\twohyphens") >= 0:
677 document.body[i] = document.body[i].replace("\\twohyphens", "--")
679 if document.body[i].find("\\threehyphens") >= 0:
680 document.body[i] = document.body[i].replace("\\threehyphens", "---")
682 if replaced and i+1 < len(document.body) and \
683 (document.body[i+1].find("\\") != 0 or \
684 document.body[i+1].find("\\twohyphens") == 0 or
685 document.body[i+1].find("\\threehyphens") == 0) and \
686 len(document.body[i]) + len(document.body[i+1]) <= 80:
687 document.body[i] = document.body[i] + document.body[i+1]
688 document.body[i+1:i+2] = []
693 # order is important for the last three!
694 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
696 def is_part_of_converted_phrase(line, j, phrase):
697 "is phrase part of an already converted phrase?"
699 converted = "\\SpecialCharNoPassThru \\" + p
700 pos = j + len(phrase) - len(converted)
702 if line[pos:pos+len(converted)] == converted:
707 def convert_phrases(document):
708 "convert special phrases from plain text to \\SpecialCharNoPassThru"
710 if document.backend != "latex":
713 for phrase in phrases:
715 while i < len(document.body):
716 words = document.body[i].split()
717 if len(words) > 1 and words[0] == "\\begin_inset" and \
718 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
719 # must not replace anything in insets that store LaTeX contents in .lyx files
720 # (math and command insets withut overridden read() and write() methods
721 j = find_end_of_inset(document.body, i)
723 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
728 if document.body[i].find("\\") == 0:
731 j = document.body[i].find(phrase)
735 if not is_part_of_converted_phrase(document.body[i], j, phrase):
736 front = document.body[i][:j]
737 back = document.body[i][j+len(phrase):]
739 document.body.insert(i+1, back)
740 # We cannot use SpecialChar since we do not know whether we are outside passThru
741 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
745 def revert_phrases(document):
746 "convert special phrases to plain text"
749 while i < len(document.body):
750 words = document.body[i].split()
751 if len(words) > 1 and words[0] == "\\begin_inset" and \
752 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
753 # see convert_phrases
754 j = find_end_of_inset(document.body, i)
756 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
762 for phrase in phrases:
763 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
764 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
765 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
767 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
768 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
770 if replaced and i+1 < len(document.body) and \
771 (document.body[i+1].find("\\") != 0 or \
772 document.body[i+1].find("\\SpecialChar") == 0) and \
773 len(document.body[i]) + len(document.body[i+1]) <= 80:
774 document.body[i] = document.body[i] + document.body[i+1]
775 document.body[i+1:i+2] = []
780 def convert_specialchar_internal(document, forward):
781 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
782 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
783 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
784 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
785 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
786 "\\LaTeX":"LaTeX" # must be after LaTeX2e
790 while i < len(document.body):
791 words = document.body[i].split()
792 if len(words) > 1 and words[0] == "\\begin_inset" and \
793 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
794 # see convert_phrases
795 j = find_end_of_inset(document.body, i)
797 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
802 for key, value in specialchars.items():
804 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
805 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
807 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
808 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
812 def convert_specialchar(document):
813 "convert special characters to new syntax"
814 convert_specialchar_internal(document, True)
817 def revert_specialchar(document):
818 "convert special characters to old syntax"
819 convert_specialchar_internal(document, False)
822 def revert_georgian(document):
823 "Set the document language to English but assure Georgian output"
825 if document.language == "georgian":
826 document.language = "english"
827 i = find_token(document.header, "\\language georgian", 0)
829 document.header[i] = "\\language english"
830 j = find_token(document.header, "\\language_package default", 0)
832 document.header[j] = "\\language_package babel"
833 k = find_token(document.header, "\\options", 0)
835 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
837 l = find_token(document.header, "\\use_default_options", 0)
838 document.header.insert(l + 1, "\\options georgian")
841 def revert_sigplan_doi(document):
842 " Reverts sigplanconf DOI layout to ERT "
844 if document.textclass != "sigplanconf":
849 i = find_token(document.body, "\\begin_layout DOI", i)
852 j = find_end_of_layout(document.body, i)
854 document.warning("Malformed LyX document: Can't find end of DOI layout")
858 content = lyx2latex(document, document.body[i:j + 1])
859 add_to_preamble(document, ["\\doi{" + content + "}"])
860 del document.body[i:j + 1]
864 def revert_ex_itemargs(document):
865 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
867 if not "linguistics" in document.get_module_list():
871 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
873 i = find_token(document.body, "\\begin_inset Argument item:", i)
876 j = find_end_of_inset(document.body, i)
877 # Find containing paragraph layout
878 parent = get_containing_layout(document.body, i)
880 document.warning("Malformed LyX document: Can't find parent paragraph layout")
884 layoutname = parent[0]
885 if layoutname in example_layouts:
886 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
887 endPlain = find_end_of_layout(document.body, beginPlain)
888 content = document.body[beginPlain + 1 : endPlain]
889 del document.body[i:j+1]
890 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
891 document.body[parbeg : parbeg] = subst
895 def revert_forest(document):
896 " Reverts the forest environment (Linguistics module) to TeX-code "
898 if not "linguistics" in document.get_module_list():
903 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
906 j = find_end_of_inset(document.body, i)
908 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
912 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
913 endPlain = find_end_of_layout(document.body, beginPlain)
914 content = lyx2latex(document, document.body[beginPlain : endPlain])
916 add_to_preamble(document, ["\\usepackage{forest}"])
918 document.body[i:j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
922 def revert_glossgroup(document):
923 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
925 if not "linguistics" in document.get_module_list():
930 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
933 j = find_end_of_inset(document.body, i)
935 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
939 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
940 endPlain = find_end_of_layout(document.body, beginPlain)
941 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
943 document.body[i:j + 1] = ["{", "", content, "", "}"]
947 def revert_newgloss(document):
948 " Reverts the new Glosse insets (Linguistics module) to the old format "
950 if not "linguistics" in document.get_module_list():
953 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
954 for glosse in glosses:
957 i = find_token(document.body, glosse, i)
960 j = find_end_of_inset(document.body, i)
962 document.warning("Malformed LyX document: Can't find end of Glosse inset")
966 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
967 endarg = find_end_of_inset(document.body, arg)
970 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
971 if argbeginPlain == -1:
972 document.warning("Malformed LyX document: Can't find arg plain Layout")
975 argendPlain = find_end_of_inset(document.body, argbeginPlain)
976 argcontent = lyx2verbatim(document, document.body[argbeginPlain : argendPlain - 2])
978 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
979 argcontent, "\\end_layout"]
981 # remove Arg insets and paragraph, if it only contains this inset
982 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
983 del document.body[arg - 1 : endarg + 4]
985 del document.body[arg : endarg + 1]
987 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
988 endPlain = find_end_of_layout(document.body, beginPlain)
989 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
991 document.body[beginPlain + 1:endPlain] = [content]
994 # Dissolve ERT insets
995 for glosse in glosses:
998 i = find_token(document.body, glosse, i)
1001 j = find_end_of_inset(document.body, i)
1003 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1007 ert = find_token(document.body, "\\begin_inset ERT", i, j)
1010 ertend = find_end_of_inset(document.body, ert)
1012 document.warning("Malformed LyX document: Can't find end of ERT inset")
1015 ertcontent = get_ert(document.body, ert, True)
1016 document.body[ert : ertend + 1] = [ertcontent]
1020 def convert_newgloss(document):
1021 " Converts Glosse insets (Linguistics module) to the new format "
1023 if not "linguistics" in document.get_module_list():
1026 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1027 for glosse in glosses:
1030 i = find_token(document.body, glosse, i)
1033 j = find_end_of_inset(document.body, i)
1035 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1042 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
1043 if beginPlain == -1:
1045 endPlain = find_end_of_layout(document.body, beginPlain)
1047 document.warning("Malformed LyX document: Can't find end of Glosse layout")
1051 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
1052 if glt != -1 and document.body[glt + 1].startswith("glt"):
1053 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1054 argcontent = document.body[glt + 1 : endPlain]
1055 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
1056 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
1057 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
1058 "\\end_layout", "", "\\end_inset"]
1060 content = document.body[beginPlain + 1 : endPlain]
1061 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
1062 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
1064 endPlain = find_end_of_layout(document.body, beginPlain)
1066 j = find_end_of_inset(document.body, i)
1071 def convert_BoxFeatures(document):
1072 " adds new box features "
1076 i = find_token(document.body, "height_special", i)
1079 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1083 def revert_BoxFeatures(document):
1084 " outputs new box features as TeX code "
1088 defaultThick = "0.4pt"
1089 defaultShadow = "4pt"
1091 i = find_token(document.body, "thickness", i)
1094 binset = find_token(document.body, "\\begin_inset Box", i - 11)
1096 return # then "thickness" is is just a word in the text
1097 einset = find_end_of_inset(document.body, binset)
1099 document.warning("Malformed LyX document: Can't find end of box inset!")
1101 # read out the values
1102 beg = document.body[i].find('"');
1103 end = document.body[i].rfind('"');
1104 thickness = document.body[i][beg+1:end];
1105 beg = document.body[i+1].find('"');
1106 end = document.body[i+1].rfind('"');
1107 separation = document.body[i+1][beg+1:end];
1108 beg = document.body[i+2].find('"');
1109 end = document.body[i+2].rfind('"');
1110 shadowsize = document.body[i+2][beg+1:end];
1111 # delete the specification
1112 del document.body[i:i+3]
1114 # first output the closing brace
1115 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1116 document.body[einset -1 : einset - 1] = put_cmd_in_ert("}")
1117 # we have now the problem that if there is already \(f)colorbox in ERT around the inset
1118 # the ERT from this routine must be around it
1119 regexp = re.compile(r'^.*colorbox{.*$')
1120 pos = find_re(document.body, regexp, binset - 4)
1121 if pos != -1 and pos == binset - 4:
1125 # now output the lengths
1126 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1127 document.body[pos : pos] = put_cmd_in_ert("{")
1128 if thickness != defaultThick:
1129 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness]
1130 if separation != defaultSep and thickness == defaultThick:
1131 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation]
1132 if separation != defaultSep and thickness != defaultThick:
1133 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1134 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1135 document.body[pos + 5 : pos +6] = ["{\\backslash shadowsize " + shadowsize]
1136 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1137 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1138 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1139 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1140 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1141 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1144 def convert_origin(document):
1145 " Insert the origin tag "
1147 i = find_token(document.header, "\\textclass ", 0)
1149 document.warning("Malformed LyX document: No \\textclass!!")
1151 if document.dir == u'':
1155 if document.systemlyxdir and document.systemlyxdir != u'':
1157 if os.path.isabs(document.dir):
1158 absdir = os.path.normpath(document.dir)
1160 absdir = os.path.normpath(os.path.abspath(document.dir))
1161 if os.path.isabs(document.systemlyxdir):
1162 abssys = os.path.normpath(document.systemlyxdir)
1164 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1165 relpath = os.path.relpath(absdir, abssys)
1166 if relpath.find(u'..') == 0:
1171 origin = document.dir.replace(u'\\', u'/') + u'/'
1173 origin = os.path.join(u"/systemlyxdir", relpath).replace(u'\\', u'/') + u'/'
1174 document.header[i:i] = ["\\origin " + origin]
1177 def revert_origin(document):
1178 " Remove the origin tag "
1180 i = find_token(document.header, "\\origin ", 0)
1182 document.warning("Malformed LyX document: No \\origin!!")
1184 del document.header[i]
1187 color_names = ["brown", "darkgray", "gray", \
1188 "lightgray", "lime", "olive", "orange", \
1189 "pink", "purple", "teal", "violet"]
1191 def revert_textcolor(document):
1192 " revert new \\textcolor colors to TeX code "
1198 i = find_token(document.body, "\\color ", i)
1202 for color in list(color_names):
1203 if document.body[i] == "\\color " + color:
1204 # register that xcolor must be loaded in the preamble
1207 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1208 # find the next \\color and/or the next \\end_layout
1209 j = find_token(document.body, "\\color", i + 1)
1210 k = find_token(document.body, "\\end_layout", i + 1)
1211 if j == -1 and k != -1:
1214 # first output the closing brace
1216 document.body[k: k] = put_cmd_in_ert("}")
1218 document.body[j: j] = put_cmd_in_ert("}")
1219 # now output the \textcolor command
1220 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1224 def convert_colorbox(document):
1225 " adds color settings for boxes "
1229 i = find_token(document.body, "shadowsize", i)
1232 document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1236 def revert_colorbox(document):
1237 " outputs color settings for boxes as TeX code "
1240 defaultframecolor = "black"
1241 defaultbackcolor = "none"
1243 i = find_token(document.body, "framecolor", i)
1246 binset = find_token(document.body, "\\begin_inset Box", i - 14)
1249 einset = find_end_of_inset(document.body, binset)
1251 document.warning("Malformed LyX document: Can't find end of box inset!")
1253 # read out the values
1254 beg = document.body[i].find('"');
1255 end = document.body[i].rfind('"');
1256 framecolor = document.body[i][beg+1:end];
1257 beg = document.body[i + 1].find('"');
1258 end = document.body[i + 1].rfind('"');
1259 backcolor = document.body[i+1][beg+1:end];
1260 # delete the specification
1261 del document.body[i:i + 2]
1263 # first output the closing brace
1264 if framecolor != defaultframecolor or backcolor != defaultbackcolor:
1265 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1266 document.body[einset : einset] = put_cmd_in_ert("}")
1267 # determine the box type
1268 isBox = find_token(document.body, "\\begin_inset Box Boxed", binset)
1269 # now output the box commands
1270 if (framecolor != defaultframecolor and isBox == binset) or (backcolor != defaultbackcolor and isBox == binset):
1271 document.body[i - 14 : i - 14] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1272 # in the case we must also change the box type because the ERT code adds a frame
1273 document.body[i - 4] = "\\begin_inset Box Frameless"
1274 # if has_inner_box 0 we must set it and use_makebox to 1
1275 ibox = find_token(document.body, "has_inner_box", i - 4)
1276 if ibox == -1 or ibox != i - 1:
1277 document.warning("Malformed LyX document: Can't find has_inner_box statement!")
1279 # read out the value
1280 innerbox = document.body[ibox][-1:];
1282 document.body[ibox] = "has_inner_box 1"
1283 document.body[ibox + 3] = "use_makebox 1"
1284 if backcolor != defaultbackcolor and isBox != binset:
1285 document.body[i - 14 : i - 14] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
1288 def revert_mathmulticol(document):
1289 " Convert formulas to ERT if they contain multicolumns "
1293 i = find_token(document.body, '\\begin_inset Formula', i)
1296 j = find_end_of_inset(document.body, i)
1298 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1301 lines = document.body[i:j]
1302 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1303 code = "\n".join(lines)
1308 n = code.find("\\multicolumn", k)
1309 # no need to convert degenerated multicolumn cells,
1310 # they work in old LyX versions as "math ERT"
1311 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1312 ert = put_cmd_in_ert(code)
1313 document.body[i:j+1] = ert
1319 i = find_end_of_inset(document.body, i)
1324 def revert_jss(document):
1325 " Reverts JSS In_Preamble commands to ERT in preamble "
1327 if document.textclass != "jss":
1336 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1337 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1340 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1342 endh = find_end_of_inset(document.body, h)
1343 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1344 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1348 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1350 endm = find_end_of_inset(document.body, m)
1351 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1352 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1356 j = find_token(document.body, "\\begin_inset Flex Code", j)
1358 # assure that we are not in a Code Chunk inset
1359 if document.body[j][-1] == "e":
1360 endj = find_end_of_inset(document.body, j)
1361 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1362 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1368 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1370 endk = find_end_of_inset(document.body, k)
1371 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1372 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1376 n = find_token(document.body, "\\begin_inset Flex URL", n)
1378 endn = find_end_of_inset(document.body, n)
1379 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1380 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1382 # now revert the In_Preamble layouts
1384 i = find_token(document.body, "\\begin_layout Title", 0)
1387 j = find_end_of_layout(document.body, i)
1389 document.warning("Malformed LyX document: Can't find end of Title layout")
1392 content = lyx2latex(document, document.body[i:j + 1])
1393 add_to_preamble(document, ["\\title{" + content + "}"])
1394 del document.body[i:j + 1]
1396 i = find_token(document.body, "\\begin_layout Author", 0)
1399 j = find_end_of_layout(document.body, i)
1401 document.warning("Malformed LyX document: Can't find end of Author layout")
1404 content = lyx2latex(document, document.body[i:j + 1])
1405 add_to_preamble(document, ["\\author{" + content + "}"])
1406 del document.body[i:j + 1]
1408 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1411 j = find_end_of_layout(document.body, i)
1413 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1416 content = lyx2latex(document, document.body[i:j + 1])
1417 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1418 del document.body[i:j + 1]
1420 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1423 j = find_end_of_layout(document.body, i)
1425 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1428 content = lyx2latex(document, document.body[i:j + 1])
1429 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1430 del document.body[i:j + 1]
1432 i = find_token(document.body, "\\begin_layout Short Title", 0)
1435 j = find_end_of_layout(document.body, i)
1437 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1440 content = lyx2latex(document, document.body[i:j + 1])
1441 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1442 del document.body[i:j + 1]
1444 i = find_token(document.body, "\\begin_layout Abstract", 0)
1447 j = find_end_of_layout(document.body, i)
1449 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1452 content = lyx2latex(document, document.body[i:j + 1])
1453 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1454 del document.body[i:j + 1]
1456 i = find_token(document.body, "\\begin_layout Keywords", 0)
1459 j = find_end_of_layout(document.body, i)
1461 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1464 content = lyx2latex(document, document.body[i:j + 1])
1465 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1466 del document.body[i:j + 1]
1468 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1471 j = find_end_of_layout(document.body, i)
1473 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1476 content = lyx2latex(document, document.body[i:j + 1])
1477 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1478 del document.body[i:j + 1]
1480 i = find_token(document.body, "\\begin_layout Address", 0)
1483 j = find_end_of_layout(document.body, i)
1485 document.warning("Malformed LyX document: Can't find end of Address layout")
1488 content = lyx2latex(document, document.body[i:j + 1])
1489 add_to_preamble(document, ["\\Address{" + content + "}"])
1490 del document.body[i:j + 1]
1491 # finally handle the code layouts
1496 while m != -1 or j != -1 or h != -1 or k != -1:
1499 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1501 endh = find_end_of_inset(document.body, h)
1502 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1503 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1504 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1508 j = find_token(document.body, "\\begin_layout Code Input", j)
1510 endj = find_end_of_layout(document.body, j)
1511 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1512 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1513 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1514 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1515 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1519 k = find_token(document.body, "\\begin_layout Code Output", k)
1521 endk = find_end_of_layout(document.body, k)
1522 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1523 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1524 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1525 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1526 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1530 m = find_token(document.body, "\\begin_layout Code", m)
1532 endm = find_end_of_layout(document.body, m)
1533 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1534 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1535 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1536 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1537 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1541 def convert_subref(document):
1542 " converts sub: ref prefixes to subref: "
1545 rx = re.compile(r'^name \"sub:(.+)$')
1548 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1551 j = find_end_of_inset(document.body, i)
1553 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1557 for p in range(i, j):
1558 m = rx.match(document.body[p])
1561 document.body[p] = "name \"subsec:" + label
1565 rx = re.compile(r'^reference \"sub:(.+)$')
1568 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1571 j = find_end_of_inset(document.body, i)
1573 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1577 for p in range(i, j):
1578 m = rx.match(document.body[p])
1581 document.body[p] = "reference \"subsec:" + label
1587 def revert_subref(document):
1588 " reverts subref: ref prefixes to sub: "
1591 rx = re.compile(r'^name \"subsec:(.+)$')
1594 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1597 j = find_end_of_inset(document.body, i)
1599 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1603 for p in range(i, j):
1604 m = rx.match(document.body[p])
1607 document.body[p] = "name \"sub:" + label
1612 rx = re.compile(r'^reference \"subsec:(.+)$')
1615 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1618 j = find_end_of_inset(document.body, i)
1620 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1624 for p in range(i, j):
1625 m = rx.match(document.body[p])
1628 document.body[p] = "reference \"sub:" + label
1633 def convert_nounzip(document):
1634 " remove the noUnzip parameter of graphics insets "
1636 rx = re.compile(r'\s*noUnzip\s*$')
1639 i = find_token(document.body, "\\begin_inset Graphics", i)
1642 j = find_end_of_inset(document.body, i)
1644 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1648 k = find_re(document.body, rx, i, j)
1650 del document.body[k]
1655 def convert_revert_external_bbox(document, forward):
1656 " add units to bounding box of external insets "
1658 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1661 i = find_token(document.body, "\\begin_inset External", i)
1664 j = find_end_of_inset(document.body, i)
1666 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1669 k = find_re(document.body, rx, i, j)
1673 tokens = document.body[k].split()
1675 for t in range(1, 5):
1678 for t in range(1, 5):
1679 tokens[t] = length_in_bp(tokens[t])
1680 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1681 tokens[3] + " " + tokens[4]
1685 def convert_external_bbox(document):
1686 convert_revert_external_bbox(document, True)
1689 def revert_external_bbox(document):
1690 convert_revert_external_bbox(document, False)
1693 def revert_tcolorbox_1(document):
1694 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1697 i = find_token(document.header, "tcolorbox", i)
1703 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1706 flexEnd = find_end_of_inset(document.body, flex)
1707 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1708 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1709 flexEnd = find_end_of_inset(document.body, flex)
1711 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1713 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1714 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1718 def revert_tcolorbox_2(document):
1719 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1722 i = find_token(document.header, "tcolorbox", i)
1728 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1731 flexEnd = find_end_of_inset(document.body, flex)
1732 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1733 flexEnd = find_end_of_inset(document.body, flex)
1734 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1735 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1739 def revert_tcolorbox_3(document):
1740 " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1743 i = find_token(document.header, "tcolorbox", i)
1749 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1752 flexEnd = find_end_of_inset(document.body, flex)
1753 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1754 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1755 flexEnd = find_end_of_inset(document.body, flex)
1756 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1757 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1761 def revert_tcolorbox_4(document):
1762 " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1765 i = find_token(document.header, "tcolorbox", i)
1771 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1774 flexEnd = find_end_of_inset(document.body, flex)
1775 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1776 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1777 flexEnd = find_end_of_inset(document.body, flex)
1778 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1779 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1783 def revert_tcolorbox_5(document):
1784 " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1787 i = find_token(document.header, "tcolorbox", i)
1793 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1796 flexEnd = find_end_of_inset(document.body, flex)
1797 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1798 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1799 flexEnd = find_end_of_inset(document.body, flex)
1800 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1801 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1805 def revert_tcolorbox_6(document):
1806 " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1809 i = find_token(document.header, "tcolorbox", i)
1815 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1818 flexEnd = find_end_of_inset(document.body, flex)
1819 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1820 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1821 flexEnd = find_end_of_inset(document.body, flex)
1822 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1823 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1827 def revert_tcolorbox_7(document):
1828 " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1831 i = find_token(document.header, "tcolorbox", i)
1837 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1840 flexEnd = find_end_of_inset(document.body, flex)
1841 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1842 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1843 flexEnd = find_end_of_inset(document.body, flex)
1844 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1845 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1849 def revert_tcolorbox_8(document):
1850 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1856 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1858 j = find_end_of_layout(document.body, i)
1859 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1860 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1861 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1862 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1864 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1866 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1867 k = find_end_of_inset(document.body, j)
1868 k = find_token(document.body, "\\end_inset", k + 1)
1869 k = find_token(document.body, "\\end_inset", k + 1)
1871 k = find_token(document.body, "\\end_inset", k + 1)
1872 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1873 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1874 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1880 def revert_moderncv_1(document):
1881 " Reverts the new inset of moderncv to TeX-code in preamble "
1883 if document.textclass != "moderncv":
1889 # at first revert the new styles
1891 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1894 j = find_end_of_layout(document.body, i)
1896 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1899 content = lyx2latex(document, document.body[i:j + 1])
1900 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1901 del document.body[i:j + 1]
1903 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1906 j = find_end_of_layout(document.body, i)
1908 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1911 content = lyx2latex(document, document.body[i:j + 1])
1912 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1913 del document.body[i:j + 1]
1914 # now change the new styles to the obsolete ones
1916 i = find_token(document.body, "\\begin_layout Name", 0)
1919 j = find_end_of_layout(document.body, i)
1921 document.warning("Malformed LyX document: Can't find end of Name layout")
1924 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1925 if lineArg > j and j != 0:
1928 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1929 # we have to assure that no other inset is in the Argument
1930 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1931 endInset = find_token(document.body, "\\end_inset", beginPlain)
1934 while beginInset < endInset and beginInset != -1:
1935 beginInset = find_token(document.body, "\\begin_inset", k)
1936 endInset = find_token(document.body, "\\end_inset", l)
1939 Arg2 = document.body[l + 5 : l + 6]
1941 document.body[i : i + 1]= ["\\begin_layout FirstName"]
1942 # delete the Argument inset
1943 del( document.body[endInset - 2 : endInset + 3])
1944 del( document.body[lineArg : beginPlain + 1])
1945 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1948 def revert_moderncv_2(document):
1949 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1951 if document.textclass != "moderncv":
1958 i = find_token(document.body, "\\begin_layout Phone", i)
1961 j = find_end_of_layout(document.body, i)
1963 document.warning("Malformed LyX document: Can't find end of Phone layout")
1966 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1967 if lineArg > j and j != 0:
1971 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1972 # we have to assure that no other inset is in the Argument
1973 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1974 endInset = find_token(document.body, "\\end_inset", beginPlain)
1977 while beginInset < endInset and beginInset != -1:
1978 beginInset = find_token(document.body, "\\begin_inset", k)
1979 endInset = find_token(document.body, "\\end_inset", l)
1982 Arg = document.body[beginPlain + 1 : beginPlain + 2]
1984 if Arg[0] == "mobile":
1985 document.body[i : i + 1]= ["\\begin_layout Mobile"]
1987 document.body[i : i + 1]= ["\\begin_layout Fax"]
1988 # delete the Argument inset
1989 del(document.body[endInset - 2 : endInset + 1])
1990 del(document.body[lineArg : beginPlain + 3])
1994 def convert_moderncv_phone(document):
1995 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
1997 if document.textclass != "moderncv":
2004 "Mobile" : "mobile",
2008 rx = re.compile(r'^\\begin_layout (\S+)$')
2010 # substitute \fax and \mobile by \phone[fax] and \phone[mobile], respectively
2011 i = find_token(document.body, "\\begin_layout", i)
2015 m = rx.match(document.body[i])
2019 if val not in list(phone_dict.keys()):
2022 j = find_end_of_layout(document.body, i)
2024 document.warning("Malformed LyX document: Can't find end of Mobile layout")
2028 document.body[i : i + 1] = ["\\begin_layout Phone", "\\begin_inset Argument 1", "status open", "",
2029 "\\begin_layout Plain Layout", phone_dict[val], "\\end_layout", "",
2033 def convert_moderncv_name(document):
2034 " Convert the FirstName and LastName layout of moderncv to the general Name layout "
2036 if document.textclass != "moderncv":
2039 fnb = 0 # Begin of FirstName inset
2040 fne = 0 # End of FirstName inset
2041 lnb = 0 # Begin of LastName (FamilyName) inset
2042 lne = 0 # End of LastName (FamilyName) inset
2043 nb = 0 # Begin of substituting Name inset
2044 ne = 0 # End of substituting Name inset
2045 FirstName = [] # FirstName content
2046 FamilyName = [] # LastName content
2050 fnb = find_token(document.body, "\\begin_layout FirstName", fnb)
2052 fne = find_end_of_layout(document.body, fnb)
2054 document.warning("Malformed LyX document: Can't find end of FirstName layout")
2056 FirstName = document.body[fnb + 1 : fne]
2058 lnb = find_token(document.body, "\\begin_layout FamilyName", lnb)
2060 lne = find_end_of_layout(document.body, lnb)
2062 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
2064 FamilyName = document.body[lnb + 1 : lne]
2065 # Determine the region for the substituting Name layout
2066 if fnb == -1 and lnb == -1: # Neither FirstName nor FamilyName exists -> Do nothing
2068 elif fnb == -1: # Only FamilyName exists -> New Name insets replaces that
2071 elif lnb == -1: # Only FirstName exists -> New Name insets replaces that
2074 elif fne > lne: # FirstName position before FamilyName -> New Name insets spans
2075 nb = lnb # from FamilyName begin
2076 ne = fne # to FirstName end
2077 else: # FirstName position before FamilyName -> New Name insets spans
2078 nb = fnb # from FirstName begin
2079 ne = lne # to FamilyName end
2081 # Insert the substituting layout now. If FirstName exists, use an otpional argument.
2083 document.body[nb : ne + 1] = ["\\begin_layout Name"] + FamilyName + ["\\end_layout", ""]
2085 document.body[nb : ne + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2086 "\\begin_layout Plain Layout"] + FirstName + ["\\end_layout", "",
2087 "\\end_inset", ""] + FamilyName + ["\\end_layout", ""]
2090 def revert_achemso(document):
2091 " Reverts the flex inset Latin to TeX code "
2093 if document.textclass != "achemso":
2098 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2100 j = find_end_of_inset(document.body, i)
2104 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2105 endPlain = find_end_of_layout(document.body, beginPlain)
2106 content = lyx2latex(document, document.body[beginPlain : endPlain])
2107 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2109 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2114 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2115 "\\font_sf_scale", "\\font_tt_scale"]
2116 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2117 fontquotes = [True, True, True, True, False, False]
2119 def convert_fontsettings(document):
2120 " Duplicate font settings "
2122 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2124 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2125 use_non_tex_fonts = "false"
2127 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2129 for f in fontsettings:
2130 i = find_token(document.header, f + " ", 0)
2132 document.warning("Malformed LyX document: No " + f + "!")
2134 # note that with i = -1, this will insert at the end
2136 value = fontdefaults[j]
2138 value = document.header[i][len(f):].strip()
2140 if use_non_tex_fonts == "true":
2141 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2143 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2145 if use_non_tex_fonts == "true":
2146 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2148 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2152 def revert_fontsettings(document):
2153 " Merge font settings "
2155 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2157 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2158 use_non_tex_fonts = "false"
2160 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2162 for f in fontsettings:
2163 i = find_token(document.header, f + " ", 0)
2165 document.warning("Malformed LyX document: No " + f + "!")
2168 line = get_value(document.header, f, i)
2171 q2 = line.find('"', q1+1)
2172 q3 = line.find('"', q2+1)
2173 q4 = line.find('"', q3+1)
2174 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2175 document.warning("Malformed LyX document: Missing quotes!")
2178 if use_non_tex_fonts == "true":
2179 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2181 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2183 if use_non_tex_fonts == "true":
2184 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2186 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2190 def revert_solution(document):
2191 " Reverts the solution environment of the theorem module to TeX code "
2193 # Do we use one of the modules that provides Solution?
2195 mods = document.get_module_list()
2197 if mod == "theorems-std" or mod == "theorems-bytype" \
2198 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2208 i = find_token(document.body, "\\begin_layout Solution", i)
2212 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2213 if is_starred == True:
2215 LyXName = "Solution*"
2216 theoremName = "newtheorem*"
2219 LyXName = "Solution"
2220 theoremName = "newtheorem"
2222 j = find_end_of_layout(document.body, i)
2224 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2228 # if this is not a consecutive env, add start command
2231 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2233 # has this a consecutive theorem of same type?
2234 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2236 # if this is not followed by a consecutive env, add end command
2238 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2240 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2242 add_to_preamble(document, "\\theoremstyle{definition}")
2243 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2244 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2245 (theoremName, LaTeXName))
2246 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2247 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2248 (theoremName, LaTeXName))
2250 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2254 def revert_verbatim_star(document):
2255 from lyx_2_1 import revert_verbatim
2256 revert_verbatim(document, True)
2259 def convert_save_props(document):
2260 " Add save_transient_properties parameter. "
2261 i = find_token(document.header, '\\begin_header', 0)
2263 document.warning("Malformed lyx document: Missing '\\begin_header'.")
2265 document.header.insert(i + 1, '\\save_transient_properties true')
2268 def revert_save_props(document):
2269 " Remove save_transient_properties parameter. "
2270 i = find_token(document.header, "\\save_transient_properties", 0)
2273 del document.header[i]
2276 def convert_info_tabular_feature(document):
2278 return arg.replace("inset-modify tabular", "tabular-feature")
2279 convert_info_insets(document, "shortcut(s)?|icon", f)
2282 def revert_info_tabular_feature(document):
2284 return arg.replace("tabular-feature", "inset-modify tabular")
2285 convert_info_insets(document, "shortcut(s)?|icon", f)
2292 supported_versions = ["2.2.0", "2.2"]
2294 [475, [convert_separator]],
2295 # nothing to do for 476: We consider it a bug that older versions
2296 # did not load amsmath automatically for these commands, and do not
2297 # want to hardcode amsmath off.
2303 [481, [convert_dashes]],
2304 [482, [convert_phrases]],
2305 [483, [convert_specialchar]],
2310 [488, [convert_newgloss]],
2311 [489, [convert_BoxFeatures]],
2312 [490, [convert_origin]],
2314 [492, [convert_colorbox]],
2317 [495, [convert_subref]],
2318 [496, [convert_nounzip]],
2319 [497, [convert_external_bbox]],
2321 [499, [convert_moderncv_phone, convert_moderncv_name]],
2323 [501, [convert_fontsettings]],
2326 [504, [convert_save_props]],
2328 [506, [convert_info_tabular_feature]],
2329 [507, [convert_longtable_label]],
2330 [508, [convert_parbreak]]
2334 [507, [revert_parbreak]],
2335 [506, [revert_longtable_label]],
2336 [505, [revert_info_tabular_feature]],
2338 [503, [revert_save_props]],
2339 [502, [revert_verbatim_star]],
2340 [501, [revert_solution]],
2341 [500, [revert_fontsettings]],
2342 [499, [revert_achemso]],
2343 [498, [revert_moderncv_1, revert_moderncv_2]],
2344 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2345 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2346 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2347 [496, [revert_external_bbox]],
2348 [495, []], # nothing to do since the noUnzip parameter was optional
2349 [494, [revert_subref]],
2350 [493, [revert_jss]],
2351 [492, [revert_mathmulticol]],
2352 [491, [revert_colorbox]],
2353 [490, [revert_textcolor]],
2354 [489, [revert_origin]],
2355 [488, [revert_BoxFeatures]],
2356 [487, [revert_newgloss, revert_glossgroup]],
2357 [486, [revert_forest]],
2358 [485, [revert_ex_itemargs]],
2359 [484, [revert_sigplan_doi]],
2360 [483, [revert_georgian]],
2361 [482, [revert_specialchar]],
2362 [481, [revert_phrases]],
2363 [480, [revert_dashes]],
2364 [479, [revert_question_env]],
2365 [478, [revert_beamer_lemma]],
2366 [477, [revert_xarrow]],
2367 [476, [revert_swissgerman]],
2368 [475, [revert_smash]],
2369 [474, [revert_separator]]
2373 if __name__ == "__main__":