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 = "none"
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 j = find_end_of_inset(document.body, i)
1113 document.warning("Malformed LyX document: Can't find end of box inset!")
1116 # delete the specification
1117 del document.body[i:i+2]
1119 # first output the closing brace
1120 if framecolor != defaultframecolor or backcolor != defaultbackcolor:
1121 document.body[j + 1 : j + 1] = put_cmd_in_ert("}")
1122 # now output the box commands
1123 if framecolor != defaultframecolor or backcolor != defaultbackcolor:
1124 document.body[i - 14 : i - 14] = put_cmd_in_ert("{")
1125 if framecolor != defaultframecolor:
1126 document.body[i - 9 : i - 8] = ["\\backslash fcolorbox{" + framecolor + "}{" + backcolor + "}{"]
1127 k = find_token(document.body, "\\begin_inset Box Boxed", i - 16)
1129 # \fcolorbox is already framed box thus remove the frame
1130 document.body[k : k + 1] = ["\\begin_inset Box Frameless"]
1131 if backcolor != defaultbackcolor and framecolor == defaultframecolor:
1132 document.body[i - 9 : i - 8] = ["\\backslash colorbox{" + backcolor + "}{"]
1136 def revert_mathmulticol(document):
1137 " Convert formulas to ERT if they contain multicolumns "
1141 i = find_token(document.body, '\\begin_inset Formula', i)
1144 j = find_end_of_inset(document.body, i)
1146 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1149 lines = document.body[i:j]
1150 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1151 code = "\n".join(lines)
1156 n = code.find("\\multicolumn", k)
1157 # no need to convert degenerated multicolumn cells,
1158 # they work in old LyX versions as "math ERT"
1159 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1160 ert = put_cmd_in_ert(code)
1161 document.body[i:j+1] = ert
1167 i = find_end_of_inset(document.body, i)
1172 def revert_jss(document):
1173 " Reverts JSS In_Preamble commands to ERT in preamble "
1175 if document.textclass != "jss":
1184 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1185 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1188 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1190 endh = find_end_of_inset(document.body, h)
1191 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1192 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1196 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1198 endm = find_end_of_inset(document.body, m)
1199 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1200 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1204 j = find_token(document.body, "\\begin_inset Flex Code", j)
1206 # assure that we are not in a Code Chunk inset
1207 if document.body[j][-1] == "e":
1208 endj = find_end_of_inset(document.body, j)
1209 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1210 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1216 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1218 endk = find_end_of_inset(document.body, k)
1219 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1220 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1224 n = find_token(document.body, "\\begin_inset Flex URL", n)
1226 endn = find_end_of_inset(document.body, n)
1227 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1228 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1230 # now revert the In_Preamble layouts
1232 i = find_token(document.body, "\\begin_layout Title", 0)
1235 j = find_end_of_layout(document.body, i)
1237 document.warning("Malformed LyX document: Can't find end of Title layout")
1240 content = lyx2latex(document, document.body[i:j + 1])
1241 add_to_preamble(document, ["\\title{" + content + "}"])
1242 del document.body[i:j + 1]
1244 i = find_token(document.body, "\\begin_layout Author", 0)
1247 j = find_end_of_layout(document.body, i)
1249 document.warning("Malformed LyX document: Can't find end of Author layout")
1252 content = lyx2latex(document, document.body[i:j + 1])
1253 add_to_preamble(document, ["\\author{" + content + "}"])
1254 del document.body[i:j + 1]
1256 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1259 j = find_end_of_layout(document.body, i)
1261 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1264 content = lyx2latex(document, document.body[i:j + 1])
1265 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1266 del document.body[i:j + 1]
1268 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1271 j = find_end_of_layout(document.body, i)
1273 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1276 content = lyx2latex(document, document.body[i:j + 1])
1277 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1278 del document.body[i:j + 1]
1280 i = find_token(document.body, "\\begin_layout Short Title", 0)
1283 j = find_end_of_layout(document.body, i)
1285 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1288 content = lyx2latex(document, document.body[i:j + 1])
1289 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1290 del document.body[i:j + 1]
1292 i = find_token(document.body, "\\begin_layout Abstract", 0)
1295 j = find_end_of_layout(document.body, i)
1297 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1300 content = lyx2latex(document, document.body[i:j + 1])
1301 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1302 del document.body[i:j + 1]
1304 i = find_token(document.body, "\\begin_layout Keywords", 0)
1307 j = find_end_of_layout(document.body, i)
1309 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1312 content = lyx2latex(document, document.body[i:j + 1])
1313 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1314 del document.body[i:j + 1]
1316 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1319 j = find_end_of_layout(document.body, i)
1321 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1324 content = lyx2latex(document, document.body[i:j + 1])
1325 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1326 del document.body[i:j + 1]
1328 i = find_token(document.body, "\\begin_layout Address", 0)
1331 j = find_end_of_layout(document.body, i)
1333 document.warning("Malformed LyX document: Can't find end of Address layout")
1336 content = lyx2latex(document, document.body[i:j + 1])
1337 add_to_preamble(document, ["\\Address{" + content + "}"])
1338 del document.body[i:j + 1]
1339 # finally handle the code layouts
1344 while m != -1 or j != -1 or h != -1 or k != -1:
1347 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1349 endh = find_end_of_inset(document.body, h)
1350 document.body[endh + 1 : endh] = ["\\end_layout"]
1351 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1352 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1353 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1357 j = find_token(document.body, "\\begin_layout Code Input", j)
1359 endj = find_end_of_layout(document.body, j)
1360 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1361 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1362 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1363 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1364 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1368 k = find_token(document.body, "\\begin_layout Code Output", k)
1370 endk = find_end_of_layout(document.body, k)
1371 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1372 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1373 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1374 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1375 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1379 m = find_token(document.body, "\\begin_layout Code", m)
1381 endm = find_end_of_layout(document.body, m)
1382 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1383 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1384 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1385 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1386 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1390 def convert_subref(document):
1391 " converts sub: ref prefixes to subref: "
1394 rx = re.compile(r'^name \"sub:(.+)$')
1397 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1400 j = find_end_of_inset(document.body, i)
1402 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1406 for p in range(i, j):
1407 m = rx.match(document.body[p])
1410 document.body[p] = "name \"subsec:" + label
1414 rx = re.compile(r'^reference \"sub:(.+)$')
1417 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1420 j = find_end_of_inset(document.body, i)
1422 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1426 for p in range(i, j):
1427 m = rx.match(document.body[p])
1430 document.body[p] = "reference \"subsec:" + label
1436 def revert_subref(document):
1437 " reverts subref: ref prefixes to sub: "
1440 rx = re.compile(r'^name \"subsec:(.+)$')
1443 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1446 j = find_end_of_inset(document.body, i)
1448 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1452 for p in range(i, j):
1453 m = rx.match(document.body[p])
1456 document.body[p] = "name \"sub:" + label
1461 rx = re.compile(r'^reference \"subsec:(.+)$')
1464 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1467 j = find_end_of_inset(document.body, i)
1469 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1473 for p in range(i, j):
1474 m = rx.match(document.body[p])
1477 document.body[p] = "reference \"sub:" + label
1486 supported_versions = ["2.2.0", "2.2"]
1488 [475, [convert_separator]],
1489 # nothing to do for 476: We consider it a bug that older versions
1490 # did not load amsmath automatically for these commands, and do not
1491 # want to hardcode amsmath off.
1497 [481, [convert_dashes]],
1498 [482, [convert_phrases]],
1499 [483, [convert_specialchar]],
1504 [488, [convert_newgloss]],
1505 [489, [convert_BoxFeatures]],
1506 [490, [convert_origin]],
1508 [492, [convert_colorbox]],
1511 [495, [convert_subref]]
1515 [494, [revert_subref]],
1516 [493, [revert_jss]],
1517 [492, [revert_mathmulticol]],
1518 [491, [revert_colorbox]],
1519 [490, [revert_textcolor]],
1520 [489, [revert_origin]],
1521 [488, [revert_BoxFeatures]],
1522 [487, [revert_newgloss, revert_glossgroup]],
1523 [486, [revert_forest]],
1524 [485, [revert_ex_itemargs]],
1525 [484, [revert_sigplan_doi]],
1526 [483, [revert_georgian]],
1527 [482, [revert_specialchar]],
1528 [481, [revert_phrases]],
1529 [480, [revert_dashes]],
1530 [479, [revert_question_env]],
1531 [478, [revert_beamer_lemma]],
1532 [477, [revert_xarrow]],
1533 [476, [revert_swissgerman]],
1534 [475, [revert_smash]],
1535 [474, [revert_separator]]
1539 if __name__ == "__main__":