1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # Copyright (C) 2015 The LyX team
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 """ Convert files to the file format generated by lyx 2.2"""
25 # Uncomment only what you need to import, please.
27 #from parser_tools import find_token, find_end_of, find_tokens, \
28 # find_token_exact, find_end_of_inset, find_end_of_layout, \
29 # find_token_backwards, is_in_inset, get_value, get_quoted_value, \
30 # del_token, check_token, get_option_value
32 from lyx2lyx_tools import (add_to_preamble, put_cmd_in_ert, get_ert,
33 lyx2latex, lyx2verbatim, length_in_bp, convert_info_insets)
34 # insert_to_preamble, latex_length, revert_flex_inset,
35 # revert_font_attrs, hex2ratio, str2bool
37 from parser_tools import (check_token, del_complete_lines,
38 find_end_of_inset, find_end_of_layout, find_nonempty_line, find_re,
39 find_substring, find_token, find_token_backwards, get_containing_layout,
40 get_containing_inset, get_quoted_value, get_value, is_in_inset,
41 get_bool_value, set_bool_value)
44 ####################################################################
45 # Private helper functions
47 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt, nolastopt):
49 Reverts an InsetArgument to TeX-code
51 revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt, notLastOpt)
52 LineOfBegin is the line of the \begin_layout or \begin_inset statement
53 LineOfEnd is the line of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
54 StartArgument is the number of the first argument that needs to be converted
55 EndArgument is the number of the last argument that needs to be converted or the last defined one
56 isEnvironment must be true, if the layout is for a LaTeX environment
57 isOpt must be true, if the argument is an optional one
58 notLastOpt must be true if the argument is mandatory and followed by optional ones
62 while lineArg != -1 and n < nmax + 1:
63 lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
64 if lineArg > endline and endline != 0:
67 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
68 # we have to assure that no other inset is in the Argument
69 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
70 endInset = find_token(document.body, "\\end_inset", beginPlain)
73 while beginInset < endInset and beginInset != -1:
74 beginInset = find_token(document.body, "\\begin_inset", k)
75 endInset = find_token(document.body, "\\end_inset", l)
78 if environment == False:
80 if nolastopt == False:
81 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
83 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
84 del(document.body[lineArg : beginPlain + 1])
87 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
88 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
92 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
93 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
96 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
97 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
103 ###############################################################################
105 ### Conversion and reversion routines
107 ###############################################################################
109 def convert_longtable_label_internal(document, forward):
111 Convert reference to "LongTableNoNumber" into "Unnumbered" if forward is True
114 old_reference = "\\begin_inset Caption LongTableNoNumber"
115 new_reference = "\\begin_inset Caption Unnumbered"
117 # if the purpose is to revert swap the strings roles
119 old_reference, new_reference = new_reference, old_reference
123 i = find_token(document.body, old_reference, i)
128 document.body[i] = new_reference
131 def convert_longtable_label(document):
132 convert_longtable_label_internal(document, True)
135 def revert_longtable_label(document):
136 convert_longtable_label_internal(document, False)
139 def convert_separator(document):
141 Convert layout separators to separator insets and add (LaTeX) paragraph
142 breaks in order to mimic previous LaTeX export.
145 parins = ["\\begin_inset Separator parbreak", "\\end_inset", ""]
146 parlay = ["\\begin_layout Standard", "\\begin_inset Separator parbreak",
147 "\\end_inset", "", "\\end_layout", ""]
149 "family" : "default",
150 "series" : "default",
159 i = find_token(document.body, "\\begin_deeper", i)
163 j = find_token_backwards(document.body, "\\end_layout", i-1)
165 # reset any text style before inserting the inset
166 lay = get_containing_layout(document.body, j-1)
168 content = "\n".join(document.body[lay[1]:lay[2]])
169 for val in list(sty_dict.keys()):
170 if content.find("\\%s" % val) != -1:
171 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
174 document.body[j:j] = parins
175 i = i + len(parins) + 1
181 i = find_token(document.body, "\\align", i)
185 lay = get_containing_layout(document.body, i)
186 if lay != False and lay[0] == "Plain Layout":
190 j = find_token_backwards(document.body, "\\end_layout", i-1)
192 # Very old LyX files do not have Plain Layout in insets (but Standard).
193 # So we additionally check here if there is no inset boundary
194 # between the previous layout and this one.
195 n = find_token(document.body, "\\end_inset", j, lay[1])
199 lay = get_containing_layout(document.body, j-1)
200 if lay != False and lay[0] == "Standard" \
201 and find_token(document.body, "\\align", lay[1], lay[2]) == -1 \
202 and find_token(document.body, "\\begin_inset VSpace", lay[1], lay[2]) == -1:
203 # reset any text style before inserting the inset
204 content = "\n".join(document.body[lay[1]:lay[2]])
205 for val in list(sty_dict.keys()):
206 if content.find("\\%s" % val) != -1:
207 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
210 document.body[j:j] = parins
211 i = i + len(parins) + 1
217 regexp = re.compile(r'^\\begin_layout (?:(-*)|(\s*))(Separator|EndOfSlide)(?:(-*)|(\s*))$', re.IGNORECASE)
221 i = find_re(document.body, regexp, i)
225 j = find_end_of_layout(document.body, i)
227 document.warning("Malformed LyX document: Missing `\\end_layout'.")
230 lay = get_containing_layout(document.body, j-1)
232 lines = document.body[lay[3]:lay[2]]
236 document.body[i:j+1] = parlay
238 document.body[i+1:i+1] = lines
240 i = i + len(parlay) + len(lines) + 1
243 def revert_separator(document):
244 " Revert separator insets to layout separators "
246 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
247 if document.textclass in beamer_classes:
248 beglaysep = "\\begin_layout Separator"
250 beglaysep = "\\begin_layout --Separator--"
252 parsep = [beglaysep, "", "\\end_layout", ""]
253 comert = ["\\begin_inset ERT", "status collapsed", "",
254 "\\begin_layout Plain Layout", "%", "\\end_layout",
255 "", "\\end_inset", ""]
256 empert = ["\\begin_inset ERT", "status collapsed", "",
257 "\\begin_layout Plain Layout", " ", "\\end_layout",
258 "", "\\end_inset", ""]
262 i = find_token(document.body, "\\begin_inset Separator", i)
266 lay = get_containing_layout(document.body, i)
268 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
275 kind = get_value(document.body, "\\begin_inset Separator", i, i+1, "plain").split()[1]
276 before = document.body[beg+1:i]
277 something_before = len(before) > 0 and len("".join(before)) > 0
278 j = find_end_of_inset(document.body, i)
279 after = document.body[j+1:end]
280 something_after = len(after) > 0 and len("".join(after)) > 0
282 beg = beg + len(before) + 1
283 elif something_before:
284 document.body[i:i] = ["\\end_layout", ""]
292 document.body[beg:j+1] = empert
295 document.body[beg:j+1] = comert
299 if layoutname == "Standard":
300 if not something_before:
301 document.body[beg:j+1] = parsep
303 document.body[i:i] = ["", "\\begin_layout Standard"]
306 document.body[beg:j+1] = ["\\begin_layout Standard"]
309 document.body[beg:j+1] = ["\\begin_deeper"]
311 end = end + 1 - (j + 1 - beg)
312 if not something_before:
313 document.body[i:i] = parsep
315 end = end + len(parsep)
316 document.body[i:i] = ["\\begin_layout Standard"]
317 document.body[end+2:end+2] = ["", "\\end_deeper", ""]
320 next_par_is_aligned = False
321 k = find_nonempty_line(document.body, end+1)
322 if k != -1 and check_token(document.body[k], "\\begin_layout"):
323 lay = get_containing_layout(document.body, k)
324 next_par_is_aligned = lay != False and \
325 find_token(document.body, "\\align", lay[1], lay[2]) != -1
326 if k != -1 and not next_par_is_aligned \
327 and not check_token(document.body[k], "\\end_deeper") \
328 and not check_token(document.body[k], "\\begin_deeper"):
329 if layoutname == "Standard":
330 document.body[beg:j+1] = [beglaysep]
333 document.body[beg:j+1] = ["\\begin_deeper", beglaysep]
334 end = end + 2 - (j + 1 - beg)
335 document.body[end+1:end+1] = ["", "\\end_deeper", ""]
339 del document.body[i:end+1]
341 del document.body[i:end-1]
346 def convert_parbreak(document):
348 Convert parbreak separators not specifically used to separate
349 environments to latexpar separators.
351 parbreakinset = "\\begin_inset Separator parbreak"
354 i = find_token(document.body, parbreakinset, i)
357 lay = get_containing_layout(document.body, i)
359 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
362 if lay[0] == "Standard":
363 # Convert only if not alone in the paragraph
364 k1 = find_nonempty_line(document.body, lay[1] + 1, i + 1)
365 k2 = find_nonempty_line(document.body, i + 1, lay[2])
366 if (k1 < i) or (k2 > i + 1) or not check_token(document.body[i], parbreakinset):
367 document.body[i] = document.body[i].replace("parbreak", "latexpar")
369 document.body[i] = document.body[i].replace("parbreak", "latexpar")
373 def revert_parbreak(document):
375 Revert latexpar separators to parbreak separators.
379 i = find_token(document.body, "\\begin_inset Separator latexpar", i)
382 document.body[i] = document.body[i].replace("latexpar", "parbreak")
386 def revert_smash(document):
387 " Set amsmath to on if smash commands are used "
389 commands = ["smash[t]", "smash[b]", "notag"]
390 i = find_token(document.header, "\\use_package amsmath", 0)
392 document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
394 value = get_value(document.header, "\\use_package amsmath", i).split()[1]
396 # nothing to do if package is not auto but on or off
400 j = find_token(document.body, '\\begin_inset Formula', j)
403 k = find_end_of_inset(document.body, j)
405 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
408 code = "\n".join(document.body[j:k])
410 if code.find("\\%s" % c) != -1:
411 # set amsmath to on, since it is loaded by the newer format
412 document.header[i] = "\\use_package amsmath 2"
417 def revert_swissgerman(document):
418 " Set language german-ch-old to german "
420 if document.language == "german-ch-old":
421 document.language = "german"
422 i = find_token(document.header, "\\language", 0)
424 document.header[i] = "\\language german"
427 j = find_token(document.body, "\\lang german-ch-old", j)
430 document.body[j] = document.body[j].replace("\\lang german-ch-old", "\\lang german")
434 def revert_use_package(document, pkg, commands, oldauto, supported):
435 # oldauto defines how the version we are reverting to behaves:
436 # if it is true, the old version uses the package automatically.
437 # if it is false, the old version never uses the package.
438 # If "supported" is true, the target version also supports this
440 regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
441 p = find_re(document.header, regexp, 0)
442 value = "1" # default is auto
444 value = get_value(document.header, "\\use_package" , p).split()[1]
446 del document.header[p]
447 if value == "2" and not supported: # on
448 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
449 elif value == "1" and not oldauto: # auto
452 i = find_token(document.body, '\\begin_inset Formula', i)
455 j = find_end_of_inset(document.body, i)
457 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
460 code = "\n".join(document.body[i:j])
462 if code.find("\\%s" % c) != -1:
464 document.header[p] = "\\use_package " + pkg + " 2"
466 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
471 mathtools_commands = ["xhookrightarrow", "xhookleftarrow", "xRightarrow", \
472 "xrightharpoondown", "xrightharpoonup", "xrightleftharpoons", \
473 "xLeftarrow", "xleftharpoondown", "xleftharpoonup", \
474 "xleftrightarrow", "xLeftrightarrow", "xleftrightharpoons", \
477 def revert_xarrow(document):
478 "remove use_package mathtools"
479 revert_use_package(document, "mathtools", mathtools_commands, False, True)
482 def revert_beamer_lemma(document):
483 " Reverts beamer lemma layout to ERT "
485 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
486 if document.textclass not in beamer_classes:
492 i = find_token(document.body, "\\begin_layout Lemma", i)
495 j = find_end_of_layout(document.body, i)
497 document.warning("Malformed LyX document: Can't find end of Lemma layout")
500 arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j)
501 endarg1 = find_end_of_inset(document.body, arg1)
502 arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j)
503 endarg2 = find_end_of_inset(document.body, arg2)
507 beginPlain1 = find_token(document.body, "\\begin_layout Plain Layout", arg1, endarg1)
508 if beginPlain1 == -1:
509 document.warning("Malformed LyX document: Can't find arg1 plain Layout")
512 endPlain1 = find_end_of_inset(document.body, beginPlain1)
513 content1 = document.body[beginPlain1 + 1 : endPlain1 - 2]
514 subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">")
516 beginPlain2 = find_token(document.body, "\\begin_layout Plain Layout", arg2, endarg2)
517 if beginPlain2 == -1:
518 document.warning("Malformed LyX document: Can't find arg2 plain Layout")
521 endPlain2 = find_end_of_inset(document.body, beginPlain2)
522 content2 = document.body[beginPlain2 + 1 : endPlain2 - 2]
523 subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]")
527 del document.body[arg2 : endarg2 + 1]
529 del document.body[arg1 : endarg1 + 1]
531 del document.body[arg1 : endarg1 + 1]
533 del document.body[arg2 : endarg2 + 1]
535 # index of end layout has probably changed
536 j = find_end_of_layout(document.body, i)
538 document.warning("Malformed LyX document: Can't find end of Lemma layout")
544 # if this is not a consecutive env, add start command
546 begcmd = put_cmd_in_ert("\\begin{lemma}")
548 # has this a consecutive lemma?
549 consecutive = document.body[j + 2] == "\\begin_layout Lemma"
551 # if this is not followed by a consecutive env, add end command
553 document.body[j : j + 1] = put_cmd_in_ert("\\end{lemma}") + ["\\end_layout"]
555 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd + subst1 + subst2
561 def revert_question_env(document):
563 Reverts question and question* environments of
564 theorems-ams-extended-bytype module to ERT
567 # Do we use theorems-ams-extended-bytype module?
568 if not "theorems-ams-extended-bytype" in document.get_module_list():
574 i = find_token(document.body, "\\begin_layout Question", i)
578 starred = document.body[i] == "\\begin_layout Question*"
580 j = find_end_of_layout(document.body, i)
582 document.warning("Malformed LyX document: Can't find end of Question layout")
586 # if this is not a consecutive env, add start command
590 begcmd = put_cmd_in_ert("\\begin{question*}")
592 begcmd = put_cmd_in_ert("\\begin{question}")
594 # has this a consecutive theorem of same type?
597 consecutive = document.body[j + 2] == "\\begin_layout Question*"
599 consecutive = document.body[j + 2] == "\\begin_layout Question"
601 # if this is not followed by a consecutive env, add end command
604 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
606 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
608 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
610 add_to_preamble(document, "\\providecommand{\questionname}{Question}")
613 add_to_preamble(document, "\\theoremstyle{plain}\n" \
614 "\\newtheorem*{question*}{\\protect\\questionname}")
616 add_to_preamble(document, "\\theoremstyle{plain}\n" \
617 "\\newtheorem{question}{\\protect\\questionname}")
622 def convert_dashes(document):
623 "convert -- and --- to \\twohyphens and \\threehyphens"
625 if document.backend != "latex":
630 i = find_substring(document.body, "--", i+1)
633 line = document.body[i]
634 # skip label width string (bug 10243):
635 if line.startswith("\\labelwidthstring"):
637 # Do not touch hyphens in some insets:
639 value, start, end = get_containing_inset(document.body, i)
641 # False means no (or malformed) containing inset
642 value, start, end = "no inset", -1, -1
643 # We must not replace anything in insets that store LaTeX contents in .lyx files
644 # (math and command insets without overridden read() and write() methods.
645 # Filtering out IPA and ERT makes Text::readParToken() more simple,
646 # Flex Code is logical markup, typically rendered as typewriter
647 if (value.split()[0] in ["CommandInset", "ERT", "External", "Formula",
648 "FormulaMacro", "Graphics", "IPA", "listings"]
649 or value in ["Flex Code", "Flex URL"]):
653 layout, start, end, j = get_containing_layout(document.body, i)
654 except TypeError: # no (or malformed) containing layout
655 document.warning("Malformed LyX document: "
656 "Can't find layout at line %d" % i)
658 if layout == "LyX-Code":
661 # We can have an arbitrary number of consecutive hyphens.
662 # Replace as LaTeX does: First try emdash, then endash
663 line = line.replace("---", "\\threehyphens\n")
664 line = line.replace("--", "\\twohyphens\n")
665 document.body[i:i+1] = line.split('\n')
667 # remove ligature breaks between dashes
670 i = find_substring(document.body,
671 r"-\SpecialChar \textcompwordmark{}", i+1)
674 if document.body[i+1].startswith("-"):
675 document.body[i] = document.body[i].replace(
676 r"\SpecialChar \textcompwordmark{}", document.body.pop(i+1))
679 def revert_dashes(document):
681 Remove preamble code from 2.3->2.2 conversion.
682 Prevent ligatures of existing --- and --.
683 Revert \\twohyphens and \\threehyphens to -- and ---.
685 del_complete_lines(document.preamble,
686 ['% Added by lyx2lyx',
687 r'\renewcommand{\textendash}{--}',
688 r'\renewcommand{\textemdash}{---}'])
690 # Insert ligature breaks to prevent ligation of hyphens to dashes:
693 i = find_substring(document.body, "--", i+1)
696 line = document.body[i]
697 # skip label width string (bug 10243):
698 if line.startswith("\\labelwidthstring"):
700 # do not touch hyphens in some insets (cf. convert_dashes):
702 value, start, end = get_containing_inset(document.body, i)
704 # False means no (or malformed) containing inset
705 value, start, end = "no inset", -1, -1
706 if (value.split()[0] in ["CommandInset", "ERT", "External", "Formula",
707 "FormulaMacro", "Graphics", "IPA", "listings"]
708 or value == "Flex URL"):
711 line = line.replace("--", "-\\SpecialChar \\textcompwordmark{}\n-")
712 document.body[i:i+1] = line.split('\n')
714 # Revert \twohyphens and \threehyphens:
716 while i < len(document.body):
717 line = document.body[i]
718 if not line.endswith("hyphens"):
720 elif line.endswith("\\twohyphens") or line.endswith("\\threehyphens"):
721 line = line.replace("\\twohyphens", "--")
722 line = line.replace("\\threehyphens", "---")
723 document.body[i] = line + document.body.pop(i+1)
728 # order is important for the last three!
729 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
731 def is_part_of_converted_phrase(line, j, phrase):
732 "is phrase part of an already converted phrase?"
734 converted = "\\SpecialCharNoPassThru \\" + p
735 pos = j + len(phrase) - len(converted)
737 if line[pos:pos+len(converted)] == converted:
742 def convert_phrases(document):
743 "convert special phrases from plain text to \\SpecialCharNoPassThru"
745 if document.backend != "latex":
748 for phrase in phrases:
750 while i < len(document.body):
751 words = document.body[i].split()
752 if len(words) > 1 and words[0] == "\\begin_inset" and \
753 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
754 # must not replace anything in insets that store LaTeX contents in .lyx files
755 # (math and command insets without overridden read() and write() methods)
756 j = find_end_of_inset(document.body, i)
758 document.warning("Malformed LyX document: Can't find end of inset at line " + str(i))
763 if document.body[i].find("\\") == 0:
766 j = document.body[i].find(phrase)
770 if not is_part_of_converted_phrase(document.body[i], j, phrase):
771 front = document.body[i][:j]
772 back = document.body[i][j+len(phrase):]
774 document.body.insert(i+1, back)
775 # We cannot use SpecialChar since we do not know whether we are outside passThru
776 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
780 def revert_phrases(document):
781 "convert special phrases to plain text"
784 while i < len(document.body):
785 words = document.body[i].split()
786 if len(words) > 1 and words[0] == "\\begin_inset" and \
787 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
788 # see convert_phrases
789 j = find_end_of_inset(document.body, i)
791 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
797 for phrase in phrases:
798 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
799 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
800 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
802 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
803 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
805 if replaced and i+1 < len(document.body) and \
806 (document.body[i+1].find("\\") != 0 or \
807 document.body[i+1].find("\\SpecialChar") == 0) and \
808 len(document.body[i]) + len(document.body[i+1]) <= 80:
809 document.body[i] = document.body[i] + document.body[i+1]
810 document.body[i+1:i+2] = []
815 def convert_specialchar_internal(document, forward):
816 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
817 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
818 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
819 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
820 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
821 "\\LaTeX":"LaTeX" # must be after LaTeX2e
825 while i < len(document.body):
826 words = document.body[i].split()
827 if len(words) > 1 and words[0] == "\\begin_inset" and \
828 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
829 # see convert_phrases
830 j = find_end_of_inset(document.body, i)
832 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
837 for key, value in specialchars.items():
839 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
840 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
842 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
843 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
847 def convert_specialchar(document):
848 "convert special characters to new syntax"
849 convert_specialchar_internal(document, True)
852 def revert_specialchar(document):
853 "convert special characters to old syntax"
854 convert_specialchar_internal(document, False)
857 def revert_georgian(document):
858 "Set the document language to English but assure Georgian output"
860 if document.language == "georgian":
861 document.language = "english"
862 i = find_token(document.header, "\\language georgian", 0)
864 document.header[i] = "\\language english"
865 j = find_token(document.header, "\\language_package default", 0)
867 document.header[j] = "\\language_package babel"
868 k = find_token(document.header, "\\options", 0)
870 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
872 l = find_token(document.header, "\\use_default_options", 0)
873 document.header.insert(l + 1, "\\options georgian")
876 def revert_sigplan_doi(document):
877 " Reverts sigplanconf DOI layout to ERT "
879 if document.textclass != "sigplanconf":
884 i = find_token(document.body, "\\begin_layout DOI", i)
887 j = find_end_of_layout(document.body, i)
889 document.warning("Malformed LyX document: Can't find end of DOI layout")
893 content = lyx2latex(document, document.body[i:j + 1])
894 add_to_preamble(document, ["\\doi{" + content + "}"])
895 del document.body[i:j + 1]
899 def revert_ex_itemargs(document):
900 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
902 if not "linguistics" in document.get_module_list():
906 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
908 i = find_token(document.body, "\\begin_inset Argument item:", i)
911 j = find_end_of_inset(document.body, i)
912 # Find containing paragraph layout
913 parent = get_containing_layout(document.body, i)
915 document.warning("Malformed LyX document: Can't find parent paragraph layout")
919 layoutname = parent[0]
920 if layoutname in example_layouts:
921 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
922 endPlain = find_end_of_layout(document.body, beginPlain)
923 content = document.body[beginPlain + 1 : endPlain]
924 del document.body[i:j+1]
925 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
926 document.body[parbeg : parbeg] = subst
930 def revert_forest(document):
931 " Reverts the forest environment (Linguistics module) to TeX-code "
933 if not "linguistics" in document.get_module_list():
938 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
941 j = find_end_of_inset(document.body, i)
943 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
947 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
948 endPlain = find_end_of_layout(document.body, beginPlain)
949 content = lyx2latex(document, document.body[beginPlain : endPlain])
951 add_to_preamble(document, ["\\usepackage{forest}"])
953 document.body[i:j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
957 def revert_glossgroup(document):
958 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
960 if not "linguistics" in document.get_module_list():
965 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
968 j = find_end_of_inset(document.body, i)
970 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
974 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
975 endPlain = find_end_of_layout(document.body, beginPlain)
976 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
978 document.body[i:j + 1] = ["{", "", content, "", "}"]
982 def revert_newgloss(document):
983 " Reverts the new Glosse insets (Linguistics module) to the old format "
985 if not "linguistics" in document.get_module_list():
988 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
989 for glosse in glosses:
992 i = find_token(document.body, glosse, i)
995 j = find_end_of_inset(document.body, i)
997 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1001 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
1002 endarg = find_end_of_inset(document.body, arg)
1005 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
1006 if argbeginPlain == -1:
1007 document.warning("Malformed LyX document: Can't find arg plain Layout")
1010 argendPlain = find_end_of_inset(document.body, argbeginPlain)
1011 argcontent = lyx2verbatim(document, document.body[argbeginPlain : argendPlain - 2])
1013 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
1014 argcontent, "\\end_layout"]
1016 # remove Arg insets and paragraph, if it only contains this inset
1017 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
1018 del document.body[arg - 1 : endarg + 4]
1020 del document.body[arg : endarg + 1]
1022 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1023 endPlain = find_end_of_layout(document.body, beginPlain)
1024 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
1026 document.body[beginPlain + 1:endPlain] = [content]
1029 # Dissolve ERT insets
1030 for glosse in glosses:
1033 i = find_token(document.body, glosse, i)
1036 j = find_end_of_inset(document.body, i)
1038 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1042 ert = find_token(document.body, "\\begin_inset ERT", i, j)
1045 ertend = find_end_of_inset(document.body, ert)
1047 document.warning("Malformed LyX document: Can't find end of ERT inset")
1050 ertcontent = get_ert(document.body, ert, True)
1051 document.body[ert : ertend + 1] = [ertcontent]
1055 def convert_newgloss(document):
1056 " Converts Glosse insets (Linguistics module) to the new format "
1058 if not "linguistics" in document.get_module_list():
1061 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1062 for glosse in glosses:
1065 i = find_token(document.body, glosse, i)
1068 j = find_end_of_inset(document.body, i)
1070 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1077 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
1078 if beginPlain == -1:
1080 endPlain = find_end_of_layout(document.body, beginPlain)
1082 document.warning("Malformed LyX document: Can't find end of Glosse layout")
1086 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
1087 if glt != -1 and document.body[glt + 1].startswith("glt"):
1088 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1089 argcontent = document.body[glt + 1 : endPlain]
1090 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
1091 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
1092 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
1093 "\\end_layout", "", "\\end_inset"]
1095 content = document.body[beginPlain + 1 : endPlain]
1096 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
1097 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
1099 endPlain = find_end_of_layout(document.body, beginPlain)
1101 j = find_end_of_inset(document.body, i)
1106 def convert_BoxFeatures(document):
1107 " adds new box features "
1111 i = find_token(document.body, "height_special", i)
1114 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1118 def revert_BoxFeatures(document):
1119 " outputs new box features as TeX code "
1123 defaultThick = "0.4pt"
1124 defaultShadow = "4pt"
1126 i = find_token(document.body, "thickness", i)
1129 binset = find_token(document.body, "\\begin_inset Box", i - 11)
1130 if binset == -1 or binset != i - 11:
1132 continue # then "thickness" is is just a word in the text
1133 einset = find_end_of_inset(document.body, binset)
1135 document.warning("Malformed LyX document: Can't find end of box inset!")
1138 # read out the values
1139 beg = document.body[i].find('"');
1140 end = document.body[i].rfind('"');
1141 thickness = document.body[i][beg+1:end];
1142 beg = document.body[i+1].find('"');
1143 end = document.body[i+1].rfind('"');
1144 separation = document.body[i+1][beg+1:end];
1145 beg = document.body[i+2].find('"');
1146 end = document.body[i+2].rfind('"');
1147 shadowsize = document.body[i+2][beg+1:end];
1148 # delete the specification
1149 del document.body[i:i+3]
1151 # first output the closing brace
1152 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1153 document.body[einset -1 : einset - 1] = put_cmd_in_ert("}")
1154 # we have now the problem that if there is already \(f)colorbox in ERT around the inset
1155 # the ERT from this routine must be around it
1156 regexp = re.compile(r'^.*colorbox{.*$')
1157 pos = find_re(document.body, regexp, binset - 4)
1158 if pos != -1 and pos == binset - 4:
1162 # now output the lengths
1163 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1164 document.body[pos : pos] = put_cmd_in_ert("{")
1165 if thickness != defaultThick:
1166 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness]
1167 if separation != defaultSep and thickness == defaultThick:
1168 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation]
1169 if separation != defaultSep and thickness != defaultThick:
1170 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1171 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1172 document.body[pos + 5 : pos +6] = ["{\\backslash shadowsize " + shadowsize]
1173 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1174 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1175 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1176 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1177 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1178 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1181 def convert_origin(document):
1182 " Insert the origin tag "
1184 i = find_token(document.header, "\\textclass ", 0)
1186 document.warning("Malformed LyX document: No \\textclass!!")
1188 if document.dir == u'':
1192 if document.systemlyxdir and document.systemlyxdir != u'':
1194 if os.path.isabs(document.dir):
1195 absdir = os.path.normpath(document.dir)
1197 absdir = os.path.normpath(os.path.abspath(document.dir))
1198 if os.path.isabs(document.systemlyxdir):
1199 abssys = os.path.normpath(document.systemlyxdir)
1201 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1202 relpath = os.path.relpath(absdir, abssys)
1203 if relpath.find(u'..') == 0:
1208 origin = document.dir.replace(u'\\', u'/') + u'/'
1210 origin = os.path.join(u"/systemlyxdir", relpath).replace(u'\\', u'/') + u'/'
1211 document.header[i:i] = ["\\origin " + origin]
1214 def revert_origin(document):
1215 " Remove the origin tag "
1217 i = find_token(document.header, "\\origin ", 0)
1219 document.warning("Malformed LyX document: No \\origin!!")
1221 del document.header[i]
1224 color_names = ["brown", "darkgray", "gray", \
1225 "lightgray", "lime", "olive", "orange", \
1226 "pink", "purple", "teal", "violet"]
1228 def revert_textcolor(document):
1229 " revert new \\textcolor colors to TeX code "
1235 i = find_token(document.body, "\\color ", i)
1239 for color in list(color_names):
1240 if document.body[i] == "\\color " + color:
1241 # register that xcolor must be loaded in the preamble
1244 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1245 # find the next \\color and/or the next \\end_layout
1246 j = find_token(document.body, "\\color", i + 1)
1247 k = find_token(document.body, "\\end_layout", i + 1)
1248 if j == -1 and k != -1:
1251 # first output the closing brace
1253 document.body[k: k] = put_cmd_in_ert("}")
1255 document.body[j: j] = put_cmd_in_ert("}")
1256 # now output the \textcolor command
1257 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1261 def convert_colorbox(document):
1262 "Add color settings for boxes."
1265 i = find_token(document.body, "shadowsize", i)
1268 # check whether this is really a LyX Box setting
1269 start, end = is_in_inset(document.body, i, "\\begin_inset Box")
1271 find_token(document.body, "\\begin_layout", start, i) != -1):
1274 document.body[i+1:i+1] = ['framecolor "black"',
1275 'backgroundcolor "none"']
1279 def revert_colorbox(document):
1280 """Change box color settings to LaTeX code."""
1284 i = find_token(document.body, "\\begin_inset Box", i+1)
1287 # Get and delete colour settings:
1288 framecolor = get_quoted_value(document.body, "framecolor", i+14, i+15, delete=True)
1289 backcolor = get_quoted_value(document.body, "backgroundcolor", i+14, i+15, delete=True)
1290 if not framecolor or not backcolor:
1291 document.warning("Malformed LyX document: color options not found in Box inset!")
1293 if framecolor == "black" and backcolor == "none": # default values
1294 i += 15 # skip box option lines
1297 # Emulate non-default colours with LaTeX code:
1298 einset = find_end_of_inset(document.body, i)
1300 document.warning("Malformed LyX document: Can't find end of box inset!")
1302 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1303 # insert the closing brace first (keeps indices 'i' and 'einset' valid)
1304 document.body[einset+1:einset+1] = put_cmd_in_ert("}") + [""]
1305 # now insert the (f)color box command
1306 if ("Box Boxed" in document.body[i]): # framed box, use \fcolorbox
1307 # change the box type (frame added by \fcolorbox)
1308 document.body[i] = "\\begin_inset Box Frameless"
1309 # ensure an inner box:
1311 if not set_bool_value(document.body, "has_inner_box", True, i+3, i+4):
1312 set_bool_value(document.body, "use_makebox", True, i+6, i+7)
1314 document.warning("Malformed LyX document: 'has_inner_box' or "
1315 "'use_makebox' option not found in box inset!")
1316 ertinset = put_cmd_in_ert("\\fcolorbox{%s}{%s}{"% (framecolor, backcolor))
1318 ertinset = put_cmd_in_ert("\\colorbox{%s}{" % backcolor)
1319 document.body[i:i] = ertinset + [""]
1320 i = einset # skip inset
1323 def revert_mathmulticol(document):
1324 " Convert formulas to ERT if they contain multicolumns "
1328 i = find_token(document.body, '\\begin_inset Formula', i)
1331 j = find_end_of_inset(document.body, i)
1333 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1336 lines = document.body[i:j]
1337 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1338 code = "\n".join(lines)
1343 n = code.find("\\multicolumn", k)
1344 # no need to convert degenerated multicolumn cells,
1345 # they work in old LyX versions as "math ERT"
1346 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1347 ert = put_cmd_in_ert(code)
1348 document.body[i:j+1] = ert
1354 i = find_end_of_inset(document.body, i)
1359 def revert_jss(document):
1360 " Reverts JSS In_Preamble commands to ERT in preamble "
1362 if document.textclass != "jss":
1371 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1372 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1375 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1377 endh = find_end_of_inset(document.body, h)
1378 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1379 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1383 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1385 endm = find_end_of_inset(document.body, m)
1386 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1387 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1391 j = find_token(document.body, "\\begin_inset Flex Code", j)
1393 # assure that we are not in a Code Chunk inset
1394 if document.body[j][-1] == "e":
1395 endj = find_end_of_inset(document.body, j)
1396 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1397 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1403 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1405 endk = find_end_of_inset(document.body, k)
1406 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1407 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1411 n = find_token(document.body, "\\begin_inset Flex URL", n)
1413 endn = find_end_of_inset(document.body, n)
1414 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1415 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1417 # now revert the In_Preamble layouts
1419 i = find_token(document.body, "\\begin_layout Title", 0)
1422 j = find_end_of_layout(document.body, i)
1424 document.warning("Malformed LyX document: Can't find end of Title layout")
1427 content = lyx2latex(document, document.body[i:j + 1])
1428 add_to_preamble(document, ["\\title{" + content + "}"])
1429 del document.body[i:j + 1]
1431 i = find_token(document.body, "\\begin_layout Author", 0)
1434 j = find_end_of_layout(document.body, i)
1436 document.warning("Malformed LyX document: Can't find end of Author layout")
1439 content = lyx2latex(document, document.body[i:j + 1])
1440 add_to_preamble(document, ["\\author{" + content + "}"])
1441 del document.body[i:j + 1]
1443 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1446 j = find_end_of_layout(document.body, i)
1448 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1451 content = lyx2latex(document, document.body[i:j + 1])
1452 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1453 del document.body[i:j + 1]
1455 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1458 j = find_end_of_layout(document.body, i)
1460 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1463 content = lyx2latex(document, document.body[i:j + 1])
1464 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1465 del document.body[i:j + 1]
1467 i = find_token(document.body, "\\begin_layout Short Title", 0)
1470 j = find_end_of_layout(document.body, i)
1472 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1475 content = lyx2latex(document, document.body[i:j + 1])
1476 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1477 del document.body[i:j + 1]
1479 i = find_token(document.body, "\\begin_layout Abstract", 0)
1482 j = find_end_of_layout(document.body, i)
1484 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1487 content = lyx2latex(document, document.body[i:j + 1])
1488 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1489 del document.body[i:j + 1]
1491 i = find_token(document.body, "\\begin_layout Keywords", 0)
1494 j = find_end_of_layout(document.body, i)
1496 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1499 content = lyx2latex(document, document.body[i:j + 1])
1500 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1501 del document.body[i:j + 1]
1503 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1506 j = find_end_of_layout(document.body, i)
1508 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1511 content = lyx2latex(document, document.body[i:j + 1])
1512 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1513 del document.body[i:j + 1]
1515 i = find_token(document.body, "\\begin_layout Address", 0)
1518 j = find_end_of_layout(document.body, i)
1520 document.warning("Malformed LyX document: Can't find end of Address layout")
1523 content = lyx2latex(document, document.body[i:j + 1])
1524 add_to_preamble(document, ["\\Address{" + content + "}"])
1525 del document.body[i:j + 1]
1526 # finally handle the code layouts
1531 while m != -1 or j != -1 or h != -1 or k != -1:
1534 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1536 endh = find_end_of_inset(document.body, h)
1537 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1538 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1539 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1543 j = find_token(document.body, "\\begin_layout Code Input", j)
1545 endj = find_end_of_layout(document.body, j)
1546 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1547 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1548 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1549 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1550 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1554 k = find_token(document.body, "\\begin_layout Code Output", k)
1556 endk = find_end_of_layout(document.body, k)
1557 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1558 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1559 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1560 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1561 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1565 m = find_token(document.body, "\\begin_layout Code", m)
1567 endm = find_end_of_layout(document.body, m)
1568 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1569 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1570 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1571 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1572 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1576 def convert_subref(document):
1577 " converts sub: ref prefixes to subref: "
1580 rx = re.compile(r'^name \"sub:(.+)$')
1583 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1586 j = find_end_of_inset(document.body, i)
1588 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1592 for p in range(i, j):
1593 m = rx.match(document.body[p])
1596 document.body[p] = "name \"subsec:" + label
1600 rx = re.compile(r'^reference \"sub:(.+)$')
1603 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1606 j = find_end_of_inset(document.body, i)
1608 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1612 for p in range(i, j):
1613 m = rx.match(document.body[p])
1616 document.body[p] = "reference \"subsec:" + label
1622 def revert_subref(document):
1623 " reverts subref: ref prefixes to sub: "
1626 rx = re.compile(r'^name \"subsec:(.+)$')
1629 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1632 j = find_end_of_inset(document.body, i)
1634 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1638 for p in range(i, j):
1639 m = rx.match(document.body[p])
1642 document.body[p] = "name \"sub:" + label
1647 rx = re.compile(r'^reference \"subsec:(.+)$')
1650 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1653 j = find_end_of_inset(document.body, i)
1655 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1659 for p in range(i, j):
1660 m = rx.match(document.body[p])
1663 document.body[p] = "reference \"sub:" + label
1668 def convert_nounzip(document):
1669 " remove the noUnzip parameter of graphics insets "
1671 rx = re.compile(r'\s*noUnzip\s*$')
1674 i = find_token(document.body, "\\begin_inset Graphics", i)
1677 j = find_end_of_inset(document.body, i)
1679 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1683 k = find_re(document.body, rx, i, j)
1685 del document.body[k]
1690 def convert_revert_external_bbox(document, forward):
1691 " add units to bounding box of external insets "
1693 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1696 i = find_token(document.body, "\\begin_inset External", i)
1699 j = find_end_of_inset(document.body, i)
1701 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1704 k = find_re(document.body, rx, i, j)
1708 tokens = document.body[k].split()
1710 for t in range(1, 5):
1713 for t in range(1, 5):
1714 tokens[t] = length_in_bp(tokens[t])
1715 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1716 tokens[3] + " " + tokens[4]
1720 def convert_external_bbox(document):
1721 convert_revert_external_bbox(document, True)
1724 def revert_external_bbox(document):
1725 convert_revert_external_bbox(document, False)
1728 def revert_tcolorbox_1(document):
1729 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1732 i = find_token(document.header, "tcolorbox", i)
1738 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1741 flexEnd = find_end_of_inset(document.body, flex)
1742 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1743 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1744 flexEnd = find_end_of_inset(document.body, flex)
1746 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1748 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1749 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1753 def revert_tcolorbox_2(document):
1754 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1757 i = find_token(document.header, "tcolorbox", i)
1763 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1766 flexEnd = find_end_of_inset(document.body, flex)
1767 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1768 flexEnd = find_end_of_inset(document.body, flex)
1769 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1770 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1774 def revert_tcolorbox_3(document):
1775 " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1778 i = find_token(document.header, "tcolorbox", i)
1784 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1787 flexEnd = find_end_of_inset(document.body, flex)
1788 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1789 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1790 flexEnd = find_end_of_inset(document.body, flex)
1791 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1792 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1796 def revert_tcolorbox_4(document):
1797 " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1800 i = find_token(document.header, "tcolorbox", i)
1806 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1809 flexEnd = find_end_of_inset(document.body, flex)
1810 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1811 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1812 flexEnd = find_end_of_inset(document.body, flex)
1813 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1814 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1818 def revert_tcolorbox_5(document):
1819 " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1822 i = find_token(document.header, "tcolorbox", i)
1828 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1831 flexEnd = find_end_of_inset(document.body, flex)
1832 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1833 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1834 flexEnd = find_end_of_inset(document.body, flex)
1835 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1836 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1840 def revert_tcolorbox_6(document):
1841 " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1844 i = find_token(document.header, "tcolorbox", i)
1850 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1853 flexEnd = find_end_of_inset(document.body, flex)
1854 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1855 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1856 flexEnd = find_end_of_inset(document.body, flex)
1857 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1858 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1862 def revert_tcolorbox_7(document):
1863 " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1866 i = find_token(document.header, "tcolorbox", i)
1872 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1875 flexEnd = find_end_of_inset(document.body, flex)
1876 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1877 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1878 flexEnd = find_end_of_inset(document.body, flex)
1879 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1880 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1884 def revert_tcolorbox_8(document):
1885 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1891 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1893 j = find_end_of_layout(document.body, i)
1894 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1895 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1896 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1897 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1899 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1901 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1902 k = find_end_of_inset(document.body, j)
1903 k = find_token(document.body, "\\end_inset", k + 1)
1904 k = find_token(document.body, "\\end_inset", k + 1)
1906 k = find_token(document.body, "\\end_inset", k + 1)
1907 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1908 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1909 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1915 def revert_moderncv_1(document):
1916 " Reverts the new inset of moderncv to TeX-code in preamble "
1918 if document.textclass != "moderncv":
1924 # at first revert the new styles
1926 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1929 j = find_end_of_layout(document.body, i)
1931 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1934 content = lyx2latex(document, document.body[i:j + 1])
1935 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1936 del document.body[i:j + 1]
1938 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1941 j = find_end_of_layout(document.body, i)
1943 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1946 content = lyx2latex(document, document.body[i:j + 1])
1947 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1948 del document.body[i:j + 1]
1949 # now change the new styles to the obsolete ones
1951 i = find_token(document.body, "\\begin_layout Name", 0)
1954 j = find_end_of_layout(document.body, i)
1956 document.warning("Malformed LyX document: Can't find end of Name layout")
1959 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1960 if lineArg > j and j != 0:
1963 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1964 # we have to assure that no other inset is in the Argument
1965 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1966 endInset = find_token(document.body, "\\end_inset", beginPlain)
1969 while beginInset < endInset and beginInset != -1:
1970 beginInset = find_token(document.body, "\\begin_inset", k)
1971 endInset = find_token(document.body, "\\end_inset", l)
1974 Arg2 = document.body[l + 5 : l + 6]
1976 document.body[i : i + 1]= ["\\begin_layout FirstName"]
1977 # delete the Argument inset
1978 del( document.body[endInset - 2 : endInset + 3])
1979 del( document.body[lineArg : beginPlain + 1])
1980 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1983 def revert_moderncv_2(document):
1984 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1986 if document.textclass != "moderncv":
1993 i = find_token(document.body, "\\begin_layout Phone", i)
1996 j = find_end_of_layout(document.body, i)
1998 document.warning("Malformed LyX document: Can't find end of Phone layout")
2001 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
2002 if lineArg > j and j != 0:
2006 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
2007 # we have to assure that no other inset is in the Argument
2008 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
2009 endInset = find_token(document.body, "\\end_inset", beginPlain)
2012 while beginInset < endInset and beginInset != -1:
2013 beginInset = find_token(document.body, "\\begin_inset", k)
2014 endInset = find_token(document.body, "\\end_inset", l)
2017 Arg = document.body[beginPlain + 1 : beginPlain + 2]
2019 if Arg[0] == "mobile":
2020 document.body[i : i + 1]= ["\\begin_layout Mobile"]
2022 document.body[i : i + 1]= ["\\begin_layout Fax"]
2023 # delete the Argument inset
2024 del(document.body[endInset - 2 : endInset + 1])
2025 del(document.body[lineArg : beginPlain + 3])
2029 def convert_moderncv_phone(document):
2030 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
2032 if document.textclass != "moderncv":
2039 "Mobile" : "mobile",
2043 rx = re.compile(r'^\\begin_layout (\S+)$')
2045 # substitute \fax and \mobile by \phone[fax] and \phone[mobile], respectively
2046 i = find_token(document.body, "\\begin_layout", i)
2050 m = rx.match(document.body[i])
2054 if val not in list(phone_dict.keys()):
2057 j = find_end_of_layout(document.body, i)
2059 document.warning("Malformed LyX document: Can't find end of Mobile layout")
2063 document.body[i : i + 1] = ["\\begin_layout Phone", "\\begin_inset Argument 1", "status open", "",
2064 "\\begin_layout Plain Layout", phone_dict[val], "\\end_layout", "",
2068 def convert_moderncv_name(document):
2069 " Convert the FirstName and LastName layout of moderncv to the general Name layout "
2071 if document.textclass != "moderncv":
2074 fnb = 0 # Begin of FirstName inset
2075 fne = 0 # End of FirstName inset
2076 lnb = 0 # Begin of LastName (FamilyName) inset
2077 lne = 0 # End of LastName (FamilyName) inset
2078 nb = 0 # Begin of substituting Name inset
2079 ne = 0 # End of substituting Name inset
2080 FirstName = [] # FirstName content
2081 FamilyName = [] # LastName content
2085 fnb = find_token(document.body, "\\begin_layout FirstName", fnb)
2087 fne = find_end_of_layout(document.body, fnb)
2089 document.warning("Malformed LyX document: Can't find end of FirstName layout")
2091 FirstName = document.body[fnb + 1 : fne]
2093 lnb = find_token(document.body, "\\begin_layout FamilyName", lnb)
2095 lne = find_end_of_layout(document.body, lnb)
2097 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
2099 FamilyName = document.body[lnb + 1 : lne]
2100 # Determine the region for the substituting Name layout
2101 if fnb == -1 and lnb == -1: # Neither FirstName nor FamilyName exists -> Do nothing
2103 elif fnb == -1: # Only FamilyName exists -> New Name insets replaces that
2106 elif lnb == -1: # Only FirstName exists -> New Name insets replaces that
2109 elif fne > lne: # FirstName position before FamilyName -> New Name insets spans
2110 nb = lnb # from FamilyName begin
2111 ne = fne # to FirstName end
2112 else: # FirstName position before FamilyName -> New Name insets spans
2113 nb = fnb # from FirstName begin
2114 ne = lne # to FamilyName end
2116 # Insert the substituting layout now. If FirstName exists, use an otpional argument.
2118 document.body[nb : ne + 1] = ["\\begin_layout Name"] + FamilyName + ["\\end_layout", ""]
2120 document.body[nb : ne + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2121 "\\begin_layout Plain Layout"] + FirstName + ["\\end_layout", "",
2122 "\\end_inset", ""] + FamilyName + ["\\end_layout", ""]
2125 def revert_achemso(document):
2126 " Reverts the flex inset Latin to TeX code "
2128 if document.textclass != "achemso":
2133 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2135 j = find_end_of_inset(document.body, i)
2139 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2140 endPlain = find_end_of_layout(document.body, beginPlain)
2141 content = lyx2latex(document, document.body[beginPlain : endPlain])
2142 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2144 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2149 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2150 "\\font_sf_scale", "\\font_tt_scale"]
2151 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2152 fontquotes = [True, True, True, True, False, False]
2154 def convert_fontsettings(document):
2155 " Duplicate font settings "
2157 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2159 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2160 use_non_tex_fonts = "false"
2162 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2164 for f in fontsettings:
2165 i = find_token(document.header, f + " ", 0)
2167 document.warning("Malformed LyX document: No " + f + "!")
2169 # note that with i = -1, this will insert at the end
2171 value = fontdefaults[j]
2173 value = document.header[i][len(f):].strip()
2175 if use_non_tex_fonts == "true":
2176 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2178 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2180 if use_non_tex_fonts == "true":
2181 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2183 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2187 def revert_fontsettings(document):
2188 " Merge font settings "
2190 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2192 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2193 use_non_tex_fonts = "false"
2195 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2197 for f in fontsettings:
2198 i = find_token(document.header, f + " ", 0)
2200 document.warning("Malformed LyX document: No " + f + "!")
2203 line = get_value(document.header, f, i)
2206 q2 = line.find('"', q1+1)
2207 q3 = line.find('"', q2+1)
2208 q4 = line.find('"', q3+1)
2209 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2210 document.warning("Malformed LyX document: Missing quotes!")
2213 if use_non_tex_fonts == "true":
2214 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2216 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2218 if use_non_tex_fonts == "true":
2219 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2221 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2225 def revert_solution(document):
2226 " Reverts the solution environment of the theorem module to TeX code "
2228 # Do we use one of the modules that provides Solution?
2230 mods = document.get_module_list()
2232 if mod == "theorems-std" or mod == "theorems-bytype" \
2233 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2243 i = find_token(document.body, "\\begin_layout Solution", i)
2247 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2248 if is_starred == True:
2250 LyXName = "Solution*"
2251 theoremName = "newtheorem*"
2254 LyXName = "Solution"
2255 theoremName = "newtheorem"
2257 j = find_end_of_layout(document.body, i)
2259 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2263 # if this is not a consecutive env, add start command
2266 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2268 # has this a consecutive theorem of same type?
2269 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2271 # if this is not followed by a consecutive env, add end command
2273 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2275 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2277 add_to_preamble(document, "\\theoremstyle{definition}")
2278 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2279 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2280 (theoremName, LaTeXName))
2281 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2282 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2283 (theoremName, LaTeXName))
2285 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2289 def revert_verbatim_star(document):
2290 from lyx_2_1 import revert_verbatim
2291 revert_verbatim(document, True)
2294 def convert_save_props(document):
2295 " Add save_transient_properties parameter. "
2296 i = find_token(document.header, '\\begin_header', 0)
2298 document.warning("Malformed lyx document: Missing '\\begin_header'.")
2300 document.header.insert(i + 1, '\\save_transient_properties true')
2303 def revert_save_props(document):
2304 " Remove save_transient_properties parameter. "
2305 i = find_token(document.header, "\\save_transient_properties", 0)
2308 del document.header[i]
2311 def convert_info_tabular_feature(document):
2313 return arg.replace("inset-modify tabular", "tabular-feature")
2314 convert_info_insets(document, "shortcut(s)?|icon", f)
2317 def revert_info_tabular_feature(document):
2319 return arg.replace("tabular-feature", "inset-modify tabular")
2320 convert_info_insets(document, "shortcut(s)?|icon", f)
2327 supported_versions = ["2.2.0", "2.2"]
2329 [475, [convert_separator]],
2330 # nothing to do for 476: We consider it a bug that older versions
2331 # did not load amsmath automatically for these commands, and do not
2332 # want to hardcode amsmath off.
2338 [481, [convert_dashes]],
2339 [482, [convert_phrases]],
2340 [483, [convert_specialchar]],
2345 [488, [convert_newgloss]],
2346 [489, [convert_BoxFeatures]],
2347 [490, [convert_origin]],
2349 [492, [convert_colorbox]],
2352 [495, [convert_subref]],
2353 [496, [convert_nounzip]],
2354 [497, [convert_external_bbox]],
2356 [499, [convert_moderncv_phone, convert_moderncv_name]],
2358 [501, [convert_fontsettings]],
2361 [504, [convert_save_props]],
2363 [506, [convert_info_tabular_feature]],
2364 [507, [convert_longtable_label]],
2365 [508, [convert_parbreak]]
2369 [507, [revert_parbreak]],
2370 [506, [revert_longtable_label]],
2371 [505, [revert_info_tabular_feature]],
2373 [503, [revert_save_props]],
2374 [502, [revert_verbatim_star]],
2375 [501, [revert_solution]],
2376 [500, [revert_fontsettings]],
2377 [499, [revert_achemso]],
2378 [498, [revert_moderncv_1, revert_moderncv_2]],
2379 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2380 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2381 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2382 [496, [revert_external_bbox]],
2383 [495, []], # nothing to do since the noUnzip parameter was optional
2384 [494, [revert_subref]],
2385 [493, [revert_jss]],
2386 [492, [revert_mathmulticol]],
2387 [491, [revert_colorbox]],
2388 [490, [revert_textcolor]],
2389 [489, [revert_origin]],
2390 [488, [revert_BoxFeatures]],
2391 [487, [revert_newgloss, revert_glossgroup]],
2392 [486, [revert_forest]],
2393 [485, [revert_ex_itemargs]],
2394 [484, [revert_sigplan_doi]],
2395 [483, [revert_georgian]],
2396 [482, [revert_specialchar]],
2397 [481, [revert_phrases]],
2398 [480, [revert_dashes]],
2399 [479, [revert_question_env]],
2400 [478, [revert_beamer_lemma]],
2401 [477, [revert_xarrow]],
2402 [476, [revert_swissgerman]],
2403 [475, [revert_smash]],
2404 [474, [revert_separator]]
2408 if __name__ == "__main__":