1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # -*- coding: utf-8 -*-
4 # Copyright (C) 2011 The LyX team
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 """ Convert files to the file format generated by lyx 2.2"""
26 # Uncomment only what you need to import, please.
28 #from parser_tools import find_token, find_end_of, find_tokens, \
29 # find_token_exact, find_end_of_inset, find_end_of_layout, \
30 # find_token_backwards, is_in_inset, get_value, get_quoted_value, \
31 # del_token, check_token, get_option_value
33 from lyx2lyx_tools import add_to_preamble, put_cmd_in_ert, lyx2latex#, \
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 ###############################################################################
43 ### Conversion and reversion routines
45 ###############################################################################
47 def convert_separator(document):
49 Convert layout separators to separator insets and add (LaTeX) paragraph
50 breaks in order to mimic previous LaTeX export.
53 parins = ["\\begin_inset Separator parbreak", "\\end_inset", ""]
54 parlay = ["\\begin_layout Standard", "\\begin_inset Separator parbreak",
55 "\\end_inset", "", "\\end_layout", ""]
67 i = find_token(document.body, "\\begin_deeper", i)
71 j = find_token_backwards(document.body, "\\end_layout", i-1)
73 # reset any text style before inserting the inset
74 lay = get_containing_layout(document.body, j-1)
76 content = "\n".join(document.body[lay[1]:lay[2]])
77 for val in list(sty_dict.keys()):
78 if content.find("\\%s" % val) != -1:
79 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
82 document.body[j:j] = parins
83 i = i + len(parins) + 1
89 i = find_token(document.body, "\\align", i)
93 lay = get_containing_layout(document.body, i)
94 if lay != False and lay[0] == "Plain Layout":
98 j = find_token_backwards(document.body, "\\end_layout", i-1)
100 lay = get_containing_layout(document.body, j-1)
101 if lay != False and lay[0] == "Standard" \
102 and find_token(document.body, "\\align", lay[1], lay[2]) == -1 \
103 and find_token(document.body, "\\begin_inset VSpace", lay[1], lay[2]) == -1:
104 # reset any text style before inserting the inset
105 content = "\n".join(document.body[lay[1]:lay[2]])
106 for val in list(sty_dict.keys()):
107 if content.find("\\%s" % val) != -1:
108 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
111 document.body[j:j] = parins
112 i = i + len(parins) + 1
118 regexp = re.compile(r'^\\begin_layout (?:(-*)|(\s*))(Separator|EndOfSlide)(?:(-*)|(\s*))$', re.IGNORECASE)
122 i = find_re(document.body, regexp, i)
126 j = find_end_of_layout(document.body, i)
128 document.warning("Malformed LyX document: Missing `\\end_layout'.")
131 lay = get_containing_layout(document.body, j-1)
133 lines = document.body[lay[3]:lay[2]]
137 document.body[i:j+1] = parlay
139 document.body[i+1:i+1] = lines
141 i = i + len(parlay) + len(lines) + 1
144 def revert_separator(document):
145 " Revert separator insets to layout separators "
147 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
148 if document.textclass in beamer_classes:
149 beglaysep = "\\begin_layout Separator"
151 beglaysep = "\\begin_layout --Separator--"
153 parsep = [beglaysep, "", "\\end_layout", ""]
154 comert = ["\\begin_inset ERT", "status collapsed", "",
155 "\\begin_layout Plain Layout", "%", "\\end_layout",
156 "", "\\end_inset", ""]
157 empert = ["\\begin_inset ERT", "status collapsed", "",
158 "\\begin_layout Plain Layout", " ", "\\end_layout",
159 "", "\\end_inset", ""]
163 i = find_token(document.body, "\\begin_inset Separator", i)
167 lay = get_containing_layout(document.body, i)
169 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
176 kind = get_value(document.body, "\\begin_inset Separator", i, i+1, "plain").split()[1]
177 before = document.body[beg+1:i]
178 something_before = len(before) > 0 and len("".join(before)) > 0
179 j = find_end_of_inset(document.body, i)
180 after = document.body[j+1:end]
181 something_after = len(after) > 0 and len("".join(after)) > 0
183 beg = beg + len(before) + 1
184 elif something_before:
185 document.body[i:i] = ["\\end_layout", ""]
193 document.body[beg:j+1] = empert
196 document.body[beg:j+1] = comert
200 if layoutname == "Standard":
201 if not something_before:
202 document.body[beg:j+1] = parsep
204 document.body[i:i] = ["", "\\begin_layout Standard"]
207 document.body[beg:j+1] = ["\\begin_layout Standard"]
210 document.body[beg:j+1] = ["\\begin_deeper"]
212 end = end + 1 - (j + 1 - beg)
213 if not something_before:
214 document.body[i:i] = parsep
216 end = end + len(parsep)
217 document.body[i:i] = ["\\begin_layout Standard"]
218 document.body[end+2:end+2] = ["", "\\end_deeper", ""]
221 next_par_is_aligned = False
222 k = find_nonempty_line(document.body, end+1)
223 if k != -1 and check_token(document.body[k], "\\begin_layout"):
224 lay = get_containing_layout(document.body, k)
225 next_par_is_aligned = lay != False and \
226 find_token(document.body, "\\align", lay[1], lay[2]) != -1
227 if k != -1 and not next_par_is_aligned \
228 and not check_token(document.body[k], "\\end_deeper") \
229 and not check_token(document.body[k], "\\begin_deeper"):
230 if layoutname == "Standard":
231 document.body[beg:j+1] = [beglaysep]
234 document.body[beg:j+1] = ["\\begin_deeper", beglaysep]
235 end = end + 2 - (j + 1 - beg)
236 document.body[end+1:end+1] = ["", "\\end_deeper", ""]
240 del document.body[i:end+1]
242 del document.body[i:end-1]
247 def revert_smash(document):
248 " Set amsmath to on if smash commands are used "
250 commands = ["smash[t]", "smash[b]", "notag"]
251 i = find_token(document.header, "\\use_package amsmath", 0)
253 document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
255 value = get_value(document.header, "\\use_package amsmath", i).split()[1]
257 # nothing to do if package is not auto but on or off
261 j = find_token(document.body, '\\begin_inset Formula', j)
264 k = find_end_of_inset(document.body, j)
266 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
269 code = "\n".join(document.body[j:k])
271 if code.find("\\%s" % c) != -1:
272 # set amsmath to on, since it is loaded by the newer format
273 document.header[i] = "\\use_package amsmath 2"
278 def revert_swissgerman(document):
279 " Set language german-ch-old to german "
281 if document.language == "german-ch-old":
282 document.language = "german"
283 i = find_token(document.header, "\\language", 0)
285 document.header[i] = "\\language german"
288 j = find_token(document.body, "\\lang german-ch-old", j)
291 document.body[j] = document.body[j].replace("\\lang german-ch-old", "\\lang german")
295 def revert_use_package(document, pkg, commands, oldauto):
296 # oldauto defines how the version we are reverting to behaves:
297 # if it is true, the old version uses the package automatically.
298 # if it is false, the old version never uses the package.
299 regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
300 i = find_re(document.header, regexp, 0)
301 value = "1" # default is auto
303 value = get_value(document.header, "\\use_package" , i).split()[1]
304 del document.header[i]
305 if value == "2": # on
306 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
307 elif value == "1" and not oldauto: # auto
310 i = find_token(document.body, '\\begin_inset Formula', i)
313 j = find_end_of_inset(document.body, i)
315 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
318 code = "\n".join(document.body[i:j])
320 if code.find("\\%s" % c) != -1:
321 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
326 mathtools_commands = ["xhookrightarrow", "xhookleftarrow", "xRightarrow", \
327 "xrightharpoondown", "xrightharpoonup", "xrightleftharpoons", \
328 "xLeftarrow", "xleftharpoondown", "xleftharpoonup", \
329 "xleftrightarrow", "xLeftrightarrow", "xleftrightharpoons", \
332 def revert_xarrow(document):
333 "remove use_package mathtools"
334 revert_use_package(document, "mathtools", mathtools_commands, False)
337 def revert_beamer_lemma(document):
338 " Reverts beamer lemma layout to ERT "
340 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
341 if document.textclass not in beamer_classes:
347 i = find_token(document.body, "\\begin_layout Lemma", i)
350 j = find_end_of_layout(document.body, i)
352 document.warning("Malformed LyX document: Can't find end of Lemma layout")
355 arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j)
356 endarg1 = find_end_of_inset(document.body, arg1)
357 arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j)
358 endarg2 = find_end_of_inset(document.body, arg2)
362 beginPlain1 = find_token(document.body, "\\begin_layout Plain Layout", arg1, endarg1)
363 if beginPlain1 == -1:
364 document.warning("Malformed LyX document: Can't find arg1 plain Layout")
367 endPlain1 = find_end_of_inset(document.body, beginPlain1)
368 content1 = document.body[beginPlain1 + 1 : endPlain1 - 2]
369 subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">")
371 beginPlain2 = find_token(document.body, "\\begin_layout Plain Layout", arg2, endarg2)
372 if beginPlain2 == -1:
373 document.warning("Malformed LyX document: Can't find arg2 plain Layout")
376 endPlain2 = find_end_of_inset(document.body, beginPlain2)
377 content2 = document.body[beginPlain2 + 1 : endPlain2 - 2]
378 subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]")
382 del document.body[arg2 : endarg2 + 1]
384 del document.body[arg1 : endarg1 + 1]
386 del document.body[arg1 : endarg1 + 1]
388 del document.body[arg2 : endarg2 + 1]
390 # index of end layout has probably changed
391 j = find_end_of_layout(document.body, i)
393 document.warning("Malformed LyX document: Can't find end of Lemma layout")
399 # if this is not a consecutive env, add start command
401 begcmd = put_cmd_in_ert("\\begin{lemma}")
403 # has this a consecutive lemma?
404 consecutive = document.body[j + 2] == "\\begin_layout Lemma"
406 # if this is not followed by a consecutive env, add end command
408 document.body[j : j + 1] = put_cmd_in_ert("\\end{lemma}") + ["\\end_layout"]
410 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd + subst1 + subst2
416 def revert_question_env(document):
418 Reverts question and question* environments of
419 theorems-ams-extended-bytype module to ERT
422 # Do we use theorems-ams-extended-bytype module?
424 mods = document.get_module_list()
426 if mod == "theorems-ams-extended-bytype":
436 i = find_token(document.body, "\\begin_layout Question", i)
440 starred = document.body[i] == "\\begin_layout Question*"
442 j = find_end_of_layout(document.body, i)
444 document.warning("Malformed LyX document: Can't find end of Question layout")
448 # if this is not a consecutive env, add start command
452 begcmd = put_cmd_in_ert("\\begin{question*}")
454 begcmd = put_cmd_in_ert("\\begin{question}")
456 # has this a consecutive theorem of same type?
459 consecutive = document.body[j + 2] == "\\begin_layout Question*"
461 consecutive = document.body[j + 2] == "\\begin_layout Question"
463 # if this is not followed by a consecutive env, add end command
466 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
468 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
470 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
472 add_to_preamble(document, "\\providecommand{\questionname}{Question}")
475 add_to_preamble(document, "\\theoremstyle{plain}\n" \
476 "\\newtheorem*{question*}{\\protect\\questionname}")
478 add_to_preamble(document, "\\theoremstyle{plain}\n" \
479 "\\newtheorem{question}{\\protect\\questionname}")
484 def convert_dashes(document):
485 "convert -- and --- to \\twohyphens and \\threehyphens"
487 if document.backend != "latex":
491 while i < len(document.body):
492 words = document.body[i].split()
493 if len(words) > 1 and words[0] == "\\begin_inset" and \
494 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
495 # must not replace anything in insets that store LaTeX contents in .lyx files
496 # (math and command insets withut overridden read() and write() methods
497 # filtering out IPA makes Text::readParToken() more simple
498 # skip ERT as well since it is not needed there
499 j = find_end_of_inset(document.body, i)
501 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
507 j = document.body[i].find("--")
510 front = document.body[i][:j]
511 back = document.body[i][j+2:]
512 # We can have an arbitrary number of consecutive hyphens.
513 # These must be split into the corresponding number of two and three hyphens
514 # We must match what LaTeX does: First try emdash, then endash, then single hyphen
515 if back.find("-") == 0:
518 document.body.insert(i+1, back)
519 document.body[i] = front + "\\threehyphens"
522 document.body.insert(i+1, back)
523 document.body[i] = front + "\\twohyphens"
527 def revert_dashes(document):
528 "convert \\twohyphens and \\threehyphens to -- and ---"
531 while i < len(document.body):
532 words = document.body[i].split()
533 if len(words) > 1 and words[0] == "\\begin_inset" and \
534 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
536 j = find_end_of_inset(document.body, i)
538 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
544 if document.body[i].find("\\twohyphens") >= 0:
545 document.body[i] = document.body[i].replace("\\twohyphens", "--")
547 if document.body[i].find("\\threehyphens") >= 0:
548 document.body[i] = document.body[i].replace("\\threehyphens", "---")
550 if replaced and i+1 < len(document.body) and \
551 (document.body[i+1].find("\\") != 0 or \
552 document.body[i+1].find("\\twohyphens") == 0 or
553 document.body[i+1].find("\\threehyphens") == 0) and \
554 len(document.body[i]) + len(document.body[i+1]) <= 80:
555 document.body[i] = document.body[i] + document.body[i+1]
556 document.body[i+1:i+2] = []
561 # order is important for the last three!
562 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
564 def is_part_of_converted_phrase(line, j, phrase):
565 "is phrase part of an already converted phrase?"
567 converted = "\\SpecialCharNoPassThru \\" + p
568 pos = j + len(phrase) - len(converted)
570 if line[pos:pos+len(converted)] == converted:
575 def convert_phrases(document):
576 "convert special phrases from plain text to \\SpecialCharNoPassThru"
578 if document.backend != "latex":
581 for phrase in phrases:
583 while i < len(document.body):
584 words = document.body[i].split()
585 if len(words) > 1 and words[0] == "\\begin_inset" and \
586 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
587 # must not replace anything in insets that store LaTeX contents in .lyx files
588 # (math and command insets withut overridden read() and write() methods
589 j = find_end_of_inset(document.body, i)
591 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
596 if document.body[i].find("\\") == 0:
599 j = document.body[i].find(phrase)
603 if not is_part_of_converted_phrase(document.body[i], j, phrase):
604 front = document.body[i][:j]
605 back = document.body[i][j+len(phrase):]
607 document.body.insert(i+1, back)
608 # We cannot use SpecialChar since we do not know whether we are outside passThru
609 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
613 def revert_phrases(document):
614 "convert special phrases to plain text"
617 while i < len(document.body):
618 words = document.body[i].split()
619 if len(words) > 1 and words[0] == "\\begin_inset" and \
620 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
621 # see convert_phrases
622 j = find_end_of_inset(document.body, i)
624 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
630 for phrase in phrases:
631 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
632 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
633 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
635 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
636 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
638 if replaced and i+1 < len(document.body) and \
639 (document.body[i+1].find("\\") != 0 or \
640 document.body[i+1].find("\\SpecialChar") == 0) and \
641 len(document.body[i]) + len(document.body[i+1]) <= 80:
642 document.body[i] = document.body[i] + document.body[i+1]
643 document.body[i+1:i+2] = []
648 def convert_specialchar_internal(document, forward):
649 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
650 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
651 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
652 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
653 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
654 "\\LaTeX":"LaTeX" # must be after LaTeX2e
658 while i < len(document.body):
659 words = document.body[i].split()
660 if len(words) > 1 and words[0] == "\\begin_inset" and \
661 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
662 # see convert_phrases
663 j = find_end_of_inset(document.body, i)
665 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
670 for key, value in specialchars.iteritems():
672 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
673 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
675 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
676 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
680 def convert_specialchar(document):
681 "convert special characters to new syntax"
682 convert_specialchar_internal(document, True)
685 def revert_specialchar(document):
686 "convert special characters to old syntax"
687 convert_specialchar_internal(document, False)
690 def revert_georgian(document):
691 "Set the document language to English but assure Georgian output"
693 if document.language == "georgian":
694 document.language = "english"
695 i = find_token(document.header, "\\language georgian", 0)
697 document.header[i] = "\\language english"
698 j = find_token(document.header, "\\language_package default", 0)
700 document.header[j] = "\\language_package babel"
701 k = find_token(document.header, "\\options", 0)
703 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
705 l = find_token(document.header, "\\use_default_options", 0)
706 document.header.insert(l + 1, "\\options georgian")
709 def revert_sigplan_doi(document):
710 " Reverts sigplanconf DOI layout to ERT "
712 if document.textclass != "sigplanconf":
717 i = find_token(document.body, "\\begin_layout DOI", i)
720 j = find_end_of_layout(document.body, i)
722 document.warning("Malformed LyX document: Can't find end of DOI layout")
726 content = lyx2latex(document, document.body[i:j + 1])
727 add_to_preamble(document, ["\\doi{" + content + "}"])
728 del document.body[i:j + 1]
732 def revert_ex_itemargs(document):
733 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
735 # Do we use the linguistics module?
737 mods = document.get_module_list()
739 if mod == "linguistics":
747 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
749 i = find_token(document.body, "\\begin_inset Argument item:", i)
752 j = find_end_of_inset(document.body, i)
753 # Find containing paragraph layout
754 parent = get_containing_layout(document.body, i)
756 document.warning("Malformed LyX document: Can't find parent paragraph layout")
760 layoutname = parent[0]
761 if layoutname in example_layouts:
762 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
763 endPlain = find_end_of_layout(document.body, beginPlain)
764 content = document.body[beginPlain + 1 : endPlain]
765 del document.body[i:j+1]
766 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
767 document.body[parbeg : parbeg] = subst
771 def revert_forest(document):
772 " Reverts the forest environment (Linguistics module) to TeX-code "
774 # Do we use the linguistics module?
776 mods = document.get_module_list()
778 if mod == "linguistics":
787 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
790 j = find_end_of_inset(document.body, i)
792 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
796 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
797 endPlain = find_end_of_layout(document.body, beginPlain)
798 content = lyx2latex(document, document.body[beginPlain : endPlain])
800 add_to_preamble(document, ["\\usepackage{forest}"])
802 document.body[i:j + 1] = ["\\begin_inset ERT", "status collapsed", "",
803 "\\begin_layout Plain Layout", "", "\\backslash",
804 "begin{forest}", "\\end_layout", "", "\\begin_layout Plain Layout",
805 content, "\\end_layout", "", "\\begin_layout Plain Layout",
806 "\\backslash", "end{forest}", "", "\\end_layout", "", "\\end_inset"]
810 def revert_glossgroup(document):
811 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
813 # Do we use the linguistics module?
815 mods = document.get_module_list()
817 if mod == "linguistics":
826 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
829 j = find_end_of_inset(document.body, i)
831 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
835 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
836 endPlain = find_end_of_layout(document.body, beginPlain)
837 content = lyx2latex(document, document.body[beginPlain : endPlain])
838 document.warning("content: %s" % content)
840 document.body[i:j + 1] = ["{", "", content, "", "}"]
844 def revert_newgloss(document):
845 " Reverts the new Glosse insets (Linguistics module) to the old format "
847 # Do we use the linguistics module?
849 mods = document.get_module_list()
851 if mod == "linguistics":
858 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
859 for glosse in glosses:
862 i = find_token(document.body, glosse, i)
865 j = find_end_of_inset(document.body, i)
867 document.warning("Malformed LyX document: Can't find end of Glosse inset")
871 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
872 endarg = find_end_of_inset(document.body, arg)
875 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
876 if argbeginPlain == -1:
877 document.warning("Malformed LyX document: Can't find arg plain Layout")
880 argendPlain = find_end_of_inset(document.body, argbeginPlain)
881 argcontent = lyx2latex(document, document.body[argbeginPlain : argendPlain - 2])
883 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
884 argcontent, "\\end_layout"]
886 # remove Arg insets and paragraph, if it only contains this inset
887 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
888 del document.body[arg - 1 : endarg + 4]
890 del document.body[arg : endarg + 1]
892 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
893 endPlain = find_end_of_layout(document.body, beginPlain)
894 content = lyx2latex(document, document.body[beginPlain : endPlain])
896 document.body[beginPlain + 1:endPlain] = [content]
900 def convert_newgloss(document):
901 " Converts Glosse insets (Linguistics module) to the new format "
903 # Do we use the linguistics module?
905 mods = document.get_module_list()
907 if mod == "linguistics":
914 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
915 for glosse in glosses:
918 i = find_token(document.body, glosse, i)
921 j = find_end_of_inset(document.body, i)
923 document.warning("Malformed LyX document: Can't find end of Glosse inset")
930 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
933 endPlain = find_end_of_layout(document.body, beginPlain)
935 document.warning("Malformed LyX document: Can't find end of Glosse layout")
939 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
940 if glt != -1 and document.body[glt + 1].startswith("glt"):
941 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
942 argcontent = document.body[glt + 1 : endPlain]
943 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
944 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
945 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
946 "\\end_layout", "", "\\end_inset"]
948 content = document.body[beginPlain + 1 : endPlain]
949 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
950 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
952 endPlain = find_end_of_layout(document.body, beginPlain)
954 j = find_end_of_inset(document.body, i)
959 def convert_BoxFeatures(document):
960 " adds new box features "
964 i = find_token(document.body, "height_special", i)
967 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
971 def revert_BoxFeatures(document):
972 " outputs new box features as TeX code "
976 defaultThick = "0.4pt"
977 defaultShadow = "4pt"
979 i = find_token(document.body, "height_special", i)
982 # read out the values
983 beg = document.body[i+1].find('"');
984 end = document.body[i+1].rfind('"');
985 thickness = document.body[i+1][beg+1:end];
986 beg = document.body[i+2].find('"');
987 end = document.body[i+2].rfind('"');
988 separation = document.body[i+2][beg+1:end];
989 beg = document.body[i+3].find('"');
990 end = document.body[i+3].rfind('"');
991 shadowsize = document.body[i+3][beg+1:end];
992 # delete the specification
993 del document.body[i+1:i+4]
995 # first output the closing brace
996 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
997 document.body[i + 10 : i + 10] = put_cmd_in_ert("}")
998 # now output the lengths
999 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1000 document.body[i - 10 : i - 10] = put_cmd_in_ert("{")
1001 if thickness != defaultThick:
1002 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness]
1003 if separation != defaultSep and thickness == defaultThick:
1004 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation]
1005 if separation != defaultSep and thickness != defaultThick:
1006 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1007 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1008 document.body[i - 5 : i - 4] = ["{\\backslash shadowsize " + shadowsize]
1009 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1010 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1011 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1012 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1013 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1014 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1018 def convert_origin(document):
1019 " Insert the origin tag "
1021 i = find_token(document.header, "\\textclass ", 0)
1023 document.warning("Malformed LyX document: No \\textclass!!")
1025 if document.dir == "":
1028 origin = document.dir.replace('\\', '/') + '/'
1030 origin = unicode(origin, sys.getfilesystemencoding())
1031 document.header[i:i] = ["\\origin " + origin]
1034 def revert_origin(document):
1035 " Remove the origin tag "
1037 i = find_token(document.header, "\\origin ", 0)
1039 document.warning("Malformed LyX document: No \\origin!!")
1041 del document.header[i]
1044 color_names = ["brown", "darkgray", "gray", \
1045 "lightgray", "lime", "olive", "orange", \
1046 "pink", "purple", "teal", "violet"]
1048 def revert_textcolor(document):
1049 " revert new \texcolor colors to TeX code "
1054 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}"])
1056 i = find_token(document.body, "\\color ", i)
1060 for color in list(color_names):
1061 if document.body[i] == "\\color " + color:
1062 # register that xcolor must be loaded in the preamble
1065 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\usepackage{xcolor}}"])
1066 # find the next \\color and/or the next \\end_layout
1067 j = find_token(document.body, "\\color", i + 1)
1068 k = find_token(document.body, "\\end_layout", i + 1)
1069 if j == -1 and k != -1:
1072 # first output the closing brace
1074 document.body[k: k] = put_cmd_in_ert("}")
1076 document.body[j: j] = put_cmd_in_ert("}")
1077 # now output the \textcolor command
1078 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1082 def convert_colorbox(document):
1083 " adds color settings for boxes "
1087 i = find_token(document.body, "shadowsize", i)
1090 document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1094 def revert_colorbox(document):
1095 " outputs color settings for boxes as TeX code "
1098 defaultframecolor = "black"
1099 defaultbackcolor = "white"
1101 i = find_token(document.body, "framecolor", i)
1104 # read out the values
1105 beg = document.body[i].find('"');
1106 end = document.body[i].rfind('"');
1107 framecolor = document.body[i][beg+1:end];
1108 beg = document.body[i+1].find('"');
1109 end = document.body[i+1].rfind('"');
1110 backcolor = document.body[i+1][beg+1:end];
1111 # delete the specification
1112 del document.body[i:i+2]
1114 # first output the closing brace
1115 if framecolor != defaultframecolor or backcolor != defaultbackcolor:
1116 document.body[i + 9 : i + 9] = put_cmd_in_ert("}")
1117 # now output the box commands
1118 if framecolor != defaultframecolor or backcolor != defaultbackcolor:
1119 document.body[i - 14 : i - 14] = put_cmd_in_ert("{")
1120 if framecolor != defaultframecolor:
1121 document.body[i - 9 : i - 8] = ["\\backslash fboxcolor{" + framecolor + "}{" + backcolor + "}{"]
1122 if backcolor != defaultbackcolor and framecolor == defaultframecolor:
1123 document.body[i - 9 : i - 8] = ["\\backslash colorbox{" + backcolor + "}{"]
1127 def revert_mathmulticol(document):
1128 " Convert formulas to ERT if they contain multicolumns "
1132 i = find_token(document.body, '\\begin_inset Formula', i)
1135 j = find_end_of_inset(document.body, i)
1137 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1140 lines = document.body[i:j]
1141 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1142 code = "\n".join(lines)
1147 n = code.find("\\multicolumn", k)
1148 # no need to convert degenerated multicolumn cells,
1149 # they work in old LyX versions as "math ERT"
1150 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1151 ert = put_cmd_in_ert(code)
1152 document.body[i:j+1] = ert
1158 i = find_end_of_inset(document.body, i)
1163 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt):
1165 Reverts an InsetArgument to TeX-code
1167 revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt)
1168 LineOfBegin is the line of the \begin_layout or \begin_inset statement
1169 LineOfEnd is the line of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
1170 StartArgument is the number of the first argument that needs to be converted
1171 EndArgument is the number of the last argument that needs to be converted or the last defined one
1172 isEnvironment must be true, if the layout is for a LaTeX environment
1173 isOpt must be true, if the argument is an optional one
1177 while lineArg != -1 and n < nmax + 1:
1178 lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
1179 if lineArg > endline and endline != 0:
1182 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1183 # we have to assure that no other inset is in the Argument
1184 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1185 endInset = find_token(document.body, "\\end_inset", beginPlain)
1188 while beginInset < endInset and beginInset != -1:
1189 beginInset = find_token(document.body, "\\begin_inset", k)
1190 endInset = find_token(document.body, "\\end_inset", l)
1193 if environment == False:
1195 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
1196 del(document.body[lineArg : beginPlain + 1])
1199 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
1200 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
1203 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
1204 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
1210 def revert_jss(document):
1211 " Reverts JSS In_Preamble commands to ERT in preamble "
1213 if document.textclass != "jss":
1222 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1223 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1226 h = find_token(document.body, "\\begin_inset Flex pkg", h)
1228 endh = find_end_of_inset(document.body, h)
1229 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1230 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1234 m = find_token(document.body, "\\begin_inset Flex proglang", m)
1236 endm = find_end_of_inset(document.body, m)
1237 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1238 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1242 j = find_token(document.body, "\\begin_inset Flex code", j)
1244 endj = find_end_of_inset(document.body, j)
1245 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1246 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1250 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1252 endk = find_end_of_inset(document.body, k)
1253 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1254 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1258 n = find_token(document.body, "\\begin_inset Flex URL", n)
1260 endn = find_end_of_inset(document.body, n)
1261 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1262 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1264 # now revert the In_Preamble layouts
1266 i = find_token(document.body, "\\begin_layout Title", 0)
1269 j = find_end_of_layout(document.body, i)
1271 document.warning("Malformed LyX document: Can't find end of Title layout")
1274 content = lyx2latex(document, document.body[i:j + 1])
1275 add_to_preamble(document, ["\\title{" + content + "}"])
1276 del document.body[i:j + 1]
1278 i = find_token(document.body, "\\begin_layout Author", 0)
1281 j = find_end_of_layout(document.body, i)
1283 document.warning("Malformed LyX document: Can't find end of Author layout")
1286 content = lyx2latex(document, document.body[i:j + 1])
1287 add_to_preamble(document, ["\\author{" + content + "}"])
1288 del document.body[i:j + 1]
1290 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1293 j = find_end_of_layout(document.body, i)
1295 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1298 content = lyx2latex(document, document.body[i:j + 1])
1299 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1300 del document.body[i:j + 1]
1302 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1305 j = find_end_of_layout(document.body, i)
1307 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1310 content = lyx2latex(document, document.body[i:j + 1])
1311 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1312 del document.body[i:j + 1]
1314 i = find_token(document.body, "\\begin_layout Short Title", 0)
1317 j = find_end_of_layout(document.body, i)
1319 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1322 content = lyx2latex(document, document.body[i:j + 1])
1323 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1324 del document.body[i:j + 1]
1326 i = find_token(document.body, "\\begin_layout Abstract", 0)
1329 j = find_end_of_layout(document.body, i)
1331 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1334 content = lyx2latex(document, document.body[i:j + 1])
1335 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1336 del document.body[i:j + 1]
1338 i = find_token(document.body, "\\begin_layout Keywords", 0)
1341 j = find_end_of_layout(document.body, i)
1343 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1346 content = lyx2latex(document, document.body[i:j + 1])
1347 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1348 del document.body[i:j + 1]
1350 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1353 j = find_end_of_layout(document.body, i)
1355 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1358 content = lyx2latex(document, document.body[i:j + 1])
1359 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1360 del document.body[i:j + 1]
1362 i = find_token(document.body, "\\begin_layout Address", 0)
1365 j = find_end_of_layout(document.body, i)
1367 document.warning("Malformed LyX document: Can't find end of Address layout")
1370 content = lyx2latex(document, document.body[i:j + 1])
1371 add_to_preamble(document, ["\\Address{" + content + "}"])
1372 del document.body[i:j + 1]
1373 # finally handle the code layouts
1378 while m != -1 or j != -1 or h != -1 or k != -1:
1381 h = find_token(document.body, "\\begin_layout Code Chunk", h)
1383 endh = find_end_of_layout(document.body, h)
1384 begindeeper = find_token(document.body, "\\begin_deeper", h)
1385 enddeeper = find_token(document.body, "\\end_deeper", h)
1386 document.body[enddeeper + 1 : enddeeper] = ["\\end_layout"]
1387 document.body[enddeeper : enddeeper + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1388 del document.body[begindeeper]
1389 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1390 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1394 m = find_token(document.body, "\\begin_layout Standard Code", m)
1396 endm = find_end_of_layout(document.body, m)
1397 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1398 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1399 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1400 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1401 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1405 j = find_token(document.body, "\\begin_layout Code Input", j)
1407 endj = find_end_of_layout(document.body, j)
1408 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1409 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1410 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1411 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1412 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1416 k = find_token(document.body, "\\begin_layout Code Output", k)
1418 endk = find_end_of_layout(document.body, k)
1419 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1420 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1421 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1422 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1423 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1427 def convert_subref(document):
1428 " converts sub: ref prefixes to subref: "
1431 rx = re.compile(r'^name \"sub:(.+)$')
1434 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1437 j = find_end_of_inset(document.body, i)
1439 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1443 for p in range(i, j):
1444 m = rx.match(document.body[p])
1447 document.body[p] = "name \"subsec:" + label
1451 rx = re.compile(r'^reference \"sub:(.+)$')
1454 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1457 j = find_end_of_inset(document.body, i)
1459 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1463 for p in range(i, j):
1464 m = rx.match(document.body[p])
1467 document.body[p] = "reference \"subsec:" + label
1473 def revert_subref(document):
1474 " reverts subref: ref prefixes to sub: "
1477 rx = re.compile(r'^name \"subsec:(.+)$')
1480 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1483 j = find_end_of_inset(document.body, i)
1485 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1489 for p in range(i, j):
1490 m = rx.match(document.body[p])
1493 document.body[p] = "name \"sub:" + label
1498 rx = re.compile(r'^reference \"subsec:(.+)$')
1501 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1504 j = find_end_of_inset(document.body, i)
1506 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1510 for p in range(i, j):
1511 m = rx.match(document.body[p])
1514 document.body[p] = "reference \"sub:" + label
1523 supported_versions = ["2.2.0", "2.2"]
1525 [475, [convert_separator]],
1526 # nothing to do for 476: We consider it a bug that older versions
1527 # did not load amsmath automatically for these commands, and do not
1528 # want to hardcode amsmath off.
1534 [481, [convert_dashes]],
1535 [482, [convert_phrases]],
1536 [483, [convert_specialchar]],
1541 [488, [convert_newgloss]],
1542 [489, [convert_BoxFeatures]],
1543 [490, [convert_origin]],
1545 [492, [convert_colorbox]],
1548 [495, [convert_subref]]
1552 [494, [revert_subref]],
1553 [493, [revert_jss]],
1554 [492, [revert_mathmulticol]],
1555 [491, [revert_colorbox]],
1556 [490, [revert_textcolor]],
1557 [489, [revert_origin]],
1558 [488, [revert_BoxFeatures]],
1559 [487, [revert_newgloss, revert_glossgroup]],
1560 [486, [revert_forest]],
1561 [485, [revert_ex_itemargs]],
1562 [484, [revert_sigplan_doi]],
1563 [483, [revert_georgian]],
1564 [482, [revert_specialchar]],
1565 [481, [revert_phrases]],
1566 [480, [revert_dashes]],
1567 [479, [revert_question_env]],
1568 [478, [revert_beamer_lemma]],
1569 [477, [revert_xarrow]],
1570 [476, [revert_swissgerman]],
1571 [475, [revert_smash]],
1572 [474, [revert_separator]]
1576 if __name__ == "__main__":