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 # Very old LyX files do not have Plain Layout in insets (but Standard).
190 # So we additionally check here if there is no inset boundary
191 # between the previous layout and this one.
192 n = find_token(document.body, "\\end_inset", j, lay[1])
196 lay = get_containing_layout(document.body, j-1)
197 if lay != False and lay[0] == "Standard" \
198 and find_token(document.body, "\\align", lay[1], lay[2]) == -1 \
199 and find_token(document.body, "\\begin_inset VSpace", lay[1], lay[2]) == -1:
200 # reset any text style before inserting the inset
201 content = "\n".join(document.body[lay[1]:lay[2]])
202 for val in list(sty_dict.keys()):
203 if content.find("\\%s" % val) != -1:
204 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
207 document.body[j:j] = parins
208 i = i + len(parins) + 1
214 regexp = re.compile(r'^\\begin_layout (?:(-*)|(\s*))(Separator|EndOfSlide)(?:(-*)|(\s*))$', re.IGNORECASE)
218 i = find_re(document.body, regexp, i)
222 j = find_end_of_layout(document.body, i)
224 document.warning("Malformed LyX document: Missing `\\end_layout'.")
227 lay = get_containing_layout(document.body, j-1)
229 lines = document.body[lay[3]:lay[2]]
233 document.body[i:j+1] = parlay
235 document.body[i+1:i+1] = lines
237 i = i + len(parlay) + len(lines) + 1
240 def revert_separator(document):
241 " Revert separator insets to layout separators "
243 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
244 if document.textclass in beamer_classes:
245 beglaysep = "\\begin_layout Separator"
247 beglaysep = "\\begin_layout --Separator--"
249 parsep = [beglaysep, "", "\\end_layout", ""]
250 comert = ["\\begin_inset ERT", "status collapsed", "",
251 "\\begin_layout Plain Layout", "%", "\\end_layout",
252 "", "\\end_inset", ""]
253 empert = ["\\begin_inset ERT", "status collapsed", "",
254 "\\begin_layout Plain Layout", " ", "\\end_layout",
255 "", "\\end_inset", ""]
259 i = find_token(document.body, "\\begin_inset Separator", i)
263 lay = get_containing_layout(document.body, i)
265 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
272 kind = get_value(document.body, "\\begin_inset Separator", i, i+1, "plain").split()[1]
273 before = document.body[beg+1:i]
274 something_before = len(before) > 0 and len("".join(before)) > 0
275 j = find_end_of_inset(document.body, i)
276 after = document.body[j+1:end]
277 something_after = len(after) > 0 and len("".join(after)) > 0
279 beg = beg + len(before) + 1
280 elif something_before:
281 document.body[i:i] = ["\\end_layout", ""]
289 document.body[beg:j+1] = empert
292 document.body[beg:j+1] = comert
296 if layoutname == "Standard":
297 if not something_before:
298 document.body[beg:j+1] = parsep
300 document.body[i:i] = ["", "\\begin_layout Standard"]
303 document.body[beg:j+1] = ["\\begin_layout Standard"]
306 document.body[beg:j+1] = ["\\begin_deeper"]
308 end = end + 1 - (j + 1 - beg)
309 if not something_before:
310 document.body[i:i] = parsep
312 end = end + len(parsep)
313 document.body[i:i] = ["\\begin_layout Standard"]
314 document.body[end+2:end+2] = ["", "\\end_deeper", ""]
317 next_par_is_aligned = False
318 k = find_nonempty_line(document.body, end+1)
319 if k != -1 and check_token(document.body[k], "\\begin_layout"):
320 lay = get_containing_layout(document.body, k)
321 next_par_is_aligned = lay != False and \
322 find_token(document.body, "\\align", lay[1], lay[2]) != -1
323 if k != -1 and not next_par_is_aligned \
324 and not check_token(document.body[k], "\\end_deeper") \
325 and not check_token(document.body[k], "\\begin_deeper"):
326 if layoutname == "Standard":
327 document.body[beg:j+1] = [beglaysep]
330 document.body[beg:j+1] = ["\\begin_deeper", beglaysep]
331 end = end + 2 - (j + 1 - beg)
332 document.body[end+1:end+1] = ["", "\\end_deeper", ""]
336 del document.body[i:end+1]
338 del document.body[i:end-1]
343 def convert_parbreak(document):
345 Convert parbreak separators not specifically used to separate
346 environments to latexpar separators.
348 parbreakinset = "\\begin_inset Separator parbreak"
351 i = find_token(document.body, parbreakinset, i)
354 lay = get_containing_layout(document.body, i)
356 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
359 if lay[0] == "Standard":
360 # Convert only if not alone in the paragraph
361 k1 = find_nonempty_line(document.body, lay[1] + 1, i + 1)
362 k2 = find_nonempty_line(document.body, i + 1, lay[2])
363 if (k1 < i) or (k2 > i + 1) or not check_token(document.body[i], parbreakinset):
364 document.body[i] = document.body[i].replace("parbreak", "latexpar")
366 document.body[i] = document.body[i].replace("parbreak", "latexpar")
370 def revert_parbreak(document):
372 Revert latexpar separators to parbreak separators.
376 i = find_token(document.body, "\\begin_inset Separator latexpar", i)
379 document.body[i] = document.body[i].replace("latexpar", "parbreak")
383 def revert_smash(document):
384 " Set amsmath to on if smash commands are used "
386 commands = ["smash[t]", "smash[b]", "notag"]
387 i = find_token(document.header, "\\use_package amsmath", 0)
389 document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
391 value = get_value(document.header, "\\use_package amsmath", i).split()[1]
393 # nothing to do if package is not auto but on or off
397 j = find_token(document.body, '\\begin_inset Formula', j)
400 k = find_end_of_inset(document.body, j)
402 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
405 code = "\n".join(document.body[j:k])
407 if code.find("\\%s" % c) != -1:
408 # set amsmath to on, since it is loaded by the newer format
409 document.header[i] = "\\use_package amsmath 2"
414 def revert_swissgerman(document):
415 " Set language german-ch-old to german "
417 if document.language == "german-ch-old":
418 document.language = "german"
419 i = find_token(document.header, "\\language", 0)
421 document.header[i] = "\\language german"
424 j = find_token(document.body, "\\lang german-ch-old", j)
427 document.body[j] = document.body[j].replace("\\lang german-ch-old", "\\lang german")
431 def revert_use_package(document, pkg, commands, oldauto, supported):
432 # oldauto defines how the version we are reverting to behaves:
433 # if it is true, the old version uses the package automatically.
434 # if it is false, the old version never uses the package.
435 # If "supported" is true, the target version also supports this
437 regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
438 p = find_re(document.header, regexp, 0)
439 value = "1" # default is auto
441 value = get_value(document.header, "\\use_package" , p).split()[1]
443 del document.header[p]
444 if value == "2" and not supported: # on
445 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
446 elif value == "1" and not oldauto: # auto
449 i = find_token(document.body, '\\begin_inset Formula', i)
452 j = find_end_of_inset(document.body, i)
454 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
457 code = "\n".join(document.body[i:j])
459 if code.find("\\%s" % c) != -1:
461 document.header[p] = "\\use_package " + pkg + " 2"
463 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
468 mathtools_commands = ["xhookrightarrow", "xhookleftarrow", "xRightarrow", \
469 "xrightharpoondown", "xrightharpoonup", "xrightleftharpoons", \
470 "xLeftarrow", "xleftharpoondown", "xleftharpoonup", \
471 "xleftrightarrow", "xLeftrightarrow", "xleftrightharpoons", \
474 def revert_xarrow(document):
475 "remove use_package mathtools"
476 revert_use_package(document, "mathtools", mathtools_commands, False, True)
479 def revert_beamer_lemma(document):
480 " Reverts beamer lemma layout to ERT "
482 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
483 if document.textclass not in beamer_classes:
489 i = find_token(document.body, "\\begin_layout Lemma", i)
492 j = find_end_of_layout(document.body, i)
494 document.warning("Malformed LyX document: Can't find end of Lemma layout")
497 arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j)
498 endarg1 = find_end_of_inset(document.body, arg1)
499 arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j)
500 endarg2 = find_end_of_inset(document.body, arg2)
504 beginPlain1 = find_token(document.body, "\\begin_layout Plain Layout", arg1, endarg1)
505 if beginPlain1 == -1:
506 document.warning("Malformed LyX document: Can't find arg1 plain Layout")
509 endPlain1 = find_end_of_inset(document.body, beginPlain1)
510 content1 = document.body[beginPlain1 + 1 : endPlain1 - 2]
511 subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">")
513 beginPlain2 = find_token(document.body, "\\begin_layout Plain Layout", arg2, endarg2)
514 if beginPlain2 == -1:
515 document.warning("Malformed LyX document: Can't find arg2 plain Layout")
518 endPlain2 = find_end_of_inset(document.body, beginPlain2)
519 content2 = document.body[beginPlain2 + 1 : endPlain2 - 2]
520 subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]")
524 del document.body[arg2 : endarg2 + 1]
526 del document.body[arg1 : endarg1 + 1]
528 del document.body[arg1 : endarg1 + 1]
530 del document.body[arg2 : endarg2 + 1]
532 # index of end layout has probably changed
533 j = find_end_of_layout(document.body, i)
535 document.warning("Malformed LyX document: Can't find end of Lemma layout")
541 # if this is not a consecutive env, add start command
543 begcmd = put_cmd_in_ert("\\begin{lemma}")
545 # has this a consecutive lemma?
546 consecutive = document.body[j + 2] == "\\begin_layout Lemma"
548 # if this is not followed by a consecutive env, add end command
550 document.body[j : j + 1] = put_cmd_in_ert("\\end{lemma}") + ["\\end_layout"]
552 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd + subst1 + subst2
558 def revert_question_env(document):
560 Reverts question and question* environments of
561 theorems-ams-extended-bytype module to ERT
564 # Do we use theorems-ams-extended-bytype module?
565 if not "theorems-ams-extended-bytype" in document.get_module_list():
571 i = find_token(document.body, "\\begin_layout Question", i)
575 starred = document.body[i] == "\\begin_layout Question*"
577 j = find_end_of_layout(document.body, i)
579 document.warning("Malformed LyX document: Can't find end of Question layout")
583 # if this is not a consecutive env, add start command
587 begcmd = put_cmd_in_ert("\\begin{question*}")
589 begcmd = put_cmd_in_ert("\\begin{question}")
591 # has this a consecutive theorem of same type?
594 consecutive = document.body[j + 2] == "\\begin_layout Question*"
596 consecutive = document.body[j + 2] == "\\begin_layout Question"
598 # if this is not followed by a consecutive env, add end command
601 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
603 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
605 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
607 add_to_preamble(document, "\\providecommand{\questionname}{Question}")
610 add_to_preamble(document, "\\theoremstyle{plain}\n" \
611 "\\newtheorem*{question*}{\\protect\\questionname}")
613 add_to_preamble(document, "\\theoremstyle{plain}\n" \
614 "\\newtheorem{question}{\\protect\\questionname}")
619 def convert_dashes(document):
620 "convert -- and --- to \\twohyphens and \\threehyphens"
622 if document.backend != "latex":
626 while i < len(document.body):
627 words = document.body[i].split()
628 if (len(words) > 1 and words[0] == "\\begin_inset"
629 and (words[1] in ["CommandInset", "ERT", "External", "Formula",
630 "FormulaMacro", "Graphics", "IPA", "listings"]
631 or ' '.join(words[1:]) == "Flex Code")):
632 # must not replace anything in insets that store LaTeX contents in .lyx files
633 # (math and command insets without overridden read() and write() methods
634 # filtering out IPA makes Text::readParToken() more simple
635 # skip ERT as well since it is not needed there
636 # Flex Code is logical markup, typically rendered as typewriter
637 j = find_end_of_inset(document.body, i)
639 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
644 if document.body[i] == "\\begin_layout LyX-Code":
645 j = find_end_of_layout(document.body, i)
647 document.warning("Malformed LyX document: "
648 "Can't find end of %s layout at line %d" % (words[1],i))
654 if len(words) > 0 and words[0] in ["\\leftindent", "\\paragraph_spacing", "\\align", "\\labelwidthstring"]:
655 # skip paragraph parameters (bug 10243)
659 j = document.body[i].find("--")
662 front = document.body[i][:j]
663 back = document.body[i][j+2:]
664 # We can have an arbitrary number of consecutive hyphens.
665 # These must be split into the corresponding number of two and three hyphens
666 # We must match what LaTeX does: First try emdash, then endash, then single hyphen
667 if back.find("-") == 0:
670 document.body.insert(i+1, back)
671 document.body[i] = front + "\\threehyphens"
674 document.body.insert(i+1, back)
675 document.body[i] = front + "\\twohyphens"
679 def revert_dashes(document):
680 "convert \\twohyphens and \\threehyphens to -- and ---"
682 # eventually remove preamble code from 2.3->2.2 conversion:
683 for i, line in enumerate(document.preamble):
684 if i > 1 and line == r'\renewcommand{\textemdash}{---}':
685 if (document.preamble[i-1] == r'\renewcommand{\textendash}{--}'
686 and document.preamble[i-2] == '% Added by lyx2lyx'):
687 del document.preamble[i-2:i+1]
689 while i < len(document.body):
690 words = document.body[i].split()
691 if len(words) > 1 and words[0] == "\\begin_inset" and \
692 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
694 j = find_end_of_inset(document.body, i)
696 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
702 if document.body[i].find("\\twohyphens") >= 0:
703 document.body[i] = document.body[i].replace("\\twohyphens", "--")
705 if document.body[i].find("\\threehyphens") >= 0:
706 document.body[i] = document.body[i].replace("\\threehyphens", "---")
708 if replaced and i+1 < len(document.body) and \
709 (document.body[i+1].find("\\") != 0 or \
710 document.body[i+1].find("\\twohyphens") == 0 or
711 document.body[i+1].find("\\threehyphens") == 0) and \
712 len(document.body[i]) + len(document.body[i+1]) <= 80:
713 document.body[i] = document.body[i] + document.body[i+1]
714 document.body[i+1:i+2] = []
719 # order is important for the last three!
720 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
722 def is_part_of_converted_phrase(line, j, phrase):
723 "is phrase part of an already converted phrase?"
725 converted = "\\SpecialCharNoPassThru \\" + p
726 pos = j + len(phrase) - len(converted)
728 if line[pos:pos+len(converted)] == converted:
733 def convert_phrases(document):
734 "convert special phrases from plain text to \\SpecialCharNoPassThru"
736 if document.backend != "latex":
739 for phrase in phrases:
741 while i < len(document.body):
742 if document.body[i] and document.body[i][0] == "\\":
743 words = document.body[i].split()
744 if len(words) > 1 and words[0] == "\\begin_inset" and \
745 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
746 # must not replace anything in insets that store LaTeX contents in .lyx files
747 # (math and command insets without overridden read() and write() methods)
748 j = find_end_of_inset(document.body, i)
750 document.warning("Malformed LyX document: Can't find end of inset at line " + str(i))
757 j = document.body[i].find(phrase)
761 if not is_part_of_converted_phrase(document.body[i], j, phrase):
762 front = document.body[i][:j]
763 back = document.body[i][j+len(phrase):]
765 document.body.insert(i+1, back)
766 # We cannot use SpecialChar since we do not know whether we are outside passThru
767 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
771 def revert_phrases(document):
772 "revert special phrases to plain text"
775 while i < len(document.body):
776 words = document.body[i].split()
777 if len(words) > 1 and words[0] == "\\begin_inset" and \
778 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
779 # see convert_phrases
780 j = find_end_of_inset(document.body, i)
782 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
788 for phrase in phrases:
789 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
790 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
791 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
793 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
794 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
796 if replaced and i+1 < len(document.body) and \
797 (document.body[i+1].find("\\") != 0 or \
798 document.body[i+1].find("\\SpecialChar") == 0) and \
799 len(document.body[i]) + len(document.body[i+1]) <= 80:
800 document.body[i] = document.body[i] + document.body[i+1]
801 document.body[i+1:i+2] = []
806 def convert_specialchar_internal(document, forward):
807 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
808 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
809 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
810 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
811 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
812 "\\LaTeX":"LaTeX" # must be after LaTeX2e
816 while i < len(document.body):
817 if document.body[i] and document.body[i][0] == "\\":
818 words = document.body[i].split()
819 if len(words) > 1 and words[0] == "\\begin_inset" and \
820 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
821 # see convert_phrases
822 j = find_end_of_inset(document.body, i)
824 document.warning("Malformed LyX document: Can't find end of %s inset at line %d" % (words[1], i))
830 if not "\\SpecialChar" in document.body[i]:
833 for key, value in specialchars.items():
835 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
836 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
838 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
839 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
843 def convert_specialchar(document):
844 "convert special characters to new syntax"
845 convert_specialchar_internal(document, True)
848 def revert_specialchar(document):
849 "convert special characters to old syntax"
850 convert_specialchar_internal(document, False)
853 def revert_georgian(document):
854 "Set the document language to English but assure Georgian output"
856 if document.language == "georgian":
857 document.language = "english"
858 i = find_token(document.header, "\\language georgian", 0)
860 document.header[i] = "\\language english"
861 j = find_token(document.header, "\\language_package default", 0)
863 document.header[j] = "\\language_package babel"
864 k = find_token(document.header, "\\options", 0)
866 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
868 l = find_token(document.header, "\\use_default_options", 0)
869 document.header.insert(l + 1, "\\options georgian")
872 def revert_sigplan_doi(document):
873 " Reverts sigplanconf DOI layout to ERT "
875 if document.textclass != "sigplanconf":
880 i = find_token(document.body, "\\begin_layout DOI", i)
883 j = find_end_of_layout(document.body, i)
885 document.warning("Malformed LyX document: Can't find end of DOI layout")
889 content = lyx2latex(document, document.body[i:j + 1])
890 add_to_preamble(document, ["\\doi{" + content + "}"])
891 del document.body[i:j + 1]
895 def revert_ex_itemargs(document):
896 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
898 if not "linguistics" in document.get_module_list():
902 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
904 i = find_token(document.body, "\\begin_inset Argument item:", i)
907 j = find_end_of_inset(document.body, i)
908 # Find containing paragraph layout
909 parent = get_containing_layout(document.body, i)
911 document.warning("Malformed LyX document: Can't find parent paragraph layout")
915 layoutname = parent[0]
916 if layoutname in example_layouts:
917 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
918 endPlain = find_end_of_layout(document.body, beginPlain)
919 content = document.body[beginPlain + 1 : endPlain]
920 del document.body[i:j+1]
921 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
922 document.body[parbeg : parbeg] = subst
926 def revert_forest(document):
927 " Reverts the forest environment (Linguistics module) to TeX-code "
929 if not "linguistics" in document.get_module_list():
934 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
937 j = find_end_of_inset(document.body, i)
939 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
943 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
944 endPlain = find_end_of_layout(document.body, beginPlain)
945 content = lyx2latex(document, document.body[beginPlain : endPlain])
947 add_to_preamble(document, ["\\usepackage{forest}"])
949 document.body[i:j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
953 def revert_glossgroup(document):
954 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
956 if not "linguistics" in document.get_module_list():
961 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
964 j = find_end_of_inset(document.body, i)
966 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
970 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
971 endPlain = find_end_of_layout(document.body, beginPlain)
972 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
974 document.body[i:j + 1] = ["{", "", content, "", "}"]
978 def revert_newgloss(document):
979 " Reverts the new Glosse insets (Linguistics module) to the old format "
981 if not "linguistics" in document.get_module_list():
984 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
985 for glosse in glosses:
988 i = find_token(document.body, glosse, i)
991 j = find_end_of_inset(document.body, i)
993 document.warning("Malformed LyX document: Can't find end of Glosse inset")
997 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
998 endarg = find_end_of_inset(document.body, arg)
1001 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
1002 if argbeginPlain == -1:
1003 document.warning("Malformed LyX document: Can't find arg plain Layout")
1006 argendPlain = find_end_of_inset(document.body, argbeginPlain)
1007 argcontent = lyx2verbatim(document, document.body[argbeginPlain : argendPlain - 2])
1009 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
1010 argcontent, "\\end_layout"]
1012 # remove Arg insets and paragraph, if it only contains this inset
1013 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
1014 del document.body[arg - 1 : endarg + 4]
1016 del document.body[arg : endarg + 1]
1018 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1019 endPlain = find_end_of_layout(document.body, beginPlain)
1020 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
1022 document.body[beginPlain + 1:endPlain] = [content]
1025 # Dissolve ERT insets
1026 for glosse in glosses:
1029 i = find_token(document.body, glosse, i)
1032 j = find_end_of_inset(document.body, i)
1034 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1038 ert = find_token(document.body, "\\begin_inset ERT", i, j)
1041 ertend = find_end_of_inset(document.body, ert)
1043 document.warning("Malformed LyX document: Can't find end of ERT inset")
1046 ertcontent = get_ert(document.body, ert, True)
1047 document.body[ert : ertend + 1] = [ertcontent]
1051 def convert_newgloss(document):
1052 " Converts Glosse insets (Linguistics module) to the new format "
1054 if not "linguistics" in document.get_module_list():
1057 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1058 for glosse in glosses:
1061 i = find_token(document.body, glosse, i)
1064 j = find_end_of_inset(document.body, i)
1066 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1073 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
1074 if beginPlain == -1:
1076 endPlain = find_end_of_layout(document.body, beginPlain)
1078 document.warning("Malformed LyX document: Can't find end of Glosse layout")
1082 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
1083 if glt != -1 and document.body[glt + 1].startswith("glt"):
1084 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1085 argcontent = document.body[glt + 1 : endPlain]
1086 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
1087 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
1088 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
1089 "\\end_layout", "", "\\end_inset"]
1091 content = document.body[beginPlain + 1 : endPlain]
1092 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
1093 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
1095 endPlain = find_end_of_layout(document.body, beginPlain)
1097 j = find_end_of_inset(document.body, i)
1102 def convert_BoxFeatures(document):
1103 " adds new box features "
1107 i = find_token(document.body, "height_special", i)
1110 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1114 def revert_BoxFeatures(document):
1115 " outputs new box features as TeX code "
1119 defaultThick = "0.4pt"
1120 defaultShadow = "4pt"
1122 i = find_token(document.body, "thickness", i)
1125 binset = find_token(document.body, "\\begin_inset Box", i - 11)
1126 if binset == -1 or binset != i - 11:
1128 continue # then "thickness" is is just a word in the text
1129 einset = find_end_of_inset(document.body, binset)
1131 document.warning("Malformed LyX document: Can't find end of box inset!")
1134 # read out the values
1135 beg = document.body[i].find('"');
1136 end = document.body[i].rfind('"');
1137 thickness = document.body[i][beg+1:end];
1138 beg = document.body[i+1].find('"');
1139 end = document.body[i+1].rfind('"');
1140 separation = document.body[i+1][beg+1:end];
1141 beg = document.body[i+2].find('"');
1142 end = document.body[i+2].rfind('"');
1143 shadowsize = document.body[i+2][beg+1:end];
1144 # delete the specification
1145 del document.body[i:i+3]
1147 # first output the closing brace
1148 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1149 document.body[einset -1 : einset - 1] = put_cmd_in_ert("}")
1150 # we have now the problem that if there is already \(f)colorbox in ERT around the inset
1151 # the ERT from this routine must be around it
1152 regexp = re.compile(r'^.*colorbox{.*$')
1153 pos = find_re(document.body, regexp, binset - 4)
1154 if pos != -1 and pos == binset - 4:
1158 # now output the lengths
1159 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1160 document.body[pos : pos] = put_cmd_in_ert("{")
1161 if thickness != defaultThick:
1162 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness]
1163 if separation != defaultSep and thickness == defaultThick:
1164 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation]
1165 if separation != defaultSep and thickness != defaultThick:
1166 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1167 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1168 document.body[pos + 5 : pos +6] = ["{\\backslash shadowsize " + shadowsize]
1169 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1170 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1171 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1172 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1173 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1174 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1177 def convert_origin(document):
1178 " Insert the origin tag "
1180 i = find_token(document.header, "\\textclass ", 0)
1182 document.warning("Malformed LyX document: No \\textclass!!")
1184 if document.dir == u'':
1188 if document.systemlyxdir and document.systemlyxdir != u'':
1190 if os.path.isabs(document.dir):
1191 absdir = os.path.normpath(document.dir)
1193 absdir = os.path.normpath(os.path.abspath(document.dir))
1194 if os.path.isabs(document.systemlyxdir):
1195 abssys = os.path.normpath(document.systemlyxdir)
1197 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1198 relpath = os.path.relpath(absdir, abssys)
1199 if relpath.find(u'..') == 0:
1204 origin = document.dir.replace(u'\\', u'/') + u'/'
1206 origin = os.path.join(u"/systemlyxdir", relpath).replace(u'\\', u'/') + u'/'
1207 document.header[i:i] = ["\\origin " + origin]
1210 def revert_origin(document):
1211 " Remove the origin tag "
1213 i = find_token(document.header, "\\origin ", 0)
1215 document.warning("Malformed LyX document: No \\origin!!")
1217 del document.header[i]
1220 color_names = ["brown", "darkgray", "gray", \
1221 "lightgray", "lime", "olive", "orange", \
1222 "pink", "purple", "teal", "violet"]
1224 def revert_textcolor(document):
1225 " revert new \\textcolor colors to TeX code "
1231 i = find_token(document.body, "\\color ", i)
1235 for color in list(color_names):
1236 if document.body[i] == "\\color " + color:
1237 # register that xcolor must be loaded in the preamble
1240 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1241 # find the next \\color and/or the next \\end_layout
1242 j = find_token(document.body, "\\color", i + 1)
1243 k = find_token(document.body, "\\end_layout", i + 1)
1244 if j == -1 and k != -1:
1247 # first output the closing brace
1249 document.body[k: k] = put_cmd_in_ert("}")
1251 document.body[j: j] = put_cmd_in_ert("}")
1252 # now output the \textcolor command
1253 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1257 def convert_colorbox(document):
1258 " adds color settings for boxes "
1262 i = find_token(document.body, "shadowsize", i)
1265 document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1269 def revert_colorbox(document):
1270 " outputs color settings for boxes as TeX code "
1273 defaultframecolor = "black"
1274 defaultbackcolor = "none"
1276 i = find_token(document.body, "framecolor", i)
1279 binset = find_token(document.body, "\\begin_inset Box", i - 14)
1282 einset = find_end_of_inset(document.body, binset)
1284 document.warning("Malformed LyX document: Can't find end of box inset!")
1286 # read out the values
1287 beg = document.body[i].find('"');
1288 end = document.body[i].rfind('"');
1289 framecolor = document.body[i][beg+1:end];
1290 beg = document.body[i + 1].find('"');
1291 end = document.body[i + 1].rfind('"');
1292 backcolor = document.body[i+1][beg+1:end];
1293 # delete the specification
1294 del document.body[i:i + 2]
1296 # first output the closing brace
1297 if framecolor != defaultframecolor or backcolor != defaultbackcolor:
1298 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1299 document.body[einset : einset] = put_cmd_in_ert("}")
1300 # determine the box type
1301 isBox = find_token(document.body, "\\begin_inset Box Boxed", binset)
1302 # now output the box commands
1303 if (framecolor != defaultframecolor and isBox == binset) or (backcolor != defaultbackcolor and isBox == binset):
1304 document.body[i - 14 : i - 14] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1305 # in the case we must also change the box type because the ERT code adds a frame
1306 document.body[i - 4] = "\\begin_inset Box Frameless"
1307 # if has_inner_box 0 we must set it and use_makebox to 1
1308 ibox = find_token(document.body, "has_inner_box", i - 4)
1309 if ibox == -1 or ibox != i - 1:
1310 document.warning("Malformed LyX document: Can't find has_inner_box statement!")
1312 # read out the value
1313 innerbox = document.body[ibox][-1:];
1315 document.body[ibox] = "has_inner_box 1"
1316 document.body[ibox + 3] = "use_makebox 1"
1317 if backcolor != defaultbackcolor and isBox != binset:
1318 document.body[i - 14 : i - 14] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
1321 def revert_mathmulticol(document):
1322 " Convert formulas to ERT if they contain multicolumns "
1326 i = find_token(document.body, '\\begin_inset Formula', i)
1329 j = find_end_of_inset(document.body, i)
1331 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1334 lines = document.body[i:j]
1335 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1336 code = "\n".join(lines)
1341 n = code.find("\\multicolumn", k)
1342 # no need to convert degenerated multicolumn cells,
1343 # they work in old LyX versions as "math ERT"
1344 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1345 ert = put_cmd_in_ert(code)
1346 document.body[i:j+1] = ert
1352 i = find_end_of_inset(document.body, i)
1357 def revert_jss(document):
1358 " Reverts JSS In_Preamble commands to ERT in preamble "
1360 if document.textclass != "jss":
1369 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1370 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1373 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1375 endh = find_end_of_inset(document.body, h)
1376 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1377 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1381 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1383 endm = find_end_of_inset(document.body, m)
1384 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1385 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1389 j = find_token(document.body, "\\begin_inset Flex Code", j)
1391 # assure that we are not in a Code Chunk inset
1392 if document.body[j][-1] == "e":
1393 endj = find_end_of_inset(document.body, j)
1394 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1395 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1401 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1403 endk = find_end_of_inset(document.body, k)
1404 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1405 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1409 n = find_token(document.body, "\\begin_inset Flex URL", n)
1411 endn = find_end_of_inset(document.body, n)
1412 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1413 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1415 # now revert the In_Preamble layouts
1417 i = find_token(document.body, "\\begin_layout Title", 0)
1420 j = find_end_of_layout(document.body, i)
1422 document.warning("Malformed LyX document: Can't find end of Title layout")
1425 content = lyx2latex(document, document.body[i:j + 1])
1426 add_to_preamble(document, ["\\title{" + content + "}"])
1427 del document.body[i:j + 1]
1429 i = find_token(document.body, "\\begin_layout Author", 0)
1432 j = find_end_of_layout(document.body, i)
1434 document.warning("Malformed LyX document: Can't find end of Author layout")
1437 content = lyx2latex(document, document.body[i:j + 1])
1438 add_to_preamble(document, ["\\author{" + content + "}"])
1439 del document.body[i:j + 1]
1441 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1444 j = find_end_of_layout(document.body, i)
1446 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1449 content = lyx2latex(document, document.body[i:j + 1])
1450 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1451 del document.body[i:j + 1]
1453 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1456 j = find_end_of_layout(document.body, i)
1458 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1461 content = lyx2latex(document, document.body[i:j + 1])
1462 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1463 del document.body[i:j + 1]
1465 i = find_token(document.body, "\\begin_layout Short Title", 0)
1468 j = find_end_of_layout(document.body, i)
1470 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1473 content = lyx2latex(document, document.body[i:j + 1])
1474 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1475 del document.body[i:j + 1]
1477 i = find_token(document.body, "\\begin_layout Abstract", 0)
1480 j = find_end_of_layout(document.body, i)
1482 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1485 content = lyx2latex(document, document.body[i:j + 1])
1486 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1487 del document.body[i:j + 1]
1489 i = find_token(document.body, "\\begin_layout Keywords", 0)
1492 j = find_end_of_layout(document.body, i)
1494 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1497 content = lyx2latex(document, document.body[i:j + 1])
1498 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1499 del document.body[i:j + 1]
1501 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1504 j = find_end_of_layout(document.body, i)
1506 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1509 content = lyx2latex(document, document.body[i:j + 1])
1510 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1511 del document.body[i:j + 1]
1513 i = find_token(document.body, "\\begin_layout Address", 0)
1516 j = find_end_of_layout(document.body, i)
1518 document.warning("Malformed LyX document: Can't find end of Address layout")
1521 content = lyx2latex(document, document.body[i:j + 1])
1522 add_to_preamble(document, ["\\Address{" + content + "}"])
1523 del document.body[i:j + 1]
1524 # finally handle the code layouts
1529 while m != -1 or j != -1 or h != -1 or k != -1:
1532 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1534 endh = find_end_of_inset(document.body, h)
1535 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1536 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1537 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1541 j = find_token(document.body, "\\begin_layout Code Input", j)
1543 endj = find_end_of_layout(document.body, j)
1544 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1545 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1546 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1547 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1548 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1552 k = find_token(document.body, "\\begin_layout Code Output", k)
1554 endk = find_end_of_layout(document.body, k)
1555 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1556 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1557 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1558 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1559 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1563 m = find_token(document.body, "\\begin_layout Code", m)
1565 endm = find_end_of_layout(document.body, m)
1566 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1567 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1568 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1569 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1570 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1574 def convert_subref(document):
1575 " converts sub: ref prefixes to subref: "
1578 rx = re.compile(r'^name \"sub:(.+)$')
1581 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1584 j = find_end_of_inset(document.body, i)
1586 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1590 for p in range(i, j):
1591 m = rx.match(document.body[p])
1594 document.body[p] = "name \"subsec:" + label
1598 rx = re.compile(r'^reference \"sub:(.+)$')
1601 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1604 j = find_end_of_inset(document.body, i)
1606 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1610 for p in range(i, j):
1611 m = rx.match(document.body[p])
1614 document.body[p] = "reference \"subsec:" + label
1620 def revert_subref(document):
1621 " reverts subref: ref prefixes to sub: "
1624 rx = re.compile(r'^name \"subsec:(.+)$')
1627 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1630 j = find_end_of_inset(document.body, i)
1632 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1636 for p in range(i, j):
1637 m = rx.match(document.body[p])
1640 document.body[p] = "name \"sub:" + label
1645 rx = re.compile(r'^reference \"subsec:(.+)$')
1648 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1651 j = find_end_of_inset(document.body, i)
1653 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1657 for p in range(i, j):
1658 m = rx.match(document.body[p])
1661 document.body[p] = "reference \"sub:" + label
1666 def convert_nounzip(document):
1667 " remove the noUnzip parameter of graphics insets "
1669 rx = re.compile(r'\s*noUnzip\s*$')
1672 i = find_token(document.body, "\\begin_inset Graphics", i)
1675 j = find_end_of_inset(document.body, i)
1677 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1681 k = find_re(document.body, rx, i, j)
1683 del document.body[k]
1688 def convert_revert_external_bbox(document, forward):
1689 " add units to bounding box of external insets "
1691 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1694 i = find_token(document.body, "\\begin_inset External", i)
1697 j = find_end_of_inset(document.body, i)
1699 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1702 k = find_re(document.body, rx, i, j)
1706 tokens = document.body[k].split()
1708 for t in range(1, 5):
1711 for t in range(1, 5):
1712 tokens[t] = length_in_bp(tokens[t])
1713 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1714 tokens[3] + " " + tokens[4]
1718 def convert_external_bbox(document):
1719 convert_revert_external_bbox(document, True)
1722 def revert_external_bbox(document):
1723 convert_revert_external_bbox(document, False)
1726 def revert_tcolorbox_1(document):
1727 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1730 i = find_token(document.header, "tcolorbox", i)
1736 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1739 flexEnd = find_end_of_inset(document.body, flex)
1740 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1741 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1742 flexEnd = find_end_of_inset(document.body, flex)
1744 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1746 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1747 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1751 def revert_tcolorbox_2(document):
1752 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1755 i = find_token(document.header, "tcolorbox", i)
1761 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1764 flexEnd = find_end_of_inset(document.body, flex)
1765 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1766 flexEnd = find_end_of_inset(document.body, flex)
1767 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1768 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1772 def revert_tcolorbox_3(document):
1773 " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1776 i = find_token(document.header, "tcolorbox", i)
1782 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1785 flexEnd = find_end_of_inset(document.body, flex)
1786 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1787 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1788 flexEnd = find_end_of_inset(document.body, flex)
1789 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1790 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1794 def revert_tcolorbox_4(document):
1795 " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1798 i = find_token(document.header, "tcolorbox", i)
1804 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1807 flexEnd = find_end_of_inset(document.body, flex)
1808 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1809 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1810 flexEnd = find_end_of_inset(document.body, flex)
1811 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1812 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1816 def revert_tcolorbox_5(document):
1817 " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1820 i = find_token(document.header, "tcolorbox", i)
1826 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1829 flexEnd = find_end_of_inset(document.body, flex)
1830 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1831 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1832 flexEnd = find_end_of_inset(document.body, flex)
1833 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1834 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1838 def revert_tcolorbox_6(document):
1839 " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1842 i = find_token(document.header, "tcolorbox", i)
1848 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1851 flexEnd = find_end_of_inset(document.body, flex)
1852 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1853 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1854 flexEnd = find_end_of_inset(document.body, flex)
1855 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1856 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1860 def revert_tcolorbox_7(document):
1861 " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1864 i = find_token(document.header, "tcolorbox", i)
1870 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1873 flexEnd = find_end_of_inset(document.body, flex)
1874 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1875 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1876 flexEnd = find_end_of_inset(document.body, flex)
1877 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1878 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1882 def revert_tcolorbox_8(document):
1883 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1889 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1891 j = find_end_of_layout(document.body, i)
1892 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1893 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1894 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1895 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1897 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1899 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1900 k = find_end_of_inset(document.body, j)
1901 k = find_token(document.body, "\\end_inset", k + 1)
1902 k = find_token(document.body, "\\end_inset", k + 1)
1904 k = find_token(document.body, "\\end_inset", k + 1)
1905 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1906 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1907 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1913 def revert_moderncv_1(document):
1914 " Reverts the new inset of moderncv to TeX-code in preamble "
1916 if document.textclass != "moderncv":
1922 # at first revert the new styles
1924 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1927 j = find_end_of_layout(document.body, i)
1929 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1932 content = lyx2latex(document, document.body[i:j + 1])
1933 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1934 del document.body[i:j + 1]
1936 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1939 j = find_end_of_layout(document.body, i)
1941 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1944 content = lyx2latex(document, document.body[i:j + 1])
1945 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1946 del document.body[i:j + 1]
1947 # now change the new styles to the obsolete ones
1949 i = find_token(document.body, "\\begin_layout Name", 0)
1952 j = find_end_of_layout(document.body, i)
1954 document.warning("Malformed LyX document: Can't find end of Name layout")
1957 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1958 if lineArg > j and j != 0:
1961 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1962 # we have to assure that no other inset is in the Argument
1963 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1964 endInset = find_token(document.body, "\\end_inset", beginPlain)
1967 while beginInset < endInset and beginInset != -1:
1968 beginInset = find_token(document.body, "\\begin_inset", k)
1969 endInset = find_token(document.body, "\\end_inset", l)
1972 Arg2 = document.body[l + 5 : l + 6]
1974 document.body[i : i + 1]= ["\\begin_layout FirstName"]
1975 # delete the Argument inset
1976 del( document.body[endInset - 2 : endInset + 3])
1977 del( document.body[lineArg : beginPlain + 1])
1978 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1981 def revert_moderncv_2(document):
1982 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1984 if document.textclass != "moderncv":
1991 i = find_token(document.body, "\\begin_layout Phone", i)
1994 j = find_end_of_layout(document.body, i)
1996 document.warning("Malformed LyX document: Can't find end of Phone layout")
1999 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
2000 if lineArg > j and j != 0:
2004 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
2005 # we have to assure that no other inset is in the Argument
2006 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
2007 endInset = find_token(document.body, "\\end_inset", beginPlain)
2010 while beginInset < endInset and beginInset != -1:
2011 beginInset = find_token(document.body, "\\begin_inset", k)
2012 endInset = find_token(document.body, "\\end_inset", l)
2015 Arg = document.body[beginPlain + 1 : beginPlain + 2]
2017 if Arg[0] == "mobile":
2018 document.body[i : i + 1]= ["\\begin_layout Mobile"]
2020 document.body[i : i + 1]= ["\\begin_layout Fax"]
2021 # delete the Argument inset
2022 del(document.body[endInset - 2 : endInset + 1])
2023 del(document.body[lineArg : beginPlain + 3])
2027 def convert_moderncv_phone(document):
2028 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
2030 if document.textclass != "moderncv":
2037 "Mobile" : "mobile",
2041 rx = re.compile(r'^\\begin_layout (\S+)$')
2043 # substitute \fax and \mobile by \phone[fax] and \phone[mobile], respectively
2044 i = find_token(document.body, "\\begin_layout", i)
2048 m = rx.match(document.body[i])
2052 if val not in list(phone_dict.keys()):
2055 j = find_end_of_layout(document.body, i)
2057 document.warning("Malformed LyX document: Can't find end of Mobile layout")
2061 document.body[i : i + 1] = ["\\begin_layout Phone", "\\begin_inset Argument 1", "status open", "",
2062 "\\begin_layout Plain Layout", phone_dict[val], "\\end_layout", "",
2066 def convert_moderncv_name(document):
2067 " Convert the FirstName and LastName layout of moderncv to the general Name layout "
2069 if document.textclass != "moderncv":
2072 fnb = 0 # Begin of FirstName inset
2073 fne = 0 # End of FirstName inset
2074 lnb = 0 # Begin of LastName (FamilyName) inset
2075 lne = 0 # End of LastName (FamilyName) inset
2076 nb = 0 # Begin of substituting Name inset
2077 ne = 0 # End of substituting Name inset
2078 FirstName = [] # FirstName content
2079 FamilyName = [] # LastName content
2083 fnb = find_token(document.body, "\\begin_layout FirstName", fnb)
2085 fne = find_end_of_layout(document.body, fnb)
2087 document.warning("Malformed LyX document: Can't find end of FirstName layout")
2089 FirstName = document.body[fnb + 1 : fne]
2091 lnb = find_token(document.body, "\\begin_layout FamilyName", lnb)
2093 lne = find_end_of_layout(document.body, lnb)
2095 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
2097 FamilyName = document.body[lnb + 1 : lne]
2098 # Determine the region for the substituting Name layout
2099 if fnb == -1 and lnb == -1: # Neither FirstName nor FamilyName exists -> Do nothing
2101 elif fnb == -1: # Only FamilyName exists -> New Name insets replaces that
2104 elif lnb == -1: # Only FirstName exists -> New Name insets replaces that
2107 elif fne > lne: # FirstName position before FamilyName -> New Name insets spans
2108 nb = lnb # from FamilyName begin
2109 ne = fne # to FirstName end
2110 else: # FirstName position before FamilyName -> New Name insets spans
2111 nb = fnb # from FirstName begin
2112 ne = lne # to FamilyName end
2114 # Insert the substituting layout now. If FirstName exists, use an otpional argument.
2116 document.body[nb : ne + 1] = ["\\begin_layout Name"] + FamilyName + ["\\end_layout", ""]
2118 document.body[nb : ne + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2119 "\\begin_layout Plain Layout"] + FirstName + ["\\end_layout", "",
2120 "\\end_inset", ""] + FamilyName + ["\\end_layout", ""]
2123 def revert_achemso(document):
2124 " Reverts the flex inset Latin to TeX code "
2126 if document.textclass != "achemso":
2131 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2133 j = find_end_of_inset(document.body, i)
2137 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2138 endPlain = find_end_of_layout(document.body, beginPlain)
2139 content = lyx2latex(document, document.body[beginPlain : endPlain])
2140 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2142 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2147 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2148 "\\font_sf_scale", "\\font_tt_scale"]
2149 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2150 fontquotes = [True, True, True, True, False, False]
2152 def convert_fontsettings(document):
2153 " Duplicate 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 + "!")
2167 # note that with i = -1, this will insert at the end
2169 value = fontdefaults[j]
2171 value = document.header[i][len(f):].strip()
2173 if use_non_tex_fonts == "true":
2174 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2176 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2178 if use_non_tex_fonts == "true":
2179 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2181 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2185 def revert_fontsettings(document):
2186 " Merge font settings "
2188 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2190 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2191 use_non_tex_fonts = "false"
2193 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2195 for f in fontsettings:
2196 i = find_token(document.header, f + " ", 0)
2198 document.warning("Malformed LyX document: No " + f + "!")
2201 line = get_value(document.header, f, i)
2204 q2 = line.find('"', q1+1)
2205 q3 = line.find('"', q2+1)
2206 q4 = line.find('"', q3+1)
2207 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2208 document.warning("Malformed LyX document: Missing quotes!")
2211 if use_non_tex_fonts == "true":
2212 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2214 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2216 if use_non_tex_fonts == "true":
2217 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2219 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2223 def revert_solution(document):
2224 " Reverts the solution environment of the theorem module to TeX code "
2226 # Do we use one of the modules that provides Solution?
2228 mods = document.get_module_list()
2230 if mod == "theorems-std" or mod == "theorems-bytype" \
2231 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2241 i = find_token(document.body, "\\begin_layout Solution", i)
2245 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2246 if is_starred == True:
2248 LyXName = "Solution*"
2249 theoremName = "newtheorem*"
2252 LyXName = "Solution"
2253 theoremName = "newtheorem"
2255 j = find_end_of_layout(document.body, i)
2257 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2261 # if this is not a consecutive env, add start command
2264 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2266 # has this a consecutive theorem of same type?
2267 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2269 # if this is not followed by a consecutive env, add end command
2271 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2273 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2275 add_to_preamble(document, "\\theoremstyle{definition}")
2276 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2277 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2278 (theoremName, LaTeXName))
2279 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2280 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2281 (theoremName, LaTeXName))
2283 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2287 def revert_verbatim_star(document):
2288 from lyx_2_1 import revert_verbatim
2289 revert_verbatim(document, True)
2292 def convert_save_props(document):
2293 " Add save_transient_properties parameter. "
2294 i = find_token(document.header, '\\begin_header', 0)
2296 document.warning("Malformed lyx document: Missing '\\begin_header'.")
2298 document.header.insert(i + 1, '\\save_transient_properties true')
2301 def revert_save_props(document):
2302 " Remove save_transient_properties parameter. "
2303 i = find_token(document.header, "\\save_transient_properties", 0)
2306 del document.header[i]
2309 def convert_info_tabular_feature(document):
2311 return arg.replace("inset-modify tabular", "tabular-feature")
2312 convert_info_insets(document, "shortcut(s)?|icon", f)
2315 def revert_info_tabular_feature(document):
2317 return arg.replace("tabular-feature", "inset-modify tabular")
2318 convert_info_insets(document, "shortcut(s)?|icon", f)
2325 supported_versions = ["2.2.0", "2.2"]
2327 [475, [convert_separator]],
2328 # nothing to do for 476: We consider it a bug that older versions
2329 # did not load amsmath automatically for these commands, and do not
2330 # want to hardcode amsmath off.
2336 [481, [convert_dashes]],
2337 [482, [convert_phrases]],
2338 [483, [convert_specialchar]],
2343 [488, [convert_newgloss]],
2344 [489, [convert_BoxFeatures]],
2345 [490, [convert_origin]],
2347 [492, [convert_colorbox]],
2350 [495, [convert_subref]],
2351 [496, [convert_nounzip]],
2352 [497, [convert_external_bbox]],
2354 [499, [convert_moderncv_phone, convert_moderncv_name]],
2356 [501, [convert_fontsettings]],
2359 [504, [convert_save_props]],
2361 [506, [convert_info_tabular_feature]],
2362 [507, [convert_longtable_label]],
2363 [508, [convert_parbreak]]
2367 [507, [revert_parbreak]],
2368 [506, [revert_longtable_label]],
2369 [505, [revert_info_tabular_feature]],
2371 [503, [revert_save_props]],
2372 [502, [revert_verbatim_star]],
2373 [501, [revert_solution]],
2374 [500, [revert_fontsettings]],
2375 [499, [revert_achemso]],
2376 [498, [revert_moderncv_1, revert_moderncv_2]],
2377 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2378 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2379 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2380 [496, [revert_external_bbox]],
2381 [495, []], # nothing to do since the noUnzip parameter was optional
2382 [494, [revert_subref]],
2383 [493, [revert_jss]],
2384 [492, [revert_mathmulticol]],
2385 [491, [revert_colorbox]],
2386 [490, [revert_textcolor]],
2387 [489, [revert_origin]],
2388 [488, [revert_BoxFeatures]],
2389 [487, [revert_newgloss, revert_glossgroup]],
2390 [486, [revert_forest]],
2391 [485, [revert_ex_itemargs]],
2392 [484, [revert_sigplan_doi]],
2393 [483, [revert_georgian]],
2394 [482, [revert_specialchar]],
2395 [481, [revert_phrases]],
2396 [480, [revert_dashes]],
2397 [479, [revert_question_env]],
2398 [478, [revert_beamer_lemma]],
2399 [477, [revert_xarrow]],
2400 [476, [revert_swissgerman]],
2401 [475, [revert_smash]],
2402 [474, [revert_separator]]
2406 if __name__ == "__main__":