1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # Copyright (C) 2015 The LyX team
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 """ Convert files to the file format generated by lyx 2.2"""
25 # Uncomment only what you need to import, please.
27 #from parser_tools import find_token, find_end_of, find_tokens, \
28 # find_token_exact, find_end_of_inset, find_end_of_layout, \
29 # find_token_backwards, is_in_inset, get_value, get_quoted_value, \
30 # del_token, check_token, get_option_value
32 from lyx2lyx_tools import add_to_preamble, put_cmd_in_ert, get_ert, lyx2latex, \
33 lyx2verbatim, length_in_bp, convert_info_insets
34 # insert_to_preamble, latex_length, revert_flex_inset, \
35 # revert_font_attrs, hex2ratio, str2bool
37 from parser_tools import find_token, find_token_backwards, find_re, \
38 find_end_of_inset, find_end_of_layout, find_nonempty_line, \
39 get_containing_layout, get_value, check_token
41 ####################################################################
42 # Private helper functions
44 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt, nolastopt):
46 Reverts an InsetArgument to TeX-code
48 revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt, notLastOpt)
49 LineOfBegin is the line of the \begin_layout or \begin_inset statement
50 LineOfEnd is the line of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
51 StartArgument is the number of the first argument that needs to be converted
52 EndArgument is the number of the last argument that needs to be converted or the last defined one
53 isEnvironment must be true, if the layout is for a LaTeX environment
54 isOpt must be true, if the argument is an optional one
55 notLastOpt must be true if the argument is mandatory and followed by optional ones
59 while lineArg != -1 and n < nmax + 1:
60 lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
61 if lineArg > endline and endline != 0:
64 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
65 # we have to assure that no other inset is in the Argument
66 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
67 endInset = find_token(document.body, "\\end_inset", beginPlain)
70 while beginInset < endInset and beginInset != -1:
71 beginInset = find_token(document.body, "\\begin_inset", k)
72 endInset = find_token(document.body, "\\end_inset", l)
75 if environment == False:
77 if nolastopt == False:
78 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
80 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
81 del(document.body[lineArg : beginPlain + 1])
84 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
85 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
89 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
90 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
93 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
94 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
100 ###############################################################################
102 ### Conversion and reversion routines
104 ###############################################################################
106 def convert_longtable_label_internal(document, forward):
108 Convert reference to "LongTableNoNumber" into "Unnumbered" if forward is True
111 old_reference = "\\begin_inset Caption LongTableNoNumber"
112 new_reference = "\\begin_inset Caption Unnumbered"
114 # if the purpose is to revert swap the strings roles
116 old_reference, new_reference = new_reference, old_reference
120 i = find_token(document.body, old_reference, i)
125 document.body[i] = new_reference
128 def convert_longtable_label(document):
129 convert_longtable_label_internal(document, True)
132 def revert_longtable_label(document):
133 convert_longtable_label_internal(document, False)
136 def convert_separator(document):
138 Convert layout separators to separator insets and add (LaTeX) paragraph
139 breaks in order to mimic previous LaTeX export.
142 parins = ["\\begin_inset Separator parbreak", "\\end_inset", ""]
143 parlay = ["\\begin_layout Standard", "\\begin_inset Separator parbreak",
144 "\\end_inset", "", "\\end_layout", ""]
146 "family" : "default",
147 "series" : "default",
156 i = find_token(document.body, "\\begin_deeper", i)
160 j = find_token_backwards(document.body, "\\end_layout", i-1)
162 # reset any text style before inserting the inset
163 lay = get_containing_layout(document.body, j-1)
165 content = "\n".join(document.body[lay[1]:lay[2]])
166 for val in list(sty_dict.keys()):
167 if content.find("\\%s" % val) != -1:
168 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
171 document.body[j:j] = parins
172 i = i + len(parins) + 1
178 i = find_token(document.body, "\\align", i)
182 lay = get_containing_layout(document.body, i)
183 if lay != False and lay[0] == "Plain Layout":
187 j = find_token_backwards(document.body, "\\end_layout", i-1)
189 lay = get_containing_layout(document.body, j-1)
190 if lay != False and lay[0] == "Standard" \
191 and find_token(document.body, "\\align", lay[1], lay[2]) == -1 \
192 and find_token(document.body, "\\begin_inset VSpace", lay[1], lay[2]) == -1:
193 # reset any text style before inserting the inset
194 content = "\n".join(document.body[lay[1]:lay[2]])
195 for val in list(sty_dict.keys()):
196 if content.find("\\%s" % val) != -1:
197 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
200 document.body[j:j] = parins
201 i = i + len(parins) + 1
207 regexp = re.compile(r'^\\begin_layout (?:(-*)|(\s*))(Separator|EndOfSlide)(?:(-*)|(\s*))$', re.IGNORECASE)
211 i = find_re(document.body, regexp, i)
215 j = find_end_of_layout(document.body, i)
217 document.warning("Malformed LyX document: Missing `\\end_layout'.")
220 lay = get_containing_layout(document.body, j-1)
222 lines = document.body[lay[3]:lay[2]]
226 document.body[i:j+1] = parlay
228 document.body[i+1:i+1] = lines
230 i = i + len(parlay) + len(lines) + 1
233 def revert_separator(document):
234 " Revert separator insets to layout separators "
236 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
237 if document.textclass in beamer_classes:
238 beglaysep = "\\begin_layout Separator"
240 beglaysep = "\\begin_layout --Separator--"
242 parsep = [beglaysep, "", "\\end_layout", ""]
243 comert = ["\\begin_inset ERT", "status collapsed", "",
244 "\\begin_layout Plain Layout", "%", "\\end_layout",
245 "", "\\end_inset", ""]
246 empert = ["\\begin_inset ERT", "status collapsed", "",
247 "\\begin_layout Plain Layout", " ", "\\end_layout",
248 "", "\\end_inset", ""]
252 i = find_token(document.body, "\\begin_inset Separator", i)
256 lay = get_containing_layout(document.body, i)
258 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
265 kind = get_value(document.body, "\\begin_inset Separator", i, i+1, "plain").split()[1]
266 before = document.body[beg+1:i]
267 something_before = len(before) > 0 and len("".join(before)) > 0
268 j = find_end_of_inset(document.body, i)
269 after = document.body[j+1:end]
270 something_after = len(after) > 0 and len("".join(after)) > 0
272 beg = beg + len(before) + 1
273 elif something_before:
274 document.body[i:i] = ["\\end_layout", ""]
282 document.body[beg:j+1] = empert
285 document.body[beg:j+1] = comert
289 if layoutname == "Standard":
290 if not something_before:
291 document.body[beg:j+1] = parsep
293 document.body[i:i] = ["", "\\begin_layout Standard"]
296 document.body[beg:j+1] = ["\\begin_layout Standard"]
299 document.body[beg:j+1] = ["\\begin_deeper"]
301 end = end + 1 - (j + 1 - beg)
302 if not something_before:
303 document.body[i:i] = parsep
305 end = end + len(parsep)
306 document.body[i:i] = ["\\begin_layout Standard"]
307 document.body[end+2:end+2] = ["", "\\end_deeper", ""]
310 next_par_is_aligned = False
311 k = find_nonempty_line(document.body, end+1)
312 if k != -1 and check_token(document.body[k], "\\begin_layout"):
313 lay = get_containing_layout(document.body, k)
314 next_par_is_aligned = lay != False and \
315 find_token(document.body, "\\align", lay[1], lay[2]) != -1
316 if k != -1 and not next_par_is_aligned \
317 and not check_token(document.body[k], "\\end_deeper") \
318 and not check_token(document.body[k], "\\begin_deeper"):
319 if layoutname == "Standard":
320 document.body[beg:j+1] = [beglaysep]
323 document.body[beg:j+1] = ["\\begin_deeper", beglaysep]
324 end = end + 2 - (j + 1 - beg)
325 document.body[end+1:end+1] = ["", "\\end_deeper", ""]
329 del document.body[i:end+1]
331 del document.body[i:end-1]
336 def convert_parbreak(document):
338 Convert parbreak separators not specifically used to separate
339 environments to latexpar separators.
341 parbreakinset = "\\begin_inset Separator parbreak"
344 i = find_token(document.body, parbreakinset, i)
347 lay = get_containing_layout(document.body, i)
349 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
352 if lay[0] == "Standard":
353 # Convert only if not alone in the paragraph
354 k1 = find_nonempty_line(document.body, lay[1] + 1, i + 1)
355 k2 = find_nonempty_line(document.body, i + 1, lay[2])
356 if (k1 < i) or (k2 > i + 1) or not check_token(document.body[i], parbreakinset):
357 document.body[i] = document.body[i].replace("parbreak", "latexpar")
359 document.body[i] = document.body[i].replace("parbreak", "latexpar")
363 def revert_parbreak(document):
365 Revert latexpar separators to parbreak separators.
369 i = find_token(document.body, "\\begin_inset Separator latexpar", i)
372 document.body[i] = document.body[i].replace("latexpar", "parbreak")
376 def revert_smash(document):
377 " Set amsmath to on if smash commands are used "
379 commands = ["smash[t]", "smash[b]", "notag"]
380 i = find_token(document.header, "\\use_package amsmath", 0)
382 document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
384 value = get_value(document.header, "\\use_package amsmath", i).split()[1]
386 # nothing to do if package is not auto but on or off
390 j = find_token(document.body, '\\begin_inset Formula', j)
393 k = find_end_of_inset(document.body, j)
395 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
398 code = "\n".join(document.body[j:k])
400 if code.find("\\%s" % c) != -1:
401 # set amsmath to on, since it is loaded by the newer format
402 document.header[i] = "\\use_package amsmath 2"
407 def revert_swissgerman(document):
408 " Set language german-ch-old to german "
410 if document.language == "german-ch-old":
411 document.language = "german"
412 i = find_token(document.header, "\\language", 0)
414 document.header[i] = "\\language german"
417 j = find_token(document.body, "\\lang german-ch-old", j)
420 document.body[j] = document.body[j].replace("\\lang german-ch-old", "\\lang german")
424 def revert_use_package(document, pkg, commands, oldauto, supported):
425 # oldauto defines how the version we are reverting to behaves:
426 # if it is true, the old version uses the package automatically.
427 # if it is false, the old version never uses the package.
428 # If "supported" is true, the target version also supports this
430 regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
431 p = find_re(document.header, regexp, 0)
432 value = "1" # default is auto
434 value = get_value(document.header, "\\use_package" , p).split()[1]
436 del document.header[p]
437 if value == "2" and not supported: # on
438 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
439 elif value == "1" and not oldauto: # auto
442 i = find_token(document.body, '\\begin_inset Formula', i)
445 j = find_end_of_inset(document.body, i)
447 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
450 code = "\n".join(document.body[i:j])
452 if code.find("\\%s" % c) != -1:
454 document.header[p] = "\\use_package " + pkg + " 2"
456 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
461 mathtools_commands = ["xhookrightarrow", "xhookleftarrow", "xRightarrow", \
462 "xrightharpoondown", "xrightharpoonup", "xrightleftharpoons", \
463 "xLeftarrow", "xleftharpoondown", "xleftharpoonup", \
464 "xleftrightarrow", "xLeftrightarrow", "xleftrightharpoons", \
467 def revert_xarrow(document):
468 "remove use_package mathtools"
469 revert_use_package(document, "mathtools", mathtools_commands, False, True)
472 def revert_beamer_lemma(document):
473 " Reverts beamer lemma layout to ERT "
475 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
476 if document.textclass not in beamer_classes:
482 i = find_token(document.body, "\\begin_layout Lemma", i)
485 j = find_end_of_layout(document.body, i)
487 document.warning("Malformed LyX document: Can't find end of Lemma layout")
490 arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j)
491 endarg1 = find_end_of_inset(document.body, arg1)
492 arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j)
493 endarg2 = find_end_of_inset(document.body, arg2)
497 beginPlain1 = find_token(document.body, "\\begin_layout Plain Layout", arg1, endarg1)
498 if beginPlain1 == -1:
499 document.warning("Malformed LyX document: Can't find arg1 plain Layout")
502 endPlain1 = find_end_of_inset(document.body, beginPlain1)
503 content1 = document.body[beginPlain1 + 1 : endPlain1 - 2]
504 subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">")
506 beginPlain2 = find_token(document.body, "\\begin_layout Plain Layout", arg2, endarg2)
507 if beginPlain2 == -1:
508 document.warning("Malformed LyX document: Can't find arg2 plain Layout")
511 endPlain2 = find_end_of_inset(document.body, beginPlain2)
512 content2 = document.body[beginPlain2 + 1 : endPlain2 - 2]
513 subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]")
517 del document.body[arg2 : endarg2 + 1]
519 del document.body[arg1 : endarg1 + 1]
521 del document.body[arg1 : endarg1 + 1]
523 del document.body[arg2 : endarg2 + 1]
525 # index of end layout has probably changed
526 j = find_end_of_layout(document.body, i)
528 document.warning("Malformed LyX document: Can't find end of Lemma layout")
534 # if this is not a consecutive env, add start command
536 begcmd = put_cmd_in_ert("\\begin{lemma}")
538 # has this a consecutive lemma?
539 consecutive = document.body[j + 2] == "\\begin_layout Lemma"
541 # if this is not followed by a consecutive env, add end command
543 document.body[j : j + 1] = put_cmd_in_ert("\\end{lemma}") + ["\\end_layout"]
545 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd + subst1 + subst2
551 def revert_question_env(document):
553 Reverts question and question* environments of
554 theorems-ams-extended-bytype module to ERT
557 # Do we use theorems-ams-extended-bytype module?
558 if not "theorems-ams-extended-bytype" in document.get_module_list():
564 i = find_token(document.body, "\\begin_layout Question", i)
568 starred = document.body[i] == "\\begin_layout Question*"
570 j = find_end_of_layout(document.body, i)
572 document.warning("Malformed LyX document: Can't find end of Question layout")
576 # if this is not a consecutive env, add start command
580 begcmd = put_cmd_in_ert("\\begin{question*}")
582 begcmd = put_cmd_in_ert("\\begin{question}")
584 # has this a consecutive theorem of same type?
587 consecutive = document.body[j + 2] == "\\begin_layout Question*"
589 consecutive = document.body[j + 2] == "\\begin_layout Question"
591 # if this is not followed by a consecutive env, add end command
594 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
596 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
598 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
600 add_to_preamble(document, "\\providecommand{\questionname}{Question}")
603 add_to_preamble(document, "\\theoremstyle{plain}\n" \
604 "\\newtheorem*{question*}{\\protect\\questionname}")
606 add_to_preamble(document, "\\theoremstyle{plain}\n" \
607 "\\newtheorem{question}{\\protect\\questionname}")
612 def convert_dashes(document):
613 "convert -- and --- to \\twohyphens and \\threehyphens"
615 if document.backend != "latex":
619 while i < len(document.body):
620 words = document.body[i].split()
621 if (len(words) > 1 and words[0] == "\\begin_inset"
622 and (words[1] in ["CommandInset", "ERT", "External", "Formula",
623 "FormulaMacro", "Graphics", "IPA", "listings"]
624 or ' '.join(words[1:]) == "Flex Code")):
625 # must not replace anything in insets that store LaTeX contents in .lyx files
626 # (math and command insets without overridden read() and write() methods
627 # filtering out IPA makes Text::readParToken() more simple
628 # skip ERT as well since it is not needed there
629 # Flex Code is logical markup, typically rendered as typewriter
630 j = find_end_of_inset(document.body, i)
632 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
637 if len(words) > 0 and words[0] in ["\\leftindent", "\\paragraph_spacing", "\\align", "\\labelwidthstring"]:
638 # skip paragraph parameters (bug 10243)
642 j = document.body[i].find("--")
645 front = document.body[i][:j]
646 back = document.body[i][j+2:]
647 # We can have an arbitrary number of consecutive hyphens.
648 # These must be split into the corresponding number of two and three hyphens
649 # We must match what LaTeX does: First try emdash, then endash, then single hyphen
650 if back.find("-") == 0:
653 document.body.insert(i+1, back)
654 document.body[i] = front + "\\threehyphens"
657 document.body.insert(i+1, back)
658 document.body[i] = front + "\\twohyphens"
662 def revert_dashes(document):
663 "convert \\twohyphens and \\threehyphens to -- and ---"
665 # eventually remove preamble code from 2.3->2.2 conversion:
666 for i, line in enumerate(document.preamble):
667 if i > 1 and line == r'\renewcommand{\textemdash}{---}':
668 if (document.preamble[i-1] == r'\renewcommand{\textendash}{--}'
669 and document.preamble[i-2] == '% Added by lyx2lyx'):
670 del document.preamble[i-2:i+1]
672 while i < len(document.body):
673 words = document.body[i].split()
674 if len(words) > 1 and words[0] == "\\begin_inset" and \
675 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
677 j = find_end_of_inset(document.body, i)
679 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
685 if document.body[i].find("\\twohyphens") >= 0:
686 document.body[i] = document.body[i].replace("\\twohyphens", "--")
688 if document.body[i].find("\\threehyphens") >= 0:
689 document.body[i] = document.body[i].replace("\\threehyphens", "---")
691 if replaced and i+1 < len(document.body) and \
692 (document.body[i+1].find("\\") != 0 or \
693 document.body[i+1].find("\\twohyphens") == 0 or
694 document.body[i+1].find("\\threehyphens") == 0) and \
695 len(document.body[i]) + len(document.body[i+1]) <= 80:
696 document.body[i] = document.body[i] + document.body[i+1]
697 document.body[i+1:i+2] = []
702 # order is important for the last three!
703 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
705 def is_part_of_converted_phrase(line, j, phrase):
706 "is phrase part of an already converted phrase?"
708 converted = "\\SpecialCharNoPassThru \\" + p
709 pos = j + len(phrase) - len(converted)
711 if line[pos:pos+len(converted)] == converted:
716 def convert_phrases(document):
717 "convert special phrases from plain text to \\SpecialCharNoPassThru"
719 if document.backend != "latex":
722 for phrase in phrases:
724 while i < len(document.body):
725 words = document.body[i].split()
726 if len(words) > 1 and words[0] == "\\begin_inset" and \
727 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
728 # must not replace anything in insets that store LaTeX contents in .lyx files
729 # (math and command insets withut overridden read() and write() methods
730 j = find_end_of_inset(document.body, i)
732 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
737 if document.body[i].find("\\") == 0:
740 j = document.body[i].find(phrase)
744 if not is_part_of_converted_phrase(document.body[i], j, phrase):
745 front = document.body[i][:j]
746 back = document.body[i][j+len(phrase):]
748 document.body.insert(i+1, back)
749 # We cannot use SpecialChar since we do not know whether we are outside passThru
750 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
754 def revert_phrases(document):
755 "convert special phrases to plain text"
758 while i < len(document.body):
759 words = document.body[i].split()
760 if len(words) > 1 and words[0] == "\\begin_inset" and \
761 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
762 # see convert_phrases
763 j = find_end_of_inset(document.body, i)
765 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
771 for phrase in phrases:
772 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
773 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
774 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
776 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
777 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
779 if replaced and i+1 < len(document.body) and \
780 (document.body[i+1].find("\\") != 0 or \
781 document.body[i+1].find("\\SpecialChar") == 0) and \
782 len(document.body[i]) + len(document.body[i+1]) <= 80:
783 document.body[i] = document.body[i] + document.body[i+1]
784 document.body[i+1:i+2] = []
789 def convert_specialchar_internal(document, forward):
790 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
791 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
792 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
793 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
794 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
795 "\\LaTeX":"LaTeX" # must be after LaTeX2e
799 while i < len(document.body):
800 words = document.body[i].split()
801 if len(words) > 1 and words[0] == "\\begin_inset" and \
802 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
803 # see convert_phrases
804 j = find_end_of_inset(document.body, i)
806 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
811 for key, value in specialchars.items():
813 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
814 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
816 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
817 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
821 def convert_specialchar(document):
822 "convert special characters to new syntax"
823 convert_specialchar_internal(document, True)
826 def revert_specialchar(document):
827 "convert special characters to old syntax"
828 convert_specialchar_internal(document, False)
831 def revert_georgian(document):
832 "Set the document language to English but assure Georgian output"
834 if document.language == "georgian":
835 document.language = "english"
836 i = find_token(document.header, "\\language georgian", 0)
838 document.header[i] = "\\language english"
839 j = find_token(document.header, "\\language_package default", 0)
841 document.header[j] = "\\language_package babel"
842 k = find_token(document.header, "\\options", 0)
844 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
846 l = find_token(document.header, "\\use_default_options", 0)
847 document.header.insert(l + 1, "\\options georgian")
850 def revert_sigplan_doi(document):
851 " Reverts sigplanconf DOI layout to ERT "
853 if document.textclass != "sigplanconf":
858 i = find_token(document.body, "\\begin_layout DOI", i)
861 j = find_end_of_layout(document.body, i)
863 document.warning("Malformed LyX document: Can't find end of DOI layout")
867 content = lyx2latex(document, document.body[i:j + 1])
868 add_to_preamble(document, ["\\doi{" + content + "}"])
869 del document.body[i:j + 1]
873 def revert_ex_itemargs(document):
874 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
876 if not "linguistics" in document.get_module_list():
880 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
882 i = find_token(document.body, "\\begin_inset Argument item:", i)
885 j = find_end_of_inset(document.body, i)
886 # Find containing paragraph layout
887 parent = get_containing_layout(document.body, i)
889 document.warning("Malformed LyX document: Can't find parent paragraph layout")
893 layoutname = parent[0]
894 if layoutname in example_layouts:
895 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
896 endPlain = find_end_of_layout(document.body, beginPlain)
897 content = document.body[beginPlain + 1 : endPlain]
898 del document.body[i:j+1]
899 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
900 document.body[parbeg : parbeg] = subst
904 def revert_forest(document):
905 " Reverts the forest environment (Linguistics module) to TeX-code "
907 if not "linguistics" in document.get_module_list():
912 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
915 j = find_end_of_inset(document.body, i)
917 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
921 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
922 endPlain = find_end_of_layout(document.body, beginPlain)
923 content = lyx2latex(document, document.body[beginPlain : endPlain])
925 add_to_preamble(document, ["\\usepackage{forest}"])
927 document.body[i:j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
931 def revert_glossgroup(document):
932 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
934 if not "linguistics" in document.get_module_list():
939 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
942 j = find_end_of_inset(document.body, i)
944 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
948 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
949 endPlain = find_end_of_layout(document.body, beginPlain)
950 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
952 document.body[i:j + 1] = ["{", "", content, "", "}"]
956 def revert_newgloss(document):
957 " Reverts the new Glosse insets (Linguistics module) to the old format "
959 if not "linguistics" in document.get_module_list():
962 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
963 for glosse in glosses:
966 i = find_token(document.body, glosse, i)
969 j = find_end_of_inset(document.body, i)
971 document.warning("Malformed LyX document: Can't find end of Glosse inset")
975 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
976 endarg = find_end_of_inset(document.body, arg)
979 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
980 if argbeginPlain == -1:
981 document.warning("Malformed LyX document: Can't find arg plain Layout")
984 argendPlain = find_end_of_inset(document.body, argbeginPlain)
985 argcontent = lyx2verbatim(document, document.body[argbeginPlain : argendPlain - 2])
987 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
988 argcontent, "\\end_layout"]
990 # remove Arg insets and paragraph, if it only contains this inset
991 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
992 del document.body[arg - 1 : endarg + 4]
994 del document.body[arg : endarg + 1]
996 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
997 endPlain = find_end_of_layout(document.body, beginPlain)
998 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
1000 document.body[beginPlain + 1:endPlain] = [content]
1003 # Dissolve ERT insets
1004 for glosse in glosses:
1007 i = find_token(document.body, glosse, i)
1010 j = find_end_of_inset(document.body, i)
1012 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1016 ert = find_token(document.body, "\\begin_inset ERT", i, j)
1019 ertend = find_end_of_inset(document.body, ert)
1021 document.warning("Malformed LyX document: Can't find end of ERT inset")
1024 ertcontent = get_ert(document.body, ert, True)
1025 document.body[ert : ertend + 1] = [ertcontent]
1029 def convert_newgloss(document):
1030 " Converts Glosse insets (Linguistics module) to the new format "
1032 if not "linguistics" in document.get_module_list():
1035 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1036 for glosse in glosses:
1039 i = find_token(document.body, glosse, i)
1042 j = find_end_of_inset(document.body, i)
1044 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1051 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
1052 if beginPlain == -1:
1054 endPlain = find_end_of_layout(document.body, beginPlain)
1056 document.warning("Malformed LyX document: Can't find end of Glosse layout")
1060 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
1061 if glt != -1 and document.body[glt + 1].startswith("glt"):
1062 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1063 argcontent = document.body[glt + 1 : endPlain]
1064 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
1065 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
1066 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
1067 "\\end_layout", "", "\\end_inset"]
1069 content = document.body[beginPlain + 1 : endPlain]
1070 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
1071 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
1073 endPlain = find_end_of_layout(document.body, beginPlain)
1075 j = find_end_of_inset(document.body, i)
1080 def convert_BoxFeatures(document):
1081 " adds new box features "
1085 i = find_token(document.body, "height_special", i)
1088 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1092 def revert_BoxFeatures(document):
1093 " outputs new box features as TeX code "
1097 defaultThick = "0.4pt"
1098 defaultShadow = "4pt"
1100 i = find_token(document.body, "thickness", i)
1103 binset = find_token(document.body, "\\begin_inset Box", i - 11)
1104 if binset == -1 or binset != i - 11:
1106 continue # then "thickness" is is just a word in the text
1107 einset = find_end_of_inset(document.body, binset)
1109 document.warning("Malformed LyX document: Can't find end of box inset!")
1112 # read out the values
1113 beg = document.body[i].find('"');
1114 end = document.body[i].rfind('"');
1115 thickness = document.body[i][beg+1:end];
1116 beg = document.body[i+1].find('"');
1117 end = document.body[i+1].rfind('"');
1118 separation = document.body[i+1][beg+1:end];
1119 beg = document.body[i+2].find('"');
1120 end = document.body[i+2].rfind('"');
1121 shadowsize = document.body[i+2][beg+1:end];
1122 # delete the specification
1123 del document.body[i:i+3]
1125 # first output the closing brace
1126 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1127 document.body[einset -1 : einset - 1] = put_cmd_in_ert("}")
1128 # we have now the problem that if there is already \(f)colorbox in ERT around the inset
1129 # the ERT from this routine must be around it
1130 regexp = re.compile(r'^.*colorbox{.*$')
1131 pos = find_re(document.body, regexp, binset - 4)
1132 if pos != -1 and pos == binset - 4:
1136 # now output the lengths
1137 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1138 document.body[pos : pos] = put_cmd_in_ert("{")
1139 if thickness != defaultThick:
1140 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness]
1141 if separation != defaultSep and thickness == defaultThick:
1142 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation]
1143 if separation != defaultSep and thickness != defaultThick:
1144 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1145 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1146 document.body[pos + 5 : pos +6] = ["{\\backslash shadowsize " + shadowsize]
1147 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1148 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1149 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1150 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1151 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1152 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1155 def convert_origin(document):
1156 " Insert the origin tag "
1158 i = find_token(document.header, "\\textclass ", 0)
1160 document.warning("Malformed LyX document: No \\textclass!!")
1162 if document.dir == u'':
1166 if document.systemlyxdir and document.systemlyxdir != u'':
1168 if os.path.isabs(document.dir):
1169 absdir = os.path.normpath(document.dir)
1171 absdir = os.path.normpath(os.path.abspath(document.dir))
1172 if os.path.isabs(document.systemlyxdir):
1173 abssys = os.path.normpath(document.systemlyxdir)
1175 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1176 relpath = os.path.relpath(absdir, abssys)
1177 if relpath.find(u'..') == 0:
1182 origin = document.dir.replace(u'\\', u'/') + u'/'
1184 origin = os.path.join(u"/systemlyxdir", relpath).replace(u'\\', u'/') + u'/'
1185 document.header[i:i] = ["\\origin " + origin]
1188 def revert_origin(document):
1189 " Remove the origin tag "
1191 i = find_token(document.header, "\\origin ", 0)
1193 document.warning("Malformed LyX document: No \\origin!!")
1195 del document.header[i]
1198 color_names = ["brown", "darkgray", "gray", \
1199 "lightgray", "lime", "olive", "orange", \
1200 "pink", "purple", "teal", "violet"]
1202 def revert_textcolor(document):
1203 " revert new \\textcolor colors to TeX code "
1209 i = find_token(document.body, "\\color ", i)
1213 for color in list(color_names):
1214 if document.body[i] == "\\color " + color:
1215 # register that xcolor must be loaded in the preamble
1218 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1219 # find the next \\color and/or the next \\end_layout
1220 j = find_token(document.body, "\\color", i + 1)
1221 k = find_token(document.body, "\\end_layout", i + 1)
1222 if j == -1 and k != -1:
1225 # first output the closing brace
1227 document.body[k: k] = put_cmd_in_ert("}")
1229 document.body[j: j] = put_cmd_in_ert("}")
1230 # now output the \textcolor command
1231 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1235 def convert_colorbox(document):
1236 " adds color settings for boxes "
1240 i = find_token(document.body, "shadowsize", i)
1243 document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1247 def revert_colorbox(document):
1248 " outputs color settings for boxes as TeX code "
1251 defaultframecolor = "black"
1252 defaultbackcolor = "none"
1254 i = find_token(document.body, "framecolor", i)
1257 binset = find_token(document.body, "\\begin_inset Box", i - 14)
1260 einset = find_end_of_inset(document.body, binset)
1262 document.warning("Malformed LyX document: Can't find end of box inset!")
1264 # read out the values
1265 beg = document.body[i].find('"');
1266 end = document.body[i].rfind('"');
1267 framecolor = document.body[i][beg+1:end];
1268 beg = document.body[i + 1].find('"');
1269 end = document.body[i + 1].rfind('"');
1270 backcolor = document.body[i+1][beg+1:end];
1271 # delete the specification
1272 del document.body[i:i + 2]
1274 # first output the closing brace
1275 if framecolor != defaultframecolor or backcolor != defaultbackcolor:
1276 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1277 document.body[einset : einset] = put_cmd_in_ert("}")
1278 # determine the box type
1279 isBox = find_token(document.body, "\\begin_inset Box Boxed", binset)
1280 # now output the box commands
1281 if (framecolor != defaultframecolor and isBox == binset) or (backcolor != defaultbackcolor and isBox == binset):
1282 document.body[i - 14 : i - 14] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1283 # in the case we must also change the box type because the ERT code adds a frame
1284 document.body[i - 4] = "\\begin_inset Box Frameless"
1285 # if has_inner_box 0 we must set it and use_makebox to 1
1286 ibox = find_token(document.body, "has_inner_box", i - 4)
1287 if ibox == -1 or ibox != i - 1:
1288 document.warning("Malformed LyX document: Can't find has_inner_box statement!")
1290 # read out the value
1291 innerbox = document.body[ibox][-1:];
1293 document.body[ibox] = "has_inner_box 1"
1294 document.body[ibox + 3] = "use_makebox 1"
1295 if backcolor != defaultbackcolor and isBox != binset:
1296 document.body[i - 14 : i - 14] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
1299 def revert_mathmulticol(document):
1300 " Convert formulas to ERT if they contain multicolumns "
1304 i = find_token(document.body, '\\begin_inset Formula', i)
1307 j = find_end_of_inset(document.body, i)
1309 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1312 lines = document.body[i:j]
1313 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1314 code = "\n".join(lines)
1319 n = code.find("\\multicolumn", k)
1320 # no need to convert degenerated multicolumn cells,
1321 # they work in old LyX versions as "math ERT"
1322 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1323 ert = put_cmd_in_ert(code)
1324 document.body[i:j+1] = ert
1330 i = find_end_of_inset(document.body, i)
1335 def revert_jss(document):
1336 " Reverts JSS In_Preamble commands to ERT in preamble "
1338 if document.textclass != "jss":
1347 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1348 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1351 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1353 endh = find_end_of_inset(document.body, h)
1354 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1355 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1359 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1361 endm = find_end_of_inset(document.body, m)
1362 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1363 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1367 j = find_token(document.body, "\\begin_inset Flex Code", j)
1369 # assure that we are not in a Code Chunk inset
1370 if document.body[j][-1] == "e":
1371 endj = find_end_of_inset(document.body, j)
1372 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1373 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1379 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1381 endk = find_end_of_inset(document.body, k)
1382 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1383 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1387 n = find_token(document.body, "\\begin_inset Flex URL", n)
1389 endn = find_end_of_inset(document.body, n)
1390 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1391 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1393 # now revert the In_Preamble layouts
1395 i = find_token(document.body, "\\begin_layout Title", 0)
1398 j = find_end_of_layout(document.body, i)
1400 document.warning("Malformed LyX document: Can't find end of Title layout")
1403 content = lyx2latex(document, document.body[i:j + 1])
1404 add_to_preamble(document, ["\\title{" + content + "}"])
1405 del document.body[i:j + 1]
1407 i = find_token(document.body, "\\begin_layout Author", 0)
1410 j = find_end_of_layout(document.body, i)
1412 document.warning("Malformed LyX document: Can't find end of Author layout")
1415 content = lyx2latex(document, document.body[i:j + 1])
1416 add_to_preamble(document, ["\\author{" + content + "}"])
1417 del document.body[i:j + 1]
1419 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1422 j = find_end_of_layout(document.body, i)
1424 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1427 content = lyx2latex(document, document.body[i:j + 1])
1428 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1429 del document.body[i:j + 1]
1431 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1434 j = find_end_of_layout(document.body, i)
1436 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1439 content = lyx2latex(document, document.body[i:j + 1])
1440 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1441 del document.body[i:j + 1]
1443 i = find_token(document.body, "\\begin_layout Short Title", 0)
1446 j = find_end_of_layout(document.body, i)
1448 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1451 content = lyx2latex(document, document.body[i:j + 1])
1452 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1453 del document.body[i:j + 1]
1455 i = find_token(document.body, "\\begin_layout Abstract", 0)
1458 j = find_end_of_layout(document.body, i)
1460 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1463 content = lyx2latex(document, document.body[i:j + 1])
1464 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1465 del document.body[i:j + 1]
1467 i = find_token(document.body, "\\begin_layout Keywords", 0)
1470 j = find_end_of_layout(document.body, i)
1472 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1475 content = lyx2latex(document, document.body[i:j + 1])
1476 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1477 del document.body[i:j + 1]
1479 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1482 j = find_end_of_layout(document.body, i)
1484 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1487 content = lyx2latex(document, document.body[i:j + 1])
1488 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1489 del document.body[i:j + 1]
1491 i = find_token(document.body, "\\begin_layout Address", 0)
1494 j = find_end_of_layout(document.body, i)
1496 document.warning("Malformed LyX document: Can't find end of Address layout")
1499 content = lyx2latex(document, document.body[i:j + 1])
1500 add_to_preamble(document, ["\\Address{" + content + "}"])
1501 del document.body[i:j + 1]
1502 # finally handle the code layouts
1507 while m != -1 or j != -1 or h != -1 or k != -1:
1510 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1512 endh = find_end_of_inset(document.body, h)
1513 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1514 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1515 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1519 j = find_token(document.body, "\\begin_layout Code Input", j)
1521 endj = find_end_of_layout(document.body, j)
1522 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1523 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1524 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1525 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1526 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1530 k = find_token(document.body, "\\begin_layout Code Output", k)
1532 endk = find_end_of_layout(document.body, k)
1533 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1534 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1535 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1536 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1537 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1541 m = find_token(document.body, "\\begin_layout Code", m)
1543 endm = find_end_of_layout(document.body, m)
1544 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1545 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1546 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1547 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1548 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1552 def convert_subref(document):
1553 " converts sub: ref prefixes to subref: "
1556 rx = re.compile(r'^name \"sub:(.+)$')
1559 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1562 j = find_end_of_inset(document.body, i)
1564 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1568 for p in range(i, j):
1569 m = rx.match(document.body[p])
1572 document.body[p] = "name \"subsec:" + label
1576 rx = re.compile(r'^reference \"sub:(.+)$')
1579 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1582 j = find_end_of_inset(document.body, i)
1584 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1588 for p in range(i, j):
1589 m = rx.match(document.body[p])
1592 document.body[p] = "reference \"subsec:" + label
1598 def revert_subref(document):
1599 " reverts subref: ref prefixes to sub: "
1602 rx = re.compile(r'^name \"subsec:(.+)$')
1605 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1608 j = find_end_of_inset(document.body, i)
1610 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1614 for p in range(i, j):
1615 m = rx.match(document.body[p])
1618 document.body[p] = "name \"sub:" + label
1623 rx = re.compile(r'^reference \"subsec:(.+)$')
1626 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1629 j = find_end_of_inset(document.body, i)
1631 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1635 for p in range(i, j):
1636 m = rx.match(document.body[p])
1639 document.body[p] = "reference \"sub:" + label
1644 def convert_nounzip(document):
1645 " remove the noUnzip parameter of graphics insets "
1647 rx = re.compile(r'\s*noUnzip\s*$')
1650 i = find_token(document.body, "\\begin_inset Graphics", i)
1653 j = find_end_of_inset(document.body, i)
1655 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1659 k = find_re(document.body, rx, i, j)
1661 del document.body[k]
1666 def convert_revert_external_bbox(document, forward):
1667 " add units to bounding box of external insets "
1669 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1672 i = find_token(document.body, "\\begin_inset External", i)
1675 j = find_end_of_inset(document.body, i)
1677 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1680 k = find_re(document.body, rx, i, j)
1684 tokens = document.body[k].split()
1686 for t in range(1, 5):
1689 for t in range(1, 5):
1690 tokens[t] = length_in_bp(tokens[t])
1691 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1692 tokens[3] + " " + tokens[4]
1696 def convert_external_bbox(document):
1697 convert_revert_external_bbox(document, True)
1700 def revert_external_bbox(document):
1701 convert_revert_external_bbox(document, False)
1704 def revert_tcolorbox_1(document):
1705 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1708 i = find_token(document.header, "tcolorbox", i)
1714 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1717 flexEnd = find_end_of_inset(document.body, flex)
1718 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1719 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1720 flexEnd = find_end_of_inset(document.body, flex)
1722 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1724 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1725 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1729 def revert_tcolorbox_2(document):
1730 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1733 i = find_token(document.header, "tcolorbox", i)
1739 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1742 flexEnd = find_end_of_inset(document.body, flex)
1743 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1744 flexEnd = find_end_of_inset(document.body, flex)
1745 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1746 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1750 def revert_tcolorbox_3(document):
1751 " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1754 i = find_token(document.header, "tcolorbox", i)
1760 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1763 flexEnd = find_end_of_inset(document.body, flex)
1764 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1765 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1766 flexEnd = find_end_of_inset(document.body, flex)
1767 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1768 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1772 def revert_tcolorbox_4(document):
1773 " Reverts the Flex:Custom_Color_Box_2 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 2", 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{cBoxB}")
1790 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1794 def revert_tcolorbox_5(document):
1795 " Reverts the Flex:Custom_Color_Box_3 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 3", 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{cBoxC}")
1812 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1816 def revert_tcolorbox_6(document):
1817 " Reverts the Flex:Custom_Color_Box_4 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 4", 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{cBoxD}")
1834 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1838 def revert_tcolorbox_7(document):
1839 " Reverts the Flex:Custom_Color_Box_5 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 5", 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{cBoxE}")
1856 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1860 def revert_tcolorbox_8(document):
1861 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1867 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1869 j = find_end_of_layout(document.body, i)
1870 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1871 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1872 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1873 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1875 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1877 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1878 k = find_end_of_inset(document.body, j)
1879 k = find_token(document.body, "\\end_inset", k + 1)
1880 k = find_token(document.body, "\\end_inset", k + 1)
1882 k = find_token(document.body, "\\end_inset", k + 1)
1883 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1884 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1885 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1891 def revert_moderncv_1(document):
1892 " Reverts the new inset of moderncv to TeX-code in preamble "
1894 if document.textclass != "moderncv":
1900 # at first revert the new styles
1902 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1905 j = find_end_of_layout(document.body, i)
1907 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1910 content = lyx2latex(document, document.body[i:j + 1])
1911 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1912 del document.body[i:j + 1]
1914 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1917 j = find_end_of_layout(document.body, i)
1919 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1922 content = lyx2latex(document, document.body[i:j + 1])
1923 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1924 del document.body[i:j + 1]
1925 # now change the new styles to the obsolete ones
1927 i = find_token(document.body, "\\begin_layout Name", 0)
1930 j = find_end_of_layout(document.body, i)
1932 document.warning("Malformed LyX document: Can't find end of Name layout")
1935 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1936 if lineArg > j and j != 0:
1939 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1940 # we have to assure that no other inset is in the Argument
1941 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1942 endInset = find_token(document.body, "\\end_inset", beginPlain)
1945 while beginInset < endInset and beginInset != -1:
1946 beginInset = find_token(document.body, "\\begin_inset", k)
1947 endInset = find_token(document.body, "\\end_inset", l)
1950 Arg2 = document.body[l + 5 : l + 6]
1952 document.body[i : i + 1]= ["\\begin_layout FirstName"]
1953 # delete the Argument inset
1954 del( document.body[endInset - 2 : endInset + 3])
1955 del( document.body[lineArg : beginPlain + 1])
1956 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1959 def revert_moderncv_2(document):
1960 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1962 if document.textclass != "moderncv":
1969 i = find_token(document.body, "\\begin_layout Phone", i)
1972 j = find_end_of_layout(document.body, i)
1974 document.warning("Malformed LyX document: Can't find end of Phone layout")
1977 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1978 if lineArg > j and j != 0:
1982 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1983 # we have to assure that no other inset is in the Argument
1984 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1985 endInset = find_token(document.body, "\\end_inset", beginPlain)
1988 while beginInset < endInset and beginInset != -1:
1989 beginInset = find_token(document.body, "\\begin_inset", k)
1990 endInset = find_token(document.body, "\\end_inset", l)
1993 Arg = document.body[beginPlain + 1 : beginPlain + 2]
1995 if Arg[0] == "mobile":
1996 document.body[i : i + 1]= ["\\begin_layout Mobile"]
1998 document.body[i : i + 1]= ["\\begin_layout Fax"]
1999 # delete the Argument inset
2000 del(document.body[endInset - 2 : endInset + 1])
2001 del(document.body[lineArg : beginPlain + 3])
2005 def convert_moderncv_phone(document):
2006 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
2008 if document.textclass != "moderncv":
2015 "Mobile" : "mobile",
2019 rx = re.compile(r'^\\begin_layout (\S+)$')
2021 # substitute \fax and \mobile by \phone[fax] and \phone[mobile], respectively
2022 i = find_token(document.body, "\\begin_layout", i)
2026 m = rx.match(document.body[i])
2030 if val not in list(phone_dict.keys()):
2033 j = find_end_of_layout(document.body, i)
2035 document.warning("Malformed LyX document: Can't find end of Mobile layout")
2039 document.body[i : i + 1] = ["\\begin_layout Phone", "\\begin_inset Argument 1", "status open", "",
2040 "\\begin_layout Plain Layout", phone_dict[val], "\\end_layout", "",
2044 def convert_moderncv_name(document):
2045 " Convert the FirstName and LastName layout of moderncv to the general Name layout "
2047 if document.textclass != "moderncv":
2050 fnb = 0 # Begin of FirstName inset
2051 fne = 0 # End of FirstName inset
2052 lnb = 0 # Begin of LastName (FamilyName) inset
2053 lne = 0 # End of LastName (FamilyName) inset
2054 nb = 0 # Begin of substituting Name inset
2055 ne = 0 # End of substituting Name inset
2056 FirstName = [] # FirstName content
2057 FamilyName = [] # LastName content
2061 fnb = find_token(document.body, "\\begin_layout FirstName", fnb)
2063 fne = find_end_of_layout(document.body, fnb)
2065 document.warning("Malformed LyX document: Can't find end of FirstName layout")
2067 FirstName = document.body[fnb + 1 : fne]
2069 lnb = find_token(document.body, "\\begin_layout FamilyName", lnb)
2071 lne = find_end_of_layout(document.body, lnb)
2073 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
2075 FamilyName = document.body[lnb + 1 : lne]
2076 # Determine the region for the substituting Name layout
2077 if fnb == -1 and lnb == -1: # Neither FirstName nor FamilyName exists -> Do nothing
2079 elif fnb == -1: # Only FamilyName exists -> New Name insets replaces that
2082 elif lnb == -1: # Only FirstName exists -> New Name insets replaces that
2085 elif fne > lne: # FirstName position before FamilyName -> New Name insets spans
2086 nb = lnb # from FamilyName begin
2087 ne = fne # to FirstName end
2088 else: # FirstName position before FamilyName -> New Name insets spans
2089 nb = fnb # from FirstName begin
2090 ne = lne # to FamilyName end
2092 # Insert the substituting layout now. If FirstName exists, use an otpional argument.
2094 document.body[nb : ne + 1] = ["\\begin_layout Name"] + FamilyName + ["\\end_layout", ""]
2096 document.body[nb : ne + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2097 "\\begin_layout Plain Layout"] + FirstName + ["\\end_layout", "",
2098 "\\end_inset", ""] + FamilyName + ["\\end_layout", ""]
2101 def revert_achemso(document):
2102 " Reverts the flex inset Latin to TeX code "
2104 if document.textclass != "achemso":
2109 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2111 j = find_end_of_inset(document.body, i)
2115 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2116 endPlain = find_end_of_layout(document.body, beginPlain)
2117 content = lyx2latex(document, document.body[beginPlain : endPlain])
2118 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2120 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2125 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2126 "\\font_sf_scale", "\\font_tt_scale"]
2127 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2128 fontquotes = [True, True, True, True, False, False]
2130 def convert_fontsettings(document):
2131 " Duplicate font settings "
2133 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2135 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2136 use_non_tex_fonts = "false"
2138 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2140 for f in fontsettings:
2141 i = find_token(document.header, f + " ", 0)
2143 document.warning("Malformed LyX document: No " + f + "!")
2145 # note that with i = -1, this will insert at the end
2147 value = fontdefaults[j]
2149 value = document.header[i][len(f):].strip()
2151 if use_non_tex_fonts == "true":
2152 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2154 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2156 if use_non_tex_fonts == "true":
2157 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2159 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2163 def revert_fontsettings(document):
2164 " Merge font settings "
2166 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2168 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2169 use_non_tex_fonts = "false"
2171 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2173 for f in fontsettings:
2174 i = find_token(document.header, f + " ", 0)
2176 document.warning("Malformed LyX document: No " + f + "!")
2179 line = get_value(document.header, f, i)
2182 q2 = line.find('"', q1+1)
2183 q3 = line.find('"', q2+1)
2184 q4 = line.find('"', q3+1)
2185 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2186 document.warning("Malformed LyX document: Missing quotes!")
2189 if use_non_tex_fonts == "true":
2190 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2192 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2194 if use_non_tex_fonts == "true":
2195 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2197 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2201 def revert_solution(document):
2202 " Reverts the solution environment of the theorem module to TeX code "
2204 # Do we use one of the modules that provides Solution?
2206 mods = document.get_module_list()
2208 if mod == "theorems-std" or mod == "theorems-bytype" \
2209 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2219 i = find_token(document.body, "\\begin_layout Solution", i)
2223 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2224 if is_starred == True:
2226 LyXName = "Solution*"
2227 theoremName = "newtheorem*"
2230 LyXName = "Solution"
2231 theoremName = "newtheorem"
2233 j = find_end_of_layout(document.body, i)
2235 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2239 # if this is not a consecutive env, add start command
2242 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2244 # has this a consecutive theorem of same type?
2245 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2247 # if this is not followed by a consecutive env, add end command
2249 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2251 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2253 add_to_preamble(document, "\\theoremstyle{definition}")
2254 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2255 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2256 (theoremName, LaTeXName))
2257 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2258 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2259 (theoremName, LaTeXName))
2261 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2265 def revert_verbatim_star(document):
2266 from lyx_2_1 import revert_verbatim
2267 revert_verbatim(document, True)
2270 def convert_save_props(document):
2271 " Add save_transient_properties parameter. "
2272 i = find_token(document.header, '\\begin_header', 0)
2274 document.warning("Malformed lyx document: Missing '\\begin_header'.")
2276 document.header.insert(i + 1, '\\save_transient_properties true')
2279 def revert_save_props(document):
2280 " Remove save_transient_properties parameter. "
2281 i = find_token(document.header, "\\save_transient_properties", 0)
2284 del document.header[i]
2287 def convert_info_tabular_feature(document):
2289 return arg.replace("inset-modify tabular", "tabular-feature")
2290 convert_info_insets(document, "shortcut(s)?|icon", f)
2293 def revert_info_tabular_feature(document):
2295 return arg.replace("tabular-feature", "inset-modify tabular")
2296 convert_info_insets(document, "shortcut(s)?|icon", f)
2303 supported_versions = ["2.2.0", "2.2"]
2305 [475, [convert_separator]],
2306 # nothing to do for 476: We consider it a bug that older versions
2307 # did not load amsmath automatically for these commands, and do not
2308 # want to hardcode amsmath off.
2314 [481, [convert_dashes]],
2315 [482, [convert_phrases]],
2316 [483, [convert_specialchar]],
2321 [488, [convert_newgloss]],
2322 [489, [convert_BoxFeatures]],
2323 [490, [convert_origin]],
2325 [492, [convert_colorbox]],
2328 [495, [convert_subref]],
2329 [496, [convert_nounzip]],
2330 [497, [convert_external_bbox]],
2332 [499, [convert_moderncv_phone, convert_moderncv_name]],
2334 [501, [convert_fontsettings]],
2337 [504, [convert_save_props]],
2339 [506, [convert_info_tabular_feature]],
2340 [507, [convert_longtable_label]],
2341 [508, [convert_parbreak]]
2345 [507, [revert_parbreak]],
2346 [506, [revert_longtable_label]],
2347 [505, [revert_info_tabular_feature]],
2349 [503, [revert_save_props]],
2350 [502, [revert_verbatim_star]],
2351 [501, [revert_solution]],
2352 [500, [revert_fontsettings]],
2353 [499, [revert_achemso]],
2354 [498, [revert_moderncv_1, revert_moderncv_2]],
2355 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2356 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2357 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2358 [496, [revert_external_bbox]],
2359 [495, []], # nothing to do since the noUnzip parameter was optional
2360 [494, [revert_subref]],
2361 [493, [revert_jss]],
2362 [492, [revert_mathmulticol]],
2363 [491, [revert_colorbox]],
2364 [490, [revert_textcolor]],
2365 [489, [revert_origin]],
2366 [488, [revert_BoxFeatures]],
2367 [487, [revert_newgloss, revert_glossgroup]],
2368 [486, [revert_forest]],
2369 [485, [revert_ex_itemargs]],
2370 [484, [revert_sigplan_doi]],
2371 [483, [revert_georgian]],
2372 [482, [revert_specialchar]],
2373 [481, [revert_phrases]],
2374 [480, [revert_dashes]],
2375 [479, [revert_question_env]],
2376 [478, [revert_beamer_lemma]],
2377 [477, [revert_xarrow]],
2378 [476, [revert_swissgerman]],
2379 [475, [revert_smash]],
2380 [474, [revert_separator]]
2384 if __name__ == "__main__":