1 # This file is part of lyx2lyx
2 # Copyright (C) 2015 The LyX team
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 """Convert files to the file format generated by lyx 2.2"""
24 # Uncomment only what you need to import, please.
26 from lyx2lyx_tools import (
34 insert_document_option,
38 from parser_tools import (
48 get_containing_layout,
58 ####################################################################
59 # Private helper functions
62 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt, nolastopt):
64 Reverts an InsetArgument to TeX-code
66 revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt, notLastOpt)
67 LineOfBegin is the line of the \begin_layout or \begin_inset statement
68 LineOfEnd is the line of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
69 StartArgument is the number of the first argument that needs to be converted
70 EndArgument is the number of the last argument that needs to be converted or the last defined one
71 isEnvironment must be true, if the layout is for a LaTeX environment
72 isOpt must be true, if the argument is an optional one
73 notLastOpt must be true if the argument is mandatory and followed by optional ones
77 while lineArg != -1 and n < nmax + 1:
78 lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
79 if lineArg > endline and endline != 0:
82 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
83 # we have to assure that no other inset is in the Argument
84 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
85 endInset = find_token(document.body, "\\end_inset", beginPlain)
88 while beginInset < endInset and beginInset != -1:
89 beginInset = find_token(document.body, "\\begin_inset", k)
90 endInset = find_token(document.body, "\\end_inset", l)
93 if environment == False:
95 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
96 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
99 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
100 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
104 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
105 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
108 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
109 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
115 ###############################################################################
117 ### Conversion and reversion routines
119 ###############################################################################
122 def convert_longtable_label_internal(document, forward):
124 Convert reference to "LongTableNoNumber" into "Unnumbered" if forward is True
127 old_reference = "\\begin_inset Caption LongTableNoNumber"
128 new_reference = "\\begin_inset Caption Unnumbered"
130 # if the purpose is to revert swap the strings roles
132 old_reference, new_reference = new_reference, old_reference
136 i = find_token(document.body, old_reference, i)
141 document.body[i] = new_reference
144 def convert_longtable_label(document):
145 convert_longtable_label_internal(document, True)
148 def revert_longtable_label(document):
149 convert_longtable_label_internal(document, False)
152 def convert_separator(document):
154 Convert layout separators to separator insets and add (LaTeX) paragraph
155 breaks in order to mimic previous LaTeX export.
158 parins = ["\\begin_inset Separator parbreak", "\\end_inset", ""]
160 "\\begin_layout Standard",
161 "\\begin_inset Separator parbreak",
178 i = find_token(document.body, "\\begin_deeper", i)
182 j = find_token_backwards(document.body, "\\end_layout", i - 1)
184 # reset any text style before inserting the inset
185 lay = get_containing_layout(document.body, j - 1)
187 content = "\n".join(document.body[lay[1] : lay[2]])
188 for val in list(sty_dict.keys()):
189 if content.find("\\%s" % val) != -1:
190 document.body[j:j] = [f"\\{val} {sty_dict[val]}"]
193 document.body[j:j] = parins
194 i = i + len(parins) + 1
200 i = find_token(document.body, "\\align", i)
204 lay = get_containing_layout(document.body, i)
205 if lay != False and lay[0] == "Plain Layout":
209 j = find_token_backwards(document.body, "\\end_layout", i - 1)
211 # Very old LyX files do not have Plain Layout in insets (but Standard).
212 # So we additionally check here if there is no inset boundary
213 # between the previous layout and this one.
214 n = find_token(document.body, "\\end_inset", j, lay[1])
218 lay = get_containing_layout(document.body, j - 1)
221 and lay[0] == "Standard"
222 and find_token(document.body, "\\align", lay[1], lay[2]) == -1
223 and find_token(document.body, "\\begin_inset VSpace", lay[1], lay[2]) == -1
225 # reset any text style before inserting the inset
226 content = "\n".join(document.body[lay[1] : lay[2]])
227 for val in list(sty_dict.keys()):
228 if content.find("\\%s" % val) != -1:
229 document.body[j:j] = [f"\\{val} {sty_dict[val]}"]
232 document.body[j:j] = parins
233 i = i + len(parins) + 1
240 r"^\\begin_layout (?:(-*)|(\s*))(Separator|EndOfSlide)(?:(-*)|(\s*))$",
246 i = find_re(document.body, regexp, i)
250 j = find_end_of_layout(document.body, i)
252 document.warning("Malformed LyX document: Missing `\\end_layout'.")
255 lay = get_containing_layout(document.body, j - 1)
257 lines = document.body[lay[3] : lay[2]]
261 document.body[i : j + 1] = parlay
263 document.body[i + 1 : i + 1] = lines
265 i = i + len(parlay) + len(lines) + 1
268 def revert_separator(document):
269 "Revert separator insets to layout separators"
271 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
272 if document.textclass in beamer_classes:
273 beglaysep = "\\begin_layout Separator"
275 beglaysep = "\\begin_layout --Separator--"
277 parsep = [beglaysep, "", "\\end_layout", ""]
282 "\\begin_layout Plain Layout",
293 "\\begin_layout Plain Layout",
303 i = find_token(document.body, "\\begin_inset Separator", i)
307 lay = get_containing_layout(document.body, i)
310 "Malformed LyX document: Can't convert separator inset at line " + str(i)
318 kind = get_value(document.body, "\\begin_inset Separator", i, i + 1, "plain").split()[1]
319 before = document.body[beg + 1 : i]
320 something_before = len(before) > 0 and len("".join(before)) > 0
321 j = find_end_of_inset(document.body, i)
322 after = document.body[j + 1 : end]
323 something_after = len(after) > 0 and len("".join(after)) > 0
325 beg = beg + len(before) + 1
326 elif something_before:
327 document.body[i:i] = ["\\end_layout", ""]
335 document.body[beg : j + 1] = empert
338 document.body[beg : j + 1] = comert
342 if layoutname == "Standard":
343 if not something_before:
344 document.body[beg : j + 1] = parsep
346 document.body[i:i] = ["", "\\begin_layout Standard"]
349 document.body[beg : j + 1] = ["\\begin_layout Standard"]
352 document.body[beg : j + 1] = ["\\begin_deeper"]
354 end = end + 1 - (j + 1 - beg)
355 if not something_before:
356 document.body[i:i] = parsep
358 end = end + len(parsep)
359 document.body[i:i] = ["\\begin_layout Standard"]
360 document.body[end + 2 : end + 2] = ["", "\\end_deeper", ""]
363 next_par_is_aligned = False
364 k = find_nonempty_line(document.body, end + 1)
365 if k != -1 and check_token(document.body[k], "\\begin_layout"):
366 lay = get_containing_layout(document.body, k)
367 next_par_is_aligned = (
369 and find_token(document.body, "\\align", lay[1], lay[2]) != -1
373 and not next_par_is_aligned
374 and not check_token(document.body[k], "\\end_deeper")
375 and not check_token(document.body[k], "\\begin_deeper")
377 if layoutname == "Standard":
378 document.body[beg : j + 1] = [beglaysep]
381 document.body[beg : j + 1] = ["\\begin_deeper", beglaysep]
382 end = end + 2 - (j + 1 - beg)
383 document.body[end + 1 : end + 1] = ["", "\\end_deeper", ""]
387 del document.body[i : end + 1]
389 del document.body[i : end - 1]
394 def convert_parbreak(document):
396 Convert parbreak separators not specifically used to separate
397 environments to latexpar separators.
399 parbreakinset = "\\begin_inset Separator parbreak"
402 i = find_token(document.body, parbreakinset, i)
405 lay = get_containing_layout(document.body, i)
408 "Malformed LyX document: " "Can't convert separator inset at line %d" % i
412 if lay[0] == "Standard":
413 # Convert only if not alone in the paragraph
414 k1 = find_nonempty_line(document.body, lay[1] + 1, i + 1)
415 k2 = find_nonempty_line(document.body, i + 1, lay[2])
416 if (k1 < i) or (k2 > i + 1) or not check_token(document.body[i], parbreakinset):
417 document.body[i] = document.body[i].replace("parbreak", "latexpar")
419 document.body[i] = document.body[i].replace("parbreak", "latexpar")
423 def revert_parbreak(document):
425 Revert latexpar separators to parbreak separators.
429 i = find_token(document.body, "\\begin_inset Separator latexpar", i)
432 document.body[i] = document.body[i].replace("latexpar", "parbreak")
436 def revert_smash(document):
437 "Set amsmath to on if smash commands are used"
439 commands = ["smash[t]", "smash[b]", "notag"]
440 i = find_token(document.header, "\\use_package amsmath", 0)
442 document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
444 value = get_value(document.header, "\\use_package amsmath", i).split()[1]
446 # nothing to do if package is not auto but on or off
450 j = find_token(document.body, "\\begin_inset Formula", j)
453 k = find_end_of_inset(document.body, j)
456 "Malformed LyX document: Can't find end of Formula inset at line " + str(j)
460 code = "\n".join(document.body[j:k])
462 if code.find("\\%s" % c) != -1:
463 # set amsmath to on, since it is loaded by the newer format
464 document.header[i] = "\\use_package amsmath 2"
469 def revert_swissgerman(document):
470 "Set language german-ch-old to german"
472 if document.language == "german-ch-old":
473 document.language = "german"
474 i = find_token(document.header, "\\language", 0)
476 document.header[i] = "\\language german"
479 j = find_token(document.body, "\\lang german-ch-old", j)
482 document.body[j] = document.body[j].replace("\\lang german-ch-old", "\\lang german")
486 def revert_use_package(document, pkg, commands, oldauto, supported):
487 # oldauto defines how the version we are reverting to behaves:
488 # if it is true, the old version uses the package automatically.
489 # if it is false, the old version never uses the package.
490 # If "supported" is true, the target version also supports this
492 regexp = re.compile(r"(\\use_package\s+%s)" % pkg)
493 p = find_re(document.header, regexp, 0)
494 value = "1" # default is auto
496 value = get_value(document.header, "\\use_package", p).split()[1]
498 del document.header[p]
499 if value == "2" and not supported: # on
500 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
501 elif value == "1" and not oldauto: # auto
504 i = find_token(document.body, "\\begin_inset Formula", i)
507 j = find_end_of_inset(document.body, i)
510 "Malformed LyX document: Can't find end of Formula inset at line " + str(i)
514 code = "\n".join(document.body[i:j])
516 if code.find("\\%s" % c) != -1:
518 document.header[p] = "\\use_package " + pkg + " 2"
520 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
525 mathtools_commands = [
531 "xrightleftharpoons",
537 "xleftrightharpoons",
542 def revert_xarrow(document):
543 "remove use_package mathtools"
544 revert_use_package(document, "mathtools", mathtools_commands, False, True)
547 def revert_beamer_lemma(document):
548 "Reverts beamer lemma layout to ERT"
550 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
551 if document.textclass not in beamer_classes:
557 i = find_token(document.body, "\\begin_layout Lemma", i)
560 j = find_end_of_layout(document.body, i)
562 document.warning("Malformed LyX document: Can't find end of Lemma layout")
565 arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j)
566 endarg1 = find_end_of_inset(document.body, arg1)
567 arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j)
568 endarg2 = find_end_of_inset(document.body, arg2)
572 beginPlain1 = find_token(
573 document.body, "\\begin_layout Plain Layout", arg1, endarg1
575 if beginPlain1 == -1:
576 document.warning("Malformed LyX document: Can't find arg1 plain Layout")
579 endPlain1 = find_end_of_inset(document.body, beginPlain1)
580 content1 = document.body[beginPlain1 + 1 : endPlain1 - 2]
581 subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">")
583 beginPlain2 = find_token(
584 document.body, "\\begin_layout Plain Layout", arg2, endarg2
586 if beginPlain2 == -1:
587 document.warning("Malformed LyX document: Can't find arg2 plain Layout")
590 endPlain2 = find_end_of_inset(document.body, beginPlain2)
591 content2 = document.body[beginPlain2 + 1 : endPlain2 - 2]
592 subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]")
596 del document.body[arg2 : endarg2 + 1]
598 del document.body[arg1 : endarg1 + 1]
600 del document.body[arg1 : endarg1 + 1]
602 del document.body[arg2 : endarg2 + 1]
604 # index of end layout has probably changed
605 j = find_end_of_layout(document.body, i)
607 document.warning("Malformed LyX document: Can't find end of Lemma layout")
613 # if this is not a consecutive env, add start command
615 begcmd = put_cmd_in_ert("\\begin{lemma}")
617 # has this a consecutive lemma?
618 consecutive = document.body[j + 2] == "\\begin_layout Lemma"
620 # if this is not followed by a consecutive env, add end command
622 document.body[j : j + 1] = put_cmd_in_ert("\\end{lemma}") + ["\\end_layout"]
624 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd + subst1 + subst2
629 def revert_question_env(document):
631 Reverts question and question* environments of
632 theorems-ams-extended-bytype module to ERT
635 # Do we use theorems-ams-extended-bytype module?
636 if not "theorems-ams-extended-bytype" in document.get_module_list():
642 i = find_token(document.body, "\\begin_layout Question", i)
646 starred = document.body[i] == "\\begin_layout Question*"
648 j = find_end_of_layout(document.body, i)
650 document.warning("Malformed LyX document: Can't find end of Question layout")
654 # if this is not a consecutive env, add start command
658 begcmd = put_cmd_in_ert("\\begin{question*}")
660 begcmd = put_cmd_in_ert("\\begin{question}")
662 # has this a consecutive theorem of same type?
665 consecutive = document.body[j + 2] == "\\begin_layout Question*"
667 consecutive = document.body[j + 2] == "\\begin_layout Question"
669 # if this is not followed by a consecutive env, add end command
672 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
674 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
676 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
678 add_to_preamble(document, "\\providecommand{\\questionname}{Question}")
683 "\\theoremstyle{plain}\n" "\\newtheorem*{question*}{\\protect\\questionname}",
688 "\\theoremstyle{plain}\n" "\\newtheorem{question}{\\protect\\questionname}",
694 def convert_dashes(document):
695 "convert -- and --- to \\twohyphens and \\threehyphens"
697 if document.backend != "latex":
702 i = find_substring(document.body, "--", i + 1)
705 line = document.body[i]
706 # skip label width string (bug 10243):
707 if line.startswith("\\labelwidthstring"):
709 # Do not touch hyphens in some insets:
711 value, start, end = get_containing_inset(document.body, i)
713 # False means no (or malformed) containing inset
714 value, start, end = "no inset", -1, -1
715 # We must not replace anything in insets that store LaTeX contents in .lyx files
716 # (math and command insets without overridden read() and write() methods.
717 # Filtering out IPA and ERT makes Text::readParToken() more simple,
718 # Flex Code is logical markup, typically rendered as typewriter
719 if value.split()[0] in [
728 ] or value in ["Flex Code", "Flex URL"]:
732 layout, start, end, j = get_containing_layout(document.body, i)
733 except TypeError: # no (or malformed) containing layout
734 document.warning("Malformed LyX document: " "Can't find layout at line %d" % i)
736 if layout == "LyX-Code":
739 # We can have an arbitrary number of consecutive hyphens.
740 # Replace as LaTeX does: First try emdash, then endash
741 line = line.replace("---", "\\threehyphens\n")
742 line = line.replace("--", "\\twohyphens\n")
743 document.body[i : i + 1] = line.split("\n")
745 # remove ligature breaks between dashes
748 i = find_substring(document.body, r"-\SpecialChar \textcompwordmark{}", i + 1)
751 if document.body[i + 1].startswith("-"):
752 document.body[i] = document.body[i].replace(
753 r"\SpecialChar \textcompwordmark{}", document.body.pop(i + 1)
757 def revert_dashes(document):
759 Remove preamble code from 2.3->2.2 conversion.
760 Prevent ligatures of existing --- and --.
761 Revert \\twohyphens and \\threehyphens to -- and ---.
766 "% Added by lyx2lyx",
767 r"\renewcommand{\textendash}{--}",
768 r"\renewcommand{\textemdash}{---}",
772 # Insert ligature breaks to prevent ligation of hyphens to dashes:
775 i = find_substring(document.body, "--", i + 1)
778 line = document.body[i]
779 # skip label width string (bug 10243):
780 if line.startswith("\\labelwidthstring"):
782 # do not touch hyphens in some insets (cf. convert_dashes):
784 value, start, end = get_containing_inset(document.body, i)
786 # False means no (or malformed) containing inset
787 value, start, end = "no inset", -1, -1
800 or value == "Flex URL"
804 line = line.replace("--", "-\\SpecialChar \\textcompwordmark{}\n-")
805 document.body[i : i + 1] = line.split("\n")
807 # Revert \twohyphens and \threehyphens:
809 while i < len(document.body):
810 line = document.body[i]
811 if not line.endswith("hyphens"):
813 elif line.endswith("\\twohyphens") or line.endswith("\\threehyphens"):
814 line = line.replace("\\twohyphens", "--")
815 line = line.replace("\\threehyphens", "---")
816 document.body[i] = line + document.body.pop(i + 1)
821 # order is important for the last three!
822 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
825 def is_part_of_converted_phrase(line, j, phrase):
826 "is phrase part of an already converted phrase?"
828 converted = "\\SpecialCharNoPassThru \\" + p
829 pos = j + len(phrase) - len(converted)
831 if line[pos : pos + len(converted)] == converted:
836 def convert_phrases(document):
837 "convert special phrases from plain text to \\SpecialCharNoPassThru"
839 if document.backend != "latex":
843 while i < len(document.body):
844 if document.body[i] and document.body[i][0] == "\\":
845 words = document.body[i].split()
848 and words[0] == "\\begin_inset"
849 and words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]
851 # must not replace anything in insets that store LaTeX contents in .lyx files
852 # (math and command insets without overridden read() and write() methods)
853 j = find_end_of_inset(document.body, i)
856 "Malformed LyX document: Can't find end of inset at line %d" % (i)
864 for phrase in phrases:
865 j = document.body[i].find(phrase)
868 if not is_part_of_converted_phrase(document.body[i], j, phrase):
869 front = document.body[i][:j]
870 back = document.body[i][j + len(phrase) :]
872 document.body.insert(i + 1, back)
873 # We cannot use SpecialChar since we do not know whether we are outside passThru
874 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
878 def revert_phrases(document):
879 "revert special phrases to plain text"
882 while i < len(document.body):
883 words = document.body[i].split()
886 and words[0] == "\\begin_inset"
887 and words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]
889 # see convert_phrases
890 j = find_end_of_inset(document.body, i)
893 "Malformed LyX document: Can't find end of Formula inset at line " + str(i)
900 for phrase in phrases:
901 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
902 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
903 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
905 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
906 document.body[i] = document.body[i].replace(
907 "\\SpecialCharNoPassThru \\" + phrase, phrase
912 and i + 1 < len(document.body)
914 document.body[i + 1].find("\\") != 0
915 or document.body[i + 1].find("\\SpecialChar") == 0
917 and len(document.body[i]) + len(document.body[i + 1]) <= 80
919 document.body[i] = document.body[i] + document.body[i + 1]
920 document.body[i + 1 : i + 2] = []
925 def convert_specialchar_internal(document, forward):
928 "\\textcompwordmark{}": "ligaturebreak",
929 "\\@.": "endofsentence",
930 "\\ldots{}": "ldots",
931 "\\menuseparator": "menuseparator",
932 "\\slash{}": "breakableslash",
933 "\\nobreakdash-": "nobreakdash",
936 "\\LaTeX2e": "LaTeX2e",
937 "\\LaTeX": "LaTeX", # must be after LaTeX2e
941 while i < len(document.body):
942 if document.body[i] and document.body[i][0] == "\\":
943 words = document.body[i].split()
946 and words[0] == "\\begin_inset"
947 and words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]
949 # see convert_phrases
950 j = find_end_of_inset(document.body, i)
953 "Malformed LyX document: Can't find end of %s inset at line %d"
961 if not "\\SpecialChar" in document.body[i]:
964 for key, value in specialchars.items():
966 document.body[i] = document.body[i].replace(
967 "\\SpecialChar " + key, "\\SpecialChar " + value
969 document.body[i] = document.body[i].replace(
970 "\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value
973 document.body[i] = document.body[i].replace(
974 "\\SpecialChar " + value, "\\SpecialChar " + key
976 document.body[i] = document.body[i].replace(
977 "\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key
982 def convert_specialchar(document):
983 "convert special characters to new syntax"
984 convert_specialchar_internal(document, True)
987 def revert_specialchar(document):
988 "convert special characters to old syntax"
989 convert_specialchar_internal(document, False)
992 def revert_georgian(document):
993 "Set the document language to English but assure Georgian output"
995 revert_language(document, "georgian", "georgian", "")
998 def revert_sigplan_doi(document):
999 "Reverts sigplanconf DOI layout to ERT"
1001 if document.textclass != "sigplanconf":
1006 i = find_token(document.body, "\\begin_layout DOI", i)
1009 j = find_end_of_layout(document.body, i)
1011 document.warning("Malformed LyX document: Can't find end of DOI layout")
1015 content = lyx2latex(document, document.body[i : j + 1])
1016 add_to_preamble(document, ["\\doi{" + content + "}"])
1017 del document.body[i : j + 1]
1018 # no need to reset i
1021 def revert_ex_itemargs(document):
1022 "Reverts \\item arguments of the example environments (Linguistics module) to TeX-code"
1024 if not "linguistics" in document.get_module_list():
1028 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
1030 i = find_token(document.body, "\\begin_inset Argument item:", i)
1033 j = find_end_of_inset(document.body, i)
1034 # Find containing paragraph layout
1035 parent = get_containing_layout(document.body, i)
1037 document.warning("Malformed LyX document: Can't find parent paragraph layout")
1041 layoutname = parent[0]
1042 if layoutname in example_layouts:
1043 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1044 endPlain = find_end_of_layout(document.body, beginPlain)
1045 content = document.body[beginPlain + 1 : endPlain]
1046 del document.body[i : j + 1]
1047 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
1048 document.body[parbeg:parbeg] = subst
1052 def revert_forest(document):
1053 "Reverts the forest environment (Linguistics module) to TeX-code"
1055 if not "linguistics" in document.get_module_list():
1060 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
1063 j = find_end_of_inset(document.body, i)
1065 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
1069 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1070 endPlain = find_end_of_layout(document.body, beginPlain)
1071 content = lyx2latex(document, document.body[beginPlain:endPlain])
1073 add_to_preamble(document, ["\\usepackage{forest}"])
1075 document.body[i : j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
1076 # no need to reset i
1079 def revert_glossgroup(document):
1080 "Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code"
1082 if not "linguistics" in document.get_module_list():
1087 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
1090 j = find_end_of_inset(document.body, i)
1093 "Malformed LyX document: Can't find end of GroupGlossedWords inset"
1098 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1099 endPlain = find_end_of_layout(document.body, beginPlain)
1100 content = lyx2verbatim(document, document.body[beginPlain:endPlain])
1102 document.body[i : j + 1] = ["{", "", content, "", "}"]
1103 # no need to reset i
1106 def revert_newgloss(document):
1107 "Reverts the new Glosse insets (Linguistics module) to the old format"
1109 if not "linguistics" in document.get_module_list():
1112 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1113 for glosse in glosses:
1116 i = find_token(document.body, glosse, i)
1119 j = find_end_of_inset(document.body, i)
1121 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1125 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
1126 endarg = find_end_of_inset(document.body, arg)
1129 argbeginPlain = find_token(
1130 document.body, "\\begin_layout Plain Layout", arg, endarg
1132 if argbeginPlain == -1:
1133 document.warning("Malformed LyX document: Can't find arg plain Layout")
1136 argendPlain = find_end_of_inset(document.body, argbeginPlain)
1137 argcontent = lyx2verbatim(
1138 document, document.body[argbeginPlain : argendPlain - 2]
1141 document.body[j:j] = [
1143 "\\begin_layout Plain Layout",
1150 # remove Arg insets and paragraph, if it only contains this inset
1152 document.body[arg - 1] == "\\begin_layout Plain Layout"
1153 and find_end_of_layout(document.body, arg - 1) == endarg + 3
1155 del document.body[arg - 1 : endarg + 4]
1157 del document.body[arg : endarg + 1]
1159 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1160 endPlain = find_end_of_layout(document.body, beginPlain)
1161 content = lyx2verbatim(document, document.body[beginPlain:endPlain])
1163 document.body[beginPlain + 1 : endPlain] = [content]
1166 # Dissolve ERT insets
1167 for glosse in glosses:
1170 i = find_token(document.body, glosse, i)
1173 j = find_end_of_inset(document.body, i)
1175 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1179 ert = find_token(document.body, "\\begin_inset ERT", i, j)
1182 ertend = find_end_of_inset(document.body, ert)
1184 document.warning("Malformed LyX document: Can't find end of ERT inset")
1187 ertcontent = get_ert(document.body, ert, True)
1188 document.body[ert : ertend + 1] = [ertcontent]
1192 def convert_newgloss(document):
1193 "Converts Glosse insets (Linguistics module) to the new format"
1195 if not "linguistics" in document.get_module_list():
1198 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1199 for glosse in glosses:
1202 i = find_token(document.body, glosse, i)
1205 j = find_end_of_inset(document.body, i)
1207 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1214 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
1215 if beginPlain == -1:
1217 endPlain = find_end_of_layout(document.body, beginPlain)
1219 document.warning("Malformed LyX document: Can't find end of Glosse layout")
1223 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
1224 if glt != -1 and document.body[glt + 1].startswith("glt"):
1225 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1226 argcontent = document.body[glt + 1 : endPlain]
1227 document.body[beginPlain + 1 : endPlain] = (
1229 "\\begin_inset Argument 1",
1232 "\\begin_layout Plain Layout",
1233 "\\begin_inset ERT",
1236 "\\begin_layout Plain Layout",
1251 content = document.body[beginPlain + 1 : endPlain]
1252 document.body[beginPlain + 1 : endPlain] = (
1254 "\\begin_inset ERT",
1257 "\\begin_layout Plain Layout",
1260 + ["\\end_layout", "", "\\end_inset"]
1263 endPlain = find_end_of_layout(document.body, beginPlain)
1265 j = find_end_of_inset(document.body, i)
1270 def convert_BoxFeatures(document):
1271 "adds new box features"
1275 i = find_token(document.body, "height_special", i)
1278 document.body[i + 1 : i + 1] = [
1279 'thickness "0.4pt"',
1286 def revert_BoxFeatures(document):
1287 "outputs new box features as TeX code"
1291 defaultThick = "0.4pt"
1292 defaultShadow = "4pt"
1294 i = find_token(document.body, "thickness", i)
1297 binset = find_token(document.body, "\\begin_inset Box", i - 11)
1298 if binset == -1 or binset != i - 11:
1300 continue # then "thickness" is is just a word in the text
1301 einset = find_end_of_inset(document.body, binset)
1303 document.warning("Malformed LyX document: Can't find end of box inset!")
1306 # read out the values
1307 beg = document.body[i].find('"')
1308 end = document.body[i].rfind('"')
1309 thickness = document.body[i][beg + 1 : end]
1310 beg = document.body[i + 1].find('"')
1311 end = document.body[i + 1].rfind('"')
1312 separation = document.body[i + 1][beg + 1 : end]
1313 beg = document.body[i + 2].find('"')
1314 end = document.body[i + 2].rfind('"')
1315 shadowsize = document.body[i + 2][beg + 1 : end]
1317 # first output the closing brace
1318 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1319 document.body[einset + 1 : einset + 1] = put_cmd_in_ert("}")
1320 # delete the specification
1321 del document.body[i : i + 3]
1322 # we have now the problem that if there is already \(f)colorbox in ERT around the inset
1323 # the ERT from this routine must be around it
1324 regexp = re.compile(r"^.*colorbox{.*$")
1325 pos = find_re(document.body, regexp, binset - 4)
1326 if pos != -1 and pos == binset - 4:
1330 # now output the lengths
1331 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1332 document.body[pos:pos] = put_cmd_in_ert("{")
1333 if thickness != defaultThick:
1334 document.body[pos + 5 : pos + 6] = ["{\\backslash fboxrule " + thickness]
1335 if separation != defaultSep and thickness == defaultThick:
1336 document.body[pos + 5 : pos + 6] = ["{\\backslash fboxsep " + separation]
1337 if separation != defaultSep and thickness != defaultThick:
1338 document.body[pos + 5 : pos + 6] = [
1339 "{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation
1342 shadowsize != defaultShadow
1343 and separation == defaultSep
1344 and thickness == defaultThick
1346 document.body[pos + 5 : pos + 6] = ["{\\backslash shadowsize " + shadowsize]
1348 shadowsize != defaultShadow
1349 and separation != defaultSep
1350 and thickness == defaultThick
1352 document.body[pos + 5 : pos + 6] = [
1353 "{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize
1356 shadowsize != defaultShadow
1357 and separation == defaultSep
1358 and thickness != defaultThick
1360 document.body[pos + 5 : pos + 6] = [
1361 "{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize
1364 shadowsize != defaultShadow
1365 and separation != defaultSep
1366 and thickness != defaultThick
1368 document.body[pos + 5 : pos + 6] = [
1369 "{\\backslash fboxrule "
1371 + "\\backslash fboxsep "
1373 + "\\backslash shadowsize "
1378 def convert_origin(document):
1379 "Insert the origin tag"
1381 i = find_token(document.header, "\\textclass ", 0)
1383 document.warning("Malformed LyX document: No \\textclass!!")
1385 if document.dir == "":
1389 if document.systemlyxdir and document.systemlyxdir != "":
1391 if os.path.isabs(document.dir):
1392 absdir = os.path.normpath(document.dir)
1394 absdir = os.path.normpath(os.path.abspath(document.dir))
1395 if os.path.isabs(document.systemlyxdir):
1396 abssys = os.path.normpath(document.systemlyxdir)
1398 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1399 relpath = os.path.relpath(absdir, abssys)
1400 if relpath.find("..") == 0:
1405 origin = document.dir.replace("\\", "/") + "/"
1407 origin = os.path.join("/systemlyxdir", relpath).replace("\\", "/") + "/"
1408 document.header[i:i] = ["\\origin " + origin]
1411 def revert_origin(document):
1412 "Remove the origin tag"
1414 i = find_token(document.header, "\\origin ", 0)
1416 document.warning("Malformed LyX document: No \\origin!!")
1418 del document.header[i]
1436 def revert_textcolor(document):
1437 "revert new \\textcolor colors to TeX code"
1443 i = find_token(document.body, "\\color ", i)
1447 for color in list(color_names):
1448 if document.body[i] == "\\color " + color:
1449 # register that xcolor must be loaded in the preamble
1454 ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"],
1456 # find the next \\color and/or the next \\end_layout
1457 j = find_token(document.body, "\\color", i + 1)
1458 k = find_token(document.body, "\\end_layout", i + 1)
1459 if j == -1 and k != -1:
1462 # first output the closing brace
1464 document.body[k:k] = put_cmd_in_ert("}")
1466 document.body[j:j] = put_cmd_in_ert("}")
1467 # now output the \textcolor command
1468 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1472 def convert_colorbox(document):
1473 "Add color settings for boxes."
1476 i = find_token(document.body, "shadowsize", i)
1479 # check whether this is really a LyX Box setting
1480 start, end = is_in_inset(document.body, i, "\\begin_inset Box")
1481 if end == -1 or find_token(document.body, "\\begin_layout", start, i) != -1:
1484 document.body[i + 1 : i + 1] = ['framecolor "black"', 'backgroundcolor "none"']
1488 def revert_colorbox(document):
1489 """Change box color settings to LaTeX code."""
1493 i = find_token(document.body, "\\begin_inset Box", i)
1497 j = find_end_of_inset(document.body, i)
1498 k = find_token(document.body, "\\begin_layout", i, j)
1500 document.warning("Malformed LyX document: no layout in Box inset!")
1503 # Get and delete colour settings:
1504 framecolor = get_quoted_value(document.body, "framecolor", i, k, delete=True)
1505 backcolor = get_quoted_value(document.body, "backgroundcolor", i, k + 1, delete=True)
1506 if not framecolor or not backcolor:
1507 document.warning("Malformed LyX document: color options not found in Box inset!")
1510 if framecolor == "black" and backcolor == "none": # default values
1514 # Emulate non-default colours with LaTeX code:
1515 einset = find_end_of_inset(document.body, i)
1517 document.warning("Malformed LyX document: Can't find end of box inset!")
1520 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1521 # insert the closing brace first (keeps indices 'i' and 'einset' valid)
1522 document.body[einset + 1 : einset + 1] = put_cmd_in_ert("}")
1523 # now insert the (f)color box command
1524 if "Box Boxed" in document.body[i]: # framed box, use \fcolorbox
1525 # change the box type (frame added by \fcolorbox)
1526 document.body[i] = "\\begin_inset Box Frameless"
1527 # ensure an inner box:
1529 if not set_bool_value(document.body, "has_inner_box", True, i + 3, i + 4):
1530 set_bool_value(document.body, "use_makebox", True, i + 6, i + 7)
1533 "Malformed LyX document: 'has_inner_box' or "
1534 "'use_makebox' option not found in box inset!"
1536 ertinset = put_cmd_in_ert("\\fcolorbox{%s}{%s}{" % (framecolor, backcolor))
1538 ertinset = put_cmd_in_ert("\\colorbox{%s}{" % backcolor)
1539 document.body[i:i] = ertinset + [""]
1543 def revert_mathmulticol(document):
1544 "Convert formulas to ERT if they contain multicolumns"
1548 i = find_token(document.body, "\\begin_inset Formula", i)
1551 j = find_end_of_inset(document.body, i)
1554 "Malformed LyX document: Can't find end of Formula inset at line " + str(i)
1558 lines = document.body[i:j]
1559 lines[0] = lines[0].replace("\\begin_inset Formula", "").lstrip()
1560 code = "\n".join(lines)
1565 n = code.find("\\multicolumn", k)
1566 # no need to convert degenerated multicolumn cells,
1567 # they work in old LyX versions as "math ERT"
1568 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1569 ert = put_cmd_in_ert(code)
1570 document.body[i : j + 1] = ert
1576 i = find_end_of_inset(document.body, i)
1581 def revert_jss(document):
1582 "Reverts JSS In_Preamble commands to ERT in preamble"
1584 if document.textclass != "jss":
1587 # at first revert the inset layouts because
1588 # they can be part of the In_Preamble layouts
1591 "Proglang": "proglang",
1596 for iltype in il_dict.keys():
1599 i = find_token(document.body, "\\begin_inset Flex " + iltype, i)
1603 if document.body[i] != "\\begin_inset Flex " + iltype:
1604 # Not an exact match!
1608 end = find_end_of_inset(document.body, i)
1610 document.warning("Malformed LyX document: No end of Flex " + iltype + " found!")
1613 document.body[end - 2 : end + 1] = put_cmd_in_ert("}")
1614 document.body[i : i + 4] = put_cmd_in_ert("\\%s{" % il_dict[iltype])
1617 # now revert the In_Preamble layouts
1621 "Plain Author": "Plainauthor",
1622 "Plain Title": "Plaintitle",
1623 "Short Title": "Shorttitle",
1624 "Abstract": "Abstract",
1625 "Keywords": "Keywords",
1626 "Plain Keywords": "Plainkeywords",
1627 "Address": "Address",
1629 for ipltype in ipl_dict.keys():
1632 i = find_token(document.body, "\\begin_layout " + ipltype, i)
1636 end = find_end_of_layout(document.body, i)
1639 "Malformed LyX document: Can't find end of " + ipltype + " layout"
1644 content = lyx2latex(document, document.body[i : end + 1])
1645 add_to_preamble(document, ["\\" + ipl_dict[ipltype] + "{" + content + "}"])
1646 del document.body[i : end + 1]
1653 i = find_token(document.body, "\\begin_inset Flex Code Chunk", i)
1657 end = find_end_of_inset(document.body, i)
1659 document.warning("Malformed LyX document: No end of Flex Code Chunk found!")
1663 document.body[end : end + 1] = [
1666 "\\begin_layout Standard",
1667 ] + put_cmd_in_ert("\\end{CodeChunk}")
1668 document.body[i : i + 2] = put_cmd_in_ert("\\begin{CodeChunk}")
1671 # finally handle the code layouts
1673 "Code Input": "CodeInput",
1674 "Code Output": "CodeOutput",
1677 for ctype in codes_dict.keys():
1680 i = find_token(document.body, "\\begin_layout " + ctype, i)
1683 if document.body[i] != "\\begin_layout " + ctype:
1684 # Not an exact match!
1687 end = find_end_of_layout(document.body, i)
1690 "Malformed LyX document: No end of " + ctype + " layout found!"
1695 # Handle subsequent layouts
1697 j = find_token(document.body, "\\begin_layout ", seq_end)
1698 if j == -1 or document.body[j] != "\\begin_layout " + ctype:
1700 this_end = find_end_of_layout(document.body, j)
1703 "Malformed LyX document: No end of " + ctype + " layout found!"
1708 document.body[seq_end + 1 : seq_end + 1] = [
1712 "\\begin_layout Standard",
1713 ] + put_cmd_in_ert("\\end{%s}" % codes_dict[ctype])
1717 document.body[k] = document.body[k].replace(
1718 "\\begin_layout " + ctype, "\\begin_layout Plain Layout"
1721 document.body[i : i + 1] = (
1722 ["\\end_layout", "", "\\begin_layout Standard"]
1723 + put_cmd_in_ert("\\begin{%s}" % codes_dict[ctype])
1727 "\\begin_layout Standard",
1729 "\\begin_inset ERT",
1732 "\\begin_layout Plain Layout",
1738 def convert_subref(document):
1739 "converts sub: ref prefixes to subref:"
1742 rx = re.compile(r"^name \"sub:(.+)$")
1745 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1748 j = find_end_of_inset(document.body, i)
1751 "Malformed LyX document: Can't find end of Label inset at line " + str(i)
1756 for p in range(i, j):
1757 m = rx.match(document.body[p])
1760 document.body[p] = 'name "subsec:' + label
1764 rx = re.compile(r"^reference \"sub:(.+)$")
1767 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1770 j = find_end_of_inset(document.body, i)
1773 "Malformed LyX document: Can't find end of Ref inset at line " + str(i)
1778 for p in range(i, j):
1779 m = rx.match(document.body[p])
1782 document.body[p] = 'reference "subsec:' + label
1787 def revert_subref(document):
1788 "reverts subref: ref prefixes to sub:"
1791 rx = re.compile(r"^name \"subsec:(.+)$")
1794 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1797 j = find_end_of_inset(document.body, i)
1800 "Malformed LyX document: Can't find end of Label inset at line " + str(i)
1805 for p in range(i, j):
1806 m = rx.match(document.body[p])
1809 document.body[p] = 'name "sub:' + label
1814 rx = re.compile(r"^reference \"subsec:(.+)$")
1817 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1820 j = find_end_of_inset(document.body, i)
1823 "Malformed LyX document: Can't find end of Ref inset at line " + str(i)
1828 for p in range(i, j):
1829 m = rx.match(document.body[p])
1832 document.body[p] = 'reference "sub:' + label
1837 def convert_nounzip(document):
1838 "remove the noUnzip parameter of graphics insets"
1840 rx = re.compile(r"\s*noUnzip\s*$")
1843 i = find_token(document.body, "\\begin_inset Graphics", i)
1846 j = find_end_of_inset(document.body, i)
1849 "Malformed LyX document: Can't find end of graphics inset at line " + str(i)
1854 k = find_re(document.body, rx, i, j)
1856 del document.body[k]
1861 def convert_revert_external_bbox(document, forward):
1862 "add units to bounding box of external insets"
1864 rx = re.compile(r"^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$")
1867 i = find_token(document.body, "\\begin_inset External", i)
1870 j = find_end_of_inset(document.body, i)
1873 "Malformed LyX document: Can't find end of external inset at line " + str(i)
1877 k = find_re(document.body, rx, i, j)
1881 tokens = document.body[k].split()
1883 for t in range(1, 5):
1886 for t in range(1, 5):
1887 tokens[t] = length_in_bp(tokens[t])
1888 document.body[k] = (
1889 "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + tokens[3] + " " + tokens[4]
1894 def convert_external_bbox(document):
1895 convert_revert_external_bbox(document, True)
1898 def revert_external_bbox(document):
1899 convert_revert_external_bbox(document, False)
1902 def revert_tcolorbox_1(document):
1903 "Reverts the Flex:Subtitle inset of tcolorbox to TeX-code"
1905 i = find_token(document.header, "tcolorbox", 0)
1912 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1916 flexEnd = find_end_of_inset(document.body, flex)
1918 document.warning("Malformed LyX document! No end of Flex Subtitle found.")
1922 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1923 flexEnd = find_end_of_inset(document.body, flex)
1925 document.warning("Malformed LyX document! No end of Flex Subtitle found.")
1928 revert_Argument_to_TeX_brace(document, flex, flexEnd, 2, 2, False, False, False)
1929 flexEnd = find_end_of_inset(document.body, flex)
1931 document.warning("Malformed LyX document! No end of Flex Subtitle found.")
1935 bp = find_token(document.body, "\\begin_layout Plain Layout", flex)
1937 document.warning("Malformed LyX document! No Flex Subtitle layout found.")
1941 ep = find_end_of_layout(document.body, bp)
1943 document.warning("Malformed LyX document! No end of layout found.")
1947 document.body[ep : flexEnd + 1] = put_cmd_in_ert("}")
1949 document.body[flex : bp + 1] = put_cmd_in_ert("\\tcbsubtitle")
1951 document.body[flex : bp + 1] = put_cmd_in_ert("\\tcbsubtitle{")
1955 def revert_tcolorbox_2(document):
1956 "Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code"
1958 i = find_token(document.header, "tcolorbox", 0)
1964 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1968 flexEnd = find_end_of_inset(document.body, flex)
1970 document.warning("Malformed LyX document! No end of Flex Raster Color Box found.")
1974 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1976 bp = find_token(document.body, "\\begin_layout Plain Layout", flex)
1979 "Malformed LyX document! No plain layout in Raster Color Box found."
1984 ep = find_end_of_layout(document.body, bp)
1986 document.warning("Malformed LyX document! No end of layout found.")
1990 flexEnd = find_end_of_inset(document.body, flex)
1992 document.warning("Malformed LyX document! No end of Flex Raster Color Box found.")
1995 document.body[ep : flexEnd + 1] = put_cmd_in_ert("\\end{tcbraster}")
1996 document.body[flex : bp + 1] = put_cmd_in_ert("\\begin{tcbraster}")
2000 def revert_tcolorbox_3(document):
2001 "Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code"
2003 i = find_token(document.header, "tcolorbox", 0)
2009 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
2013 flexEnd = find_end_of_inset(document.body, flex)
2015 document.warning("Malformed LyX document! No end of Flex Custom Color Box 1 found.")
2019 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
2020 flexEnd = find_end_of_inset(document.body, flex)
2022 document.warning("Malformed LyX document! No end of Flex Subtitle found.")
2025 revert_Argument_to_TeX_brace(document, flex, flexEnd, 2, 2, True, False, False)
2027 bp = find_token(document.body, "\\begin_layout Plain Layout", flex)
2030 "Malformed LyX document! No plain layout in Custom Color Box 1 found."
2035 ep = find_end_of_layout(document.body, bp)
2037 document.warning("Malformed LyX document! No end of layout found.")
2041 flexEnd = find_end_of_inset(document.body, flex)
2043 document.warning("Malformed LyX document! No end of Flex Custom Color Box 1 found.")
2047 document.body[ep : flexEnd + 1] = put_cmd_in_ert("{}\\end{cBoxA}")
2048 document.body[flex : bp + 1] = put_cmd_in_ert("\\begin{cBoxA}")
2052 def revert_tcolorbox_4(document):
2053 "Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code"
2055 i = find_token(document.header, "tcolorbox", 0)
2061 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
2065 flexEnd = find_end_of_inset(document.body, flex)
2067 document.warning("Malformed LyX document! No end of Flex Custom Color Box 2 found.")
2071 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
2072 flexEnd = find_end_of_inset(document.body, flex)
2074 document.warning("Malformed LyX document! No end of Flex Subtitle found.")
2077 revert_Argument_to_TeX_brace(document, flex, flexEnd, 2, 2, True, False, False)
2078 flexEnd = find_end_of_inset(document.body, flex)
2080 document.warning("Malformed LyX document! No end of Flex Custom Color Box 2 found.")
2084 bp = find_token(document.body, "\\begin_layout Plain Layout", flex)
2087 "Malformed LyX document! No plain layout in Custom Color Box 2 found."
2092 ep = find_end_of_layout(document.body, bp)
2094 document.warning("Malformed LyX document! No end of layout found.")
2098 document.body[ep : flexEnd + 1] = put_cmd_in_ert("{}\\end{cBoxB}")
2099 document.body[flex : bp + 1] = put_cmd_in_ert("\\begin{cBoxB}")
2103 def revert_tcolorbox_5(document):
2104 "Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code"
2106 i = find_token(document.header, "tcolorbox", 0)
2112 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
2116 flexEnd = find_end_of_inset(document.body, flex)
2118 document.warning("Malformed LyX document! No end of Flex Custom Color Box 3 found.")
2122 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
2123 flexEnd = find_end_of_inset(document.body, flex)
2125 document.warning("Malformed LyX document! No end of Flex Subtitle found.")
2128 revert_Argument_to_TeX_brace(document, flex, flexEnd, 2, 2, True, False, False)
2129 flexEnd = find_end_of_inset(document.body, flex)
2131 document.warning("Malformed LyX document! No end of Flex Custom Color Box 3 found.")
2135 bp = find_token(document.body, "\\begin_layout Plain Layout", flex)
2138 "Malformed LyX document! No plain layout in Custom Color Box 3 found."
2143 ep = find_end_of_layout(document.body, bp)
2145 document.warning("Malformed LyX document! No end of layout found.")
2149 document.body[ep : flexEnd + 1] = put_cmd_in_ert("{}\\end{cBoxC}")
2150 document.body[flex : bp + 1] = put_cmd_in_ert("\\begin{cBoxC}")
2154 def revert_tcolorbox_6(document):
2155 "Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code"
2157 i = find_token(document.header, "tcolorbox", 0)
2163 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
2167 flexEnd = find_end_of_inset(document.body, flex)
2169 document.warning("Malformed LyX document! No end of Flex Custom Color Box 4 found.")
2173 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
2174 flexEnd = find_end_of_inset(document.body, flex)
2176 document.warning("Malformed LyX document! No end of Flex Subtitle found.")
2179 revert_Argument_to_TeX_brace(document, flex, flexEnd, 2, 2, True, False, False)
2180 flexEnd = find_end_of_inset(document.body, flex)
2182 document.warning("Malformed LyX document! No end of Flex Custom Color Box 4 found.")
2186 bp = find_token(document.body, "\\begin_layout Plain Layout", flex)
2189 "Malformed LyX document! No plain layout in Custom Color Box 4 found."
2194 ep = find_end_of_layout(document.body, bp)
2196 document.warning("Malformed LyX document! No end of layout found.")
2200 document.body[ep : flexEnd + 1] = put_cmd_in_ert("{}\\end{cBoxD}")
2201 document.body[flex : bp + 1] = put_cmd_in_ert("\\begin{cBoxD}")
2205 def revert_tcolorbox_7(document):
2206 "Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code"
2208 i = find_token(document.header, "tcolorbox", 0)
2214 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
2218 flexEnd = find_end_of_inset(document.body, flex)
2220 document.warning("Malformed LyX document! No end of Flex Custom Color Box 5 found.")
2224 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
2225 flexEnd = find_end_of_inset(document.body, flex)
2227 document.warning("Malformed LyX document! No end of Flex Subtitle found.")
2230 revert_Argument_to_TeX_brace(document, flex, flexEnd, 2, 2, True, False, False)
2231 flexEnd = find_end_of_inset(document.body, flex)
2233 document.warning("Malformed LyX document! No end of Flex Custom Color Box 5 found.")
2237 bp = find_token(document.body, "\\begin_layout Plain Layout", flex)
2240 "Malformed LyX document! No plain layout in Custom Color Box 5 found."
2245 ep = find_end_of_layout(document.body, bp)
2247 document.warning("Malformed LyX document! No end of layout found.")
2251 document.body[ep : flexEnd + 1] = put_cmd_in_ert("{}\\end{cBoxE}")
2252 document.body[flex : bp + 1] = put_cmd_in_ert("\\begin{cBoxE}")
2256 def revert_tcolorbox_8(document):
2257 "Reverts the layout New Color Box Type of tcolorbox to TeX-code"
2261 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
2265 j = find_end_of_layout(document.body, i)
2268 "Malformed LyX document! No end of New Color Box Type layout found."
2273 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, True)
2274 j = find_end_of_layout(document.body, i)
2277 "Malformed LyX document! No end of New Color Box Type layout found."
2281 revert_Argument_to_TeX_brace(document, i, j, 2, 2, False, False, True)
2282 j = find_end_of_layout(document.body, i)
2285 "Malformed LyX document! No end of New Color Box Type layout found."
2289 revert_Argument_to_TeX_brace(document, i, j, 3, 4, False, True, False)
2290 j = find_end_of_layout(document.body, i)
2293 "Malformed LyX document! No end of New Color Box Type layout found."
2297 document.body[i] = document.body[i].replace(
2298 "\\begin_layout New Color Box Type", "\\begin_layout Standard"
2300 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
2301 k = find_end_of_inset(document.body, j)
2302 document.body[k + 2 : j + 2] = put_cmd_in_ert("{") + [
2303 "\\begin_inset ERT",
2305 "\\begin_layout Plain Layout",
2307 j = find_token(document.body, "\\begin_layout Standard", j + 1)
2308 document.body[j - 2 : j - 2] = ["\\end_layout", "\\end_inset"] + put_cmd_in_ert("}")
2312 def revert_moderncv_1(document):
2313 "Reverts the new inset of moderncv to TeX-code in preamble"
2315 if document.textclass != "moderncv":
2321 # at first revert the new styles
2323 i = find_token(document.body, "\\begin_layout CVIcons", 0)
2326 j = find_end_of_layout(document.body, i)
2328 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
2331 content = lyx2latex(document, document.body[i : j + 1])
2332 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
2333 del document.body[i : j + 1]
2335 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
2338 j = find_end_of_layout(document.body, i)
2340 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
2343 content = lyx2latex(document, document.body[i : j + 1])
2344 add_to_preamble(document, ["\\setlength{\\hintscolumnwidth}{" + content + "}"])
2345 del document.body[i : j + 1]
2346 # now change the new styles to the obsolete ones
2348 i = find_token(document.body, "\\begin_layout Name", 0)
2351 j = find_end_of_layout(document.body, i)
2353 document.warning("Malformed LyX document: Can't find end of Name layout")
2356 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
2357 if lineArg > j and j != 0:
2360 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
2361 # we have to assure that no other inset is in the Argument
2362 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
2363 endInset = find_token(document.body, "\\end_inset", beginPlain)
2366 while beginInset < endInset and beginInset != -1:
2367 beginInset = find_token(document.body, "\\begin_inset", k)
2368 endInset = find_token(document.body, "\\end_inset", l)
2371 Arg2 = document.body[l + 5 : l + 6]
2373 document.body[i : i + 1] = ["\\begin_layout FirstName"]
2374 # delete the Argument inset
2375 del document.body[endInset - 2 : endInset + 3]
2376 del document.body[lineArg : beginPlain + 1]
2377 document.body[i + 4 : i + 4] = (
2378 ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
2382 def revert_moderncv_2(document):
2383 "Reverts the phone inset of moderncv to the obsoleted mobile or fax"
2385 if document.textclass != "moderncv":
2392 i = find_token(document.body, "\\begin_layout Phone", i)
2395 j = find_end_of_layout(document.body, i)
2397 document.warning("Malformed LyX document: Can't find end of Phone layout")
2400 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
2401 if lineArg > j and j != 0:
2405 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
2406 # we have to assure that no other inset is in the Argument
2407 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
2408 endInset = find_token(document.body, "\\end_inset", beginPlain)
2411 while beginInset < endInset and beginInset != -1:
2412 beginInset = find_token(document.body, "\\begin_inset", k)
2413 endInset = find_token(document.body, "\\end_inset", l)
2416 Arg = document.body[beginPlain + 1 : beginPlain + 2]
2418 if Arg[0] == "mobile":
2419 document.body[i : i + 1] = ["\\begin_layout Mobile"]
2421 document.body[i : i + 1] = ["\\begin_layout Fax"]
2422 # delete the Argument inset
2423 del document.body[endInset - 2 : endInset + 1]
2424 del document.body[lineArg : beginPlain + 3]
2428 def convert_moderncv_phone(document):
2429 "Convert the Fax and Mobile inset of moderncv to the new phone inset"
2431 if document.textclass != "moderncv":
2442 rx = re.compile(r"^\\begin_layout (\S+)$")
2444 # substitute \fax and \mobile by \phone[fax] and \phone[mobile], respectively
2445 i = find_token(document.body, "\\begin_layout", i)
2449 m = rx.match(document.body[i])
2453 if val not in list(phone_dict.keys()):
2456 j = find_end_of_layout(document.body, i)
2458 document.warning("Malformed LyX document: Can't find end of Mobile layout")
2462 document.body[i : i + 1] = [
2463 "\\begin_layout Phone",
2464 "\\begin_inset Argument 1",
2467 "\\begin_layout Plain Layout",
2476 def convert_moderncv_name(document):
2477 "Convert the FirstName and LastName layout of moderncv to the general Name layout"
2479 if document.textclass != "moderncv":
2482 fnb = 0 # Begin of FirstName inset
2483 fne = 0 # End of FirstName inset
2484 lnb = 0 # Begin of LastName (FamilyName) inset
2485 lne = 0 # End of LastName (FamilyName) inset
2486 nb = 0 # Begin of substituting Name inset
2487 ne = 0 # End of substituting Name inset
2488 FirstName = [] # FirstName content
2489 FamilyName = [] # LastName content
2493 fnb = find_token(document.body, "\\begin_layout FirstName", fnb)
2495 fne = find_end_of_layout(document.body, fnb)
2497 document.warning("Malformed LyX document: Can't find end of FirstName layout")
2499 FirstName = document.body[fnb + 1 : fne]
2501 lnb = find_token(document.body, "\\begin_layout FamilyName", lnb)
2503 lne = find_end_of_layout(document.body, lnb)
2505 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
2507 FamilyName = document.body[lnb + 1 : lne]
2508 # Determine the region for the substituting Name layout
2509 if fnb == -1 and lnb == -1: # Neither FirstName nor FamilyName exists -> Do nothing
2511 elif fnb == -1: # Only FamilyName exists -> New Name insets replaces that
2514 elif lnb == -1: # Only FirstName exists -> New Name insets replaces that
2517 elif fne > lne: # FirstName position before FamilyName -> New Name insets spans
2518 nb = lnb # from FamilyName begin
2519 ne = fne # to FirstName end
2520 else: # FirstName position before FamilyName -> New Name insets spans
2521 nb = fnb # from FirstName begin
2522 ne = lne # to FamilyName end
2524 # Insert the substituting layout now. If FirstName exists, use an otpional argument.
2526 document.body[nb : ne + 1] = (
2527 ["\\begin_layout Name"] + FamilyName + ["\\end_layout", ""]
2530 document.body[nb : ne + 1] = (
2532 "\\begin_layout Name",
2533 "\\begin_inset Argument 1",
2536 "\\begin_layout Plain Layout",
2539 + ["\\end_layout", "", "\\end_inset", ""]
2541 + ["\\end_layout", ""]
2545 def revert_achemso(document):
2546 "Reverts the flex inset Latin to TeX code"
2548 if document.textclass != "achemso":
2553 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2555 j = find_end_of_inset(document.body, i)
2559 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2560 endPlain = find_end_of_layout(document.body, beginPlain)
2561 content = lyx2latex(document, document.body[beginPlain:endPlain])
2562 document.body[i : j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2564 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2572 "\\font_typewriter",
2577 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2578 fontquotes = [True, True, True, True, False, False]
2581 def convert_fontsettings(document):
2582 "Duplicate font settings"
2584 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2586 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2587 use_non_tex_fonts = "false"
2589 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2591 for f in fontsettings:
2592 i = find_token(document.header, f + " ", 0)
2594 document.warning("Malformed LyX document: No " + f + "!")
2596 # note that with i = -1, this will insert at the end
2598 value = fontdefaults[j]
2600 value = document.header[i][len(f) :].strip()
2602 if use_non_tex_fonts == "true":
2603 document.header[i : i + 1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2605 document.header[i : i + 1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2607 if use_non_tex_fonts == "true":
2608 document.header[i : i + 1] = [f + " " + fontdefaults[j] + " " + value]
2610 document.header[i : i + 1] = [f + " " + value + " " + fontdefaults[j]]
2614 def revert_fontsettings(document):
2615 "Merge font settings"
2617 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2619 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2620 use_non_tex_fonts = "false"
2622 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2624 for f in fontsettings:
2625 i = find_token(document.header, f + " ", 0)
2627 document.warning("Malformed LyX document: No " + f + "!")
2630 line = get_value(document.header, f, i)
2633 q2 = line.find('"', q1 + 1)
2634 q3 = line.find('"', q2 + 1)
2635 q4 = line.find('"', q3 + 1)
2636 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2637 document.warning("Malformed LyX document: Missing quotes!")
2640 if use_non_tex_fonts == "true":
2641 document.header[i : i + 1] = [f + " " + line[q3 + 1 : q4]]
2643 document.header[i : i + 1] = [f + " " + line[q1 + 1 : q2]]
2645 if use_non_tex_fonts == "true":
2646 document.header[i : i + 1] = [f + " " + line.split()[1]]
2648 document.header[i : i + 1] = [f + " " + line.split()[0]]
2652 def revert_solution(document):
2653 "Reverts the solution environment of the theorem module to TeX code"
2655 # Do we use one of the modules that provides Solution?
2657 mods = document.get_module_list()
2660 mod == "theorems-std"
2661 or mod == "theorems-bytype"
2662 or mod == "theorems-ams"
2663 or mod == "theorems-ams-bytype"
2674 i = find_token(document.body, "\\begin_layout Solution", i)
2678 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2679 if is_starred == True:
2681 LyXName = "Solution*"
2682 theoremName = "newtheorem*"
2685 LyXName = "Solution"
2686 theoremName = "newtheorem"
2688 j = find_end_of_layout(document.body, i)
2690 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2694 # if this is not a consecutive env, add start command
2697 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2699 # has this a consecutive theorem of same type?
2700 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2702 # if this is not followed by a consecutive env, add end command
2704 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + [
2708 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2710 add_to_preamble(document, "\\theoremstyle{definition}")
2711 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2713 document, "\\%s{%s}{\\protect\\solutionname}" % (theoremName, LaTeXName)
2715 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2718 "\\%s{%s}[thm]{\\protect\\solutionname}" % (theoremName, LaTeXName),
2721 add_to_preamble(document, "\\providecommand{\\solutionname}{Solution}")
2725 def revert_verbatim_star(document):
2726 from lyx_2_1 import revert_verbatim
2728 revert_verbatim(document, True)
2731 def convert_save_props(document):
2732 "Add save_transient_properties parameter."
2733 i = find_token(document.header, "\\begin_header", 0)
2735 document.warning("Malformed lyx document: Missing '\\begin_header'.")
2737 document.header.insert(i + 1, "\\save_transient_properties true")
2740 def revert_save_props(document):
2741 "Remove save_transient_properties parameter."
2742 i = find_token(document.header, "\\save_transient_properties", 0)
2745 del document.header[i]
2748 def convert_info_tabular_feature(document):
2750 return arg.replace("inset-modify tabular", "tabular-feature")
2752 convert_info_insets(document, "shortcut(s)?|icon", f)
2755 def revert_info_tabular_feature(document):
2757 return arg.replace("tabular-feature", "inset-modify tabular")
2759 convert_info_insets(document, "shortcut(s)?|icon", f)
2766 supported_versions = ["2.2.0", "2.2"]
2768 [475, [convert_separator]],
2769 # nothing to do for 476: We consider it a bug that older versions
2770 # did not load amsmath automatically for these commands, and do not
2771 # want to hardcode amsmath off.
2777 [481, [convert_dashes]],
2778 [482, [convert_phrases]],
2779 [483, [convert_specialchar]],
2784 [488, [convert_newgloss]],
2785 [489, [convert_BoxFeatures]],
2786 [490, [convert_origin]],
2788 [492, [convert_colorbox]],
2791 [495, [convert_subref]],
2792 [496, [convert_nounzip]],
2793 [497, [convert_external_bbox]],
2795 [499, [convert_moderncv_phone, convert_moderncv_name]],
2797 [501, [convert_fontsettings]],
2800 [504, [convert_save_props]],
2802 [506, [convert_info_tabular_feature]],
2803 [507, [convert_longtable_label]],
2804 [508, [convert_parbreak]],
2808 [507, [revert_parbreak]],
2809 [506, [revert_longtable_label]],
2810 [505, [revert_info_tabular_feature]],
2812 [503, [revert_save_props]],
2813 [502, [revert_verbatim_star]],
2814 [501, [revert_solution]],
2815 [500, [revert_fontsettings]],
2816 [499, [revert_achemso]],
2817 [498, [revert_moderncv_1, revert_moderncv_2]],
2831 [496, [revert_external_bbox]],
2832 [495, []], # nothing to do since the noUnzip parameter was optional
2833 [494, [revert_subref]],
2834 [493, [revert_jss]],
2835 [492, [revert_mathmulticol]],
2836 [491, [revert_colorbox]],
2837 [490, [revert_textcolor]],
2838 [489, [revert_origin]],
2839 [488, [revert_BoxFeatures]],
2840 [487, [revert_newgloss, revert_glossgroup]],
2841 [486, [revert_forest]],
2842 [485, [revert_ex_itemargs]],
2843 [484, [revert_sigplan_doi]],
2844 [483, [revert_georgian]],
2845 [482, [revert_specialchar]],
2846 [481, [revert_phrases]],
2847 [480, [revert_dashes]],
2848 [479, [revert_question_env]],
2849 [478, [revert_beamer_lemma]],
2850 [477, [revert_xarrow]],
2851 [476, [revert_swissgerman]],
2852 [475, [revert_smash]],
2853 [474, [revert_separator]],
2857 if __name__ == "__main__":