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, supported):
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 # If "supported" is true, the target version also supports this
301 regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
302 p = find_re(document.header, regexp, 0)
303 value = "1" # default is auto
305 value = get_value(document.header, "\\use_package" , p).split()[1]
307 del document.header[p]
308 if value == "2" and not supported: # on
309 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
310 elif value == "1" and not oldauto: # auto
313 i = find_token(document.body, '\\begin_inset Formula', i)
316 j = find_end_of_inset(document.body, i)
318 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
321 code = "\n".join(document.body[i:j])
323 if code.find("\\%s" % c) != -1:
325 document.header[p] = "\\use_package " + pkg + " 2"
327 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
332 mathtools_commands = ["xhookrightarrow", "xhookleftarrow", "xRightarrow", \
333 "xrightharpoondown", "xrightharpoonup", "xrightleftharpoons", \
334 "xLeftarrow", "xleftharpoondown", "xleftharpoonup", \
335 "xleftrightarrow", "xLeftrightarrow", "xleftrightharpoons", \
338 def revert_xarrow(document):
339 "remove use_package mathtools"
340 revert_use_package(document, "mathtools", mathtools_commands, False, True)
343 def revert_beamer_lemma(document):
344 " Reverts beamer lemma layout to ERT "
346 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
347 if document.textclass not in beamer_classes:
353 i = find_token(document.body, "\\begin_layout Lemma", i)
356 j = find_end_of_layout(document.body, i)
358 document.warning("Malformed LyX document: Can't find end of Lemma layout")
361 arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j)
362 endarg1 = find_end_of_inset(document.body, arg1)
363 arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j)
364 endarg2 = find_end_of_inset(document.body, arg2)
368 beginPlain1 = find_token(document.body, "\\begin_layout Plain Layout", arg1, endarg1)
369 if beginPlain1 == -1:
370 document.warning("Malformed LyX document: Can't find arg1 plain Layout")
373 endPlain1 = find_end_of_inset(document.body, beginPlain1)
374 content1 = document.body[beginPlain1 + 1 : endPlain1 - 2]
375 subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">")
377 beginPlain2 = find_token(document.body, "\\begin_layout Plain Layout", arg2, endarg2)
378 if beginPlain2 == -1:
379 document.warning("Malformed LyX document: Can't find arg2 plain Layout")
382 endPlain2 = find_end_of_inset(document.body, beginPlain2)
383 content2 = document.body[beginPlain2 + 1 : endPlain2 - 2]
384 subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]")
388 del document.body[arg2 : endarg2 + 1]
390 del document.body[arg1 : endarg1 + 1]
392 del document.body[arg1 : endarg1 + 1]
394 del document.body[arg2 : endarg2 + 1]
396 # index of end layout has probably changed
397 j = find_end_of_layout(document.body, i)
399 document.warning("Malformed LyX document: Can't find end of Lemma layout")
405 # if this is not a consecutive env, add start command
407 begcmd = put_cmd_in_ert("\\begin{lemma}")
409 # has this a consecutive lemma?
410 consecutive = document.body[j + 2] == "\\begin_layout Lemma"
412 # if this is not followed by a consecutive env, add end command
414 document.body[j : j + 1] = put_cmd_in_ert("\\end{lemma}") + ["\\end_layout"]
416 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd + subst1 + subst2
422 def revert_question_env(document):
424 Reverts question and question* environments of
425 theorems-ams-extended-bytype module to ERT
428 # Do we use theorems-ams-extended-bytype module?
430 mods = document.get_module_list()
432 if mod == "theorems-ams-extended-bytype":
442 i = find_token(document.body, "\\begin_layout Question", i)
446 starred = document.body[i] == "\\begin_layout Question*"
448 j = find_end_of_layout(document.body, i)
450 document.warning("Malformed LyX document: Can't find end of Question layout")
454 # if this is not a consecutive env, add start command
458 begcmd = put_cmd_in_ert("\\begin{question*}")
460 begcmd = put_cmd_in_ert("\\begin{question}")
462 # has this a consecutive theorem of same type?
465 consecutive = document.body[j + 2] == "\\begin_layout Question*"
467 consecutive = document.body[j + 2] == "\\begin_layout Question"
469 # if this is not followed by a consecutive env, add end command
472 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
474 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
476 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
478 add_to_preamble(document, "\\providecommand{\questionname}{Question}")
481 add_to_preamble(document, "\\theoremstyle{plain}\n" \
482 "\\newtheorem*{question*}{\\protect\\questionname}")
484 add_to_preamble(document, "\\theoremstyle{plain}\n" \
485 "\\newtheorem{question}{\\protect\\questionname}")
490 def convert_dashes(document):
491 "convert -- and --- to \\twohyphens and \\threehyphens"
493 if document.backend != "latex":
497 while i < len(document.body):
498 words = document.body[i].split()
499 if len(words) > 1 and words[0] == "\\begin_inset" and \
500 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
501 # must not replace anything in insets that store LaTeX contents in .lyx files
502 # (math and command insets withut overridden read() and write() methods
503 # filtering out IPA makes Text::readParToken() more simple
504 # skip ERT as well since it is not needed there
505 j = find_end_of_inset(document.body, i)
507 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
513 j = document.body[i].find("--")
516 front = document.body[i][:j]
517 back = document.body[i][j+2:]
518 # We can have an arbitrary number of consecutive hyphens.
519 # These must be split into the corresponding number of two and three hyphens
520 # We must match what LaTeX does: First try emdash, then endash, then single hyphen
521 if back.find("-") == 0:
524 document.body.insert(i+1, back)
525 document.body[i] = front + "\\threehyphens"
528 document.body.insert(i+1, back)
529 document.body[i] = front + "\\twohyphens"
533 def revert_dashes(document):
534 "convert \\twohyphens and \\threehyphens to -- and ---"
537 while i < len(document.body):
538 words = document.body[i].split()
539 if len(words) > 1 and words[0] == "\\begin_inset" and \
540 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
542 j = find_end_of_inset(document.body, i)
544 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
550 if document.body[i].find("\\twohyphens") >= 0:
551 document.body[i] = document.body[i].replace("\\twohyphens", "--")
553 if document.body[i].find("\\threehyphens") >= 0:
554 document.body[i] = document.body[i].replace("\\threehyphens", "---")
556 if replaced and i+1 < len(document.body) and \
557 (document.body[i+1].find("\\") != 0 or \
558 document.body[i+1].find("\\twohyphens") == 0 or
559 document.body[i+1].find("\\threehyphens") == 0) and \
560 len(document.body[i]) + len(document.body[i+1]) <= 80:
561 document.body[i] = document.body[i] + document.body[i+1]
562 document.body[i+1:i+2] = []
567 # order is important for the last three!
568 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
570 def is_part_of_converted_phrase(line, j, phrase):
571 "is phrase part of an already converted phrase?"
573 converted = "\\SpecialCharNoPassThru \\" + p
574 pos = j + len(phrase) - len(converted)
576 if line[pos:pos+len(converted)] == converted:
581 def convert_phrases(document):
582 "convert special phrases from plain text to \\SpecialCharNoPassThru"
584 if document.backend != "latex":
587 for phrase in phrases:
589 while i < len(document.body):
590 words = document.body[i].split()
591 if len(words) > 1 and words[0] == "\\begin_inset" and \
592 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
593 # must not replace anything in insets that store LaTeX contents in .lyx files
594 # (math and command insets withut overridden read() and write() methods
595 j = find_end_of_inset(document.body, i)
597 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
602 if document.body[i].find("\\") == 0:
605 j = document.body[i].find(phrase)
609 if not is_part_of_converted_phrase(document.body[i], j, phrase):
610 front = document.body[i][:j]
611 back = document.body[i][j+len(phrase):]
613 document.body.insert(i+1, back)
614 # We cannot use SpecialChar since we do not know whether we are outside passThru
615 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
619 def revert_phrases(document):
620 "convert special phrases to plain text"
623 while i < len(document.body):
624 words = document.body[i].split()
625 if len(words) > 1 and words[0] == "\\begin_inset" and \
626 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
627 # see convert_phrases
628 j = find_end_of_inset(document.body, i)
630 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
636 for phrase in phrases:
637 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
638 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
639 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
641 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
642 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
644 if replaced and i+1 < len(document.body) and \
645 (document.body[i+1].find("\\") != 0 or \
646 document.body[i+1].find("\\SpecialChar") == 0) and \
647 len(document.body[i]) + len(document.body[i+1]) <= 80:
648 document.body[i] = document.body[i] + document.body[i+1]
649 document.body[i+1:i+2] = []
654 def convert_specialchar_internal(document, forward):
655 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
656 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
657 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
658 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
659 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
660 "\\LaTeX":"LaTeX" # must be after LaTeX2e
664 while i < len(document.body):
665 words = document.body[i].split()
666 if len(words) > 1 and words[0] == "\\begin_inset" and \
667 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
668 # see convert_phrases
669 j = find_end_of_inset(document.body, i)
671 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
676 for key, value in specialchars.iteritems():
678 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
679 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
681 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
682 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
686 def convert_specialchar(document):
687 "convert special characters to new syntax"
688 convert_specialchar_internal(document, True)
691 def revert_specialchar(document):
692 "convert special characters to old syntax"
693 convert_specialchar_internal(document, False)
696 def revert_georgian(document):
697 "Set the document language to English but assure Georgian output"
699 if document.language == "georgian":
700 document.language = "english"
701 i = find_token(document.header, "\\language georgian", 0)
703 document.header[i] = "\\language english"
704 j = find_token(document.header, "\\language_package default", 0)
706 document.header[j] = "\\language_package babel"
707 k = find_token(document.header, "\\options", 0)
709 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
711 l = find_token(document.header, "\\use_default_options", 0)
712 document.header.insert(l + 1, "\\options georgian")
715 def revert_sigplan_doi(document):
716 " Reverts sigplanconf DOI layout to ERT "
718 if document.textclass != "sigplanconf":
723 i = find_token(document.body, "\\begin_layout DOI", i)
726 j = find_end_of_layout(document.body, i)
728 document.warning("Malformed LyX document: Can't find end of DOI layout")
732 content = lyx2latex(document, document.body[i:j + 1])
733 add_to_preamble(document, ["\\doi{" + content + "}"])
734 del document.body[i:j + 1]
738 def revert_ex_itemargs(document):
739 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
741 # Do we use the linguistics module?
743 mods = document.get_module_list()
745 if mod == "linguistics":
753 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
755 i = find_token(document.body, "\\begin_inset Argument item:", i)
758 j = find_end_of_inset(document.body, i)
759 # Find containing paragraph layout
760 parent = get_containing_layout(document.body, i)
762 document.warning("Malformed LyX document: Can't find parent paragraph layout")
766 layoutname = parent[0]
767 if layoutname in example_layouts:
768 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
769 endPlain = find_end_of_layout(document.body, beginPlain)
770 content = document.body[beginPlain + 1 : endPlain]
771 del document.body[i:j+1]
772 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
773 document.body[parbeg : parbeg] = subst
777 def revert_forest(document):
778 " Reverts the forest environment (Linguistics module) to TeX-code "
780 # Do we use the linguistics module?
782 mods = document.get_module_list()
784 if mod == "linguistics":
793 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
796 j = find_end_of_inset(document.body, i)
798 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
802 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
803 endPlain = find_end_of_layout(document.body, beginPlain)
804 content = lyx2latex(document, document.body[beginPlain : endPlain])
806 add_to_preamble(document, ["\\usepackage{forest}"])
808 document.body[i:j + 1] = ["\\begin_inset ERT", "status collapsed", "",
809 "\\begin_layout Plain Layout", "", "\\backslash",
810 "begin{forest}", "\\end_layout", "", "\\begin_layout Plain Layout",
811 content, "\\end_layout", "", "\\begin_layout Plain Layout",
812 "\\backslash", "end{forest}", "", "\\end_layout", "", "\\end_inset"]
816 def revert_glossgroup(document):
817 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
819 # Do we use the linguistics module?
821 mods = document.get_module_list()
823 if mod == "linguistics":
832 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
835 j = find_end_of_inset(document.body, i)
837 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
841 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
842 endPlain = find_end_of_layout(document.body, beginPlain)
843 content = lyx2latex(document, document.body[beginPlain : endPlain])
844 document.warning("content: %s" % content)
846 document.body[i:j + 1] = ["{", "", content, "", "}"]
850 def revert_newgloss(document):
851 " Reverts the new Glosse insets (Linguistics module) to the old format "
853 # Do we use the linguistics module?
855 mods = document.get_module_list()
857 if mod == "linguistics":
864 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
865 for glosse in glosses:
868 i = find_token(document.body, glosse, i)
871 j = find_end_of_inset(document.body, i)
873 document.warning("Malformed LyX document: Can't find end of Glosse inset")
877 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
878 endarg = find_end_of_inset(document.body, arg)
881 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
882 if argbeginPlain == -1:
883 document.warning("Malformed LyX document: Can't find arg plain Layout")
886 argendPlain = find_end_of_inset(document.body, argbeginPlain)
887 argcontent = lyx2latex(document, document.body[argbeginPlain : argendPlain - 2])
889 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
890 argcontent, "\\end_layout"]
892 # remove Arg insets and paragraph, if it only contains this inset
893 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
894 del document.body[arg - 1 : endarg + 4]
896 del document.body[arg : endarg + 1]
898 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
899 endPlain = find_end_of_layout(document.body, beginPlain)
900 content = lyx2latex(document, document.body[beginPlain : endPlain])
902 document.body[beginPlain + 1:endPlain] = [content]
906 def convert_newgloss(document):
907 " Converts Glosse insets (Linguistics module) to the new format "
909 # Do we use the linguistics module?
911 mods = document.get_module_list()
913 if mod == "linguistics":
920 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
921 for glosse in glosses:
924 i = find_token(document.body, glosse, i)
927 j = find_end_of_inset(document.body, i)
929 document.warning("Malformed LyX document: Can't find end of Glosse inset")
936 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
939 endPlain = find_end_of_layout(document.body, beginPlain)
941 document.warning("Malformed LyX document: Can't find end of Glosse layout")
945 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
946 if glt != -1 and document.body[glt + 1].startswith("glt"):
947 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
948 argcontent = document.body[glt + 1 : endPlain]
949 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
950 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
951 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
952 "\\end_layout", "", "\\end_inset"]
954 content = document.body[beginPlain + 1 : endPlain]
955 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
956 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
958 endPlain = find_end_of_layout(document.body, beginPlain)
960 j = find_end_of_inset(document.body, i)
965 def convert_BoxFeatures(document):
966 " adds new box features "
970 i = find_token(document.body, "height_special", i)
973 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
977 def revert_BoxFeatures(document):
978 " outputs new box features as TeX code "
982 defaultThick = "0.4pt"
983 defaultShadow = "4pt"
985 i = find_token(document.body, "height_special", i)
988 # read out the values
989 beg = document.body[i+1].find('"');
990 end = document.body[i+1].rfind('"');
991 thickness = document.body[i+1][beg+1:end];
992 beg = document.body[i+2].find('"');
993 end = document.body[i+2].rfind('"');
994 separation = document.body[i+2][beg+1:end];
995 beg = document.body[i+3].find('"');
996 end = document.body[i+3].rfind('"');
997 shadowsize = document.body[i+3][beg+1:end];
998 # delete the specification
999 del document.body[i+1:i+4]
1001 # first output the closing brace
1002 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1003 document.body[i + 10 : i + 10] = put_cmd_in_ert("}")
1004 # now output the lengths
1005 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1006 document.body[i - 10 : i - 10] = put_cmd_in_ert("{")
1007 if thickness != defaultThick:
1008 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness]
1009 if separation != defaultSep and thickness == defaultThick:
1010 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation]
1011 if separation != defaultSep and thickness != defaultThick:
1012 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1013 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1014 document.body[i - 5 : i - 4] = ["{\\backslash shadowsize " + shadowsize]
1015 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1016 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1017 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1018 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1019 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1020 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1024 def convert_origin(document):
1025 " Insert the origin tag "
1027 i = find_token(document.header, "\\textclass ", 0)
1029 document.warning("Malformed LyX document: No \\textclass!!")
1031 if document.dir == "":
1034 origin = document.dir.replace('\\', '/') + '/'
1036 origin = unicode(origin, sys.getfilesystemencoding())
1037 document.header[i:i] = ["\\origin " + origin]
1040 def revert_origin(document):
1041 " Remove the origin tag "
1043 i = find_token(document.header, "\\origin ", 0)
1045 document.warning("Malformed LyX document: No \\origin!!")
1047 del document.header[i]
1050 color_names = ["brown", "darkgray", "gray", \
1051 "lightgray", "lime", "olive", "orange", \
1052 "pink", "purple", "teal", "violet"]
1054 def revert_textcolor(document):
1055 " revert new \\textcolor colors to TeX code "
1061 i = find_token(document.body, "\\color ", i)
1065 for color in list(color_names):
1066 if document.body[i] == "\\color " + color:
1067 # register that xcolor must be loaded in the preamble
1070 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\usepackage{xcolor}}{}"])
1071 # find the next \\color and/or the next \\end_layout
1072 j = find_token(document.body, "\\color", i + 1)
1073 k = find_token(document.body, "\\end_layout", i + 1)
1074 if j == -1 and k != -1:
1077 # first output the closing brace
1079 document.body[k: k] = put_cmd_in_ert("}")
1081 document.body[j: j] = put_cmd_in_ert("}")
1082 # now output the \textcolor command
1083 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1087 def convert_colorbox(document):
1088 " adds color settings for boxes "
1092 i = find_token(document.body, "shadowsize", i)
1095 document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1099 def revert_colorbox(document):
1100 " outputs color settings for boxes as TeX code "
1103 defaultframecolor = "black"
1104 defaultbackcolor = "none"
1106 binset = find_token(document.body, "\\begin_inset Box", binset)
1110 einset = find_end_of_inset(document.body, binset)
1112 document.warning("Malformed LyX document: Can't find end of box inset!")
1116 blay = find_token(document.body, "\\begin_layout", binset, einset)
1118 document.warning("Malformed LyX document: Can't find start of layout!")
1122 # doing it this way, we make sure only to find a framecolor option
1123 frame = find_token(document.body, "framecolor", binset, blay)
1128 beg = document.body[frame].find('"')
1129 end = document.body[frame].rfind('"')
1130 framecolor = document.body[frame][beg+1:end]
1132 # this should be on the next line
1134 beg = document.body[bgcolor].find('"')
1135 end = document.body[bgcolor].rfind('"')
1136 backcolor = document.body[bgcolor][beg+1:end]
1139 del document.body[frame:frame+2]
1140 # adjust end of inset
1143 if document.body[binset] == "\\begin_inset Box Boxed" and \
1144 framecolor != defaultframecolor:
1145 document.body[binset] = "\\begin_inset Box Frameless"
1148 # first output the closing brace
1149 if framecolor == defaultframecolor and backcolor == defaultbackcolor:
1153 document.body[einset + 1 : einset + 1] = put_cmd_in_ert("}")
1154 if framecolor != defaultframecolor:
1155 document.body[binset:binset] = put_cmd_in_ert("\\backslash fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1157 document.body[binset:binset] = put_cmd_in_ert("\\backslash colorbox{" + backcolor + "}{")
1162 def revert_mathmulticol(document):
1163 " Convert formulas to ERT if they contain multicolumns "
1167 i = find_token(document.body, '\\begin_inset Formula', i)
1170 j = find_end_of_inset(document.body, i)
1172 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1175 lines = document.body[i:j]
1176 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1177 code = "\n".join(lines)
1182 n = code.find("\\multicolumn", k)
1183 # no need to convert degenerated multicolumn cells,
1184 # they work in old LyX versions as "math ERT"
1185 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1186 ert = put_cmd_in_ert(code)
1187 document.body[i:j+1] = ert
1193 i = find_end_of_inset(document.body, i)
1198 def revert_jss(document):
1199 " Reverts JSS In_Preamble commands to ERT in preamble "
1201 if document.textclass != "jss":
1210 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1211 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1214 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1216 endh = find_end_of_inset(document.body, h)
1217 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1218 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1222 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1224 endm = find_end_of_inset(document.body, m)
1225 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1226 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1230 j = find_token(document.body, "\\begin_inset Flex Code", j)
1232 # assure that we are not in a Code Chunk inset
1233 if document.body[j][-1] == "e":
1234 endj = find_end_of_inset(document.body, j)
1235 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1236 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1242 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1244 endk = find_end_of_inset(document.body, k)
1245 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1246 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1250 n = find_token(document.body, "\\begin_inset Flex URL", n)
1252 endn = find_end_of_inset(document.body, n)
1253 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1254 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1256 # now revert the In_Preamble layouts
1258 i = find_token(document.body, "\\begin_layout Title", 0)
1261 j = find_end_of_layout(document.body, i)
1263 document.warning("Malformed LyX document: Can't find end of Title layout")
1266 content = lyx2latex(document, document.body[i:j + 1])
1267 add_to_preamble(document, ["\\title{" + content + "}"])
1268 del document.body[i:j + 1]
1270 i = find_token(document.body, "\\begin_layout Author", 0)
1273 j = find_end_of_layout(document.body, i)
1275 document.warning("Malformed LyX document: Can't find end of Author layout")
1278 content = lyx2latex(document, document.body[i:j + 1])
1279 add_to_preamble(document, ["\\author{" + content + "}"])
1280 del document.body[i:j + 1]
1282 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1285 j = find_end_of_layout(document.body, i)
1287 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1290 content = lyx2latex(document, document.body[i:j + 1])
1291 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1292 del document.body[i:j + 1]
1294 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1297 j = find_end_of_layout(document.body, i)
1299 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1302 content = lyx2latex(document, document.body[i:j + 1])
1303 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1304 del document.body[i:j + 1]
1306 i = find_token(document.body, "\\begin_layout Short Title", 0)
1309 j = find_end_of_layout(document.body, i)
1311 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1314 content = lyx2latex(document, document.body[i:j + 1])
1315 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1316 del document.body[i:j + 1]
1318 i = find_token(document.body, "\\begin_layout Abstract", 0)
1321 j = find_end_of_layout(document.body, i)
1323 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1326 content = lyx2latex(document, document.body[i:j + 1])
1327 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1328 del document.body[i:j + 1]
1330 i = find_token(document.body, "\\begin_layout Keywords", 0)
1333 j = find_end_of_layout(document.body, i)
1335 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1338 content = lyx2latex(document, document.body[i:j + 1])
1339 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1340 del document.body[i:j + 1]
1342 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1345 j = find_end_of_layout(document.body, i)
1347 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1350 content = lyx2latex(document, document.body[i:j + 1])
1351 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1352 del document.body[i:j + 1]
1354 i = find_token(document.body, "\\begin_layout Address", 0)
1357 j = find_end_of_layout(document.body, i)
1359 document.warning("Malformed LyX document: Can't find end of Address layout")
1362 content = lyx2latex(document, document.body[i:j + 1])
1363 add_to_preamble(document, ["\\Address{" + content + "}"])
1364 del document.body[i:j + 1]
1365 # finally handle the code layouts
1370 while m != -1 or j != -1 or h != -1 or k != -1:
1373 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1375 endh = find_end_of_inset(document.body, h)
1376 document.body[endh + 1 : endh] = ["\\end_layout"]
1377 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1378 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1379 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1383 j = find_token(document.body, "\\begin_layout Code Input", j)
1385 endj = find_end_of_layout(document.body, j)
1386 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1387 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1388 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1389 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1390 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1394 k = find_token(document.body, "\\begin_layout Code Output", k)
1396 endk = find_end_of_layout(document.body, k)
1397 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1398 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1399 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1400 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1401 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1405 m = find_token(document.body, "\\begin_layout Code", m)
1407 endm = find_end_of_layout(document.body, m)
1408 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1409 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1410 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1411 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1412 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1416 def convert_subref(document):
1417 " converts sub: ref prefixes to subref: "
1420 rx = re.compile(r'^name \"sub:(.+)$')
1423 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1426 j = find_end_of_inset(document.body, i)
1428 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1432 for p in range(i, j):
1433 m = rx.match(document.body[p])
1436 document.body[p] = "name \"subsec:" + label
1440 rx = re.compile(r'^reference \"sub:(.+)$')
1443 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1446 j = find_end_of_inset(document.body, i)
1448 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1452 for p in range(i, j):
1453 m = rx.match(document.body[p])
1456 document.body[p] = "reference \"subsec:" + label
1462 def revert_subref(document):
1463 " reverts subref: ref prefixes to sub: "
1466 rx = re.compile(r'^name \"subsec:(.+)$')
1469 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1472 j = find_end_of_inset(document.body, i)
1474 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1478 for p in range(i, j):
1479 m = rx.match(document.body[p])
1482 document.body[p] = "name \"sub:" + label
1487 rx = re.compile(r'^reference \"subsec:(.+)$')
1490 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1493 j = find_end_of_inset(document.body, i)
1495 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1499 for p in range(i, j):
1500 m = rx.match(document.body[p])
1503 document.body[p] = "reference \"sub:" + label
1508 def convert_nounzip(document):
1509 " remove the noUnzip parameter of graphics insets "
1511 rx = re.compile(r'\s*noUnzip\s*$')
1514 i = find_token(document.body, "\\begin_inset Graphics", i)
1517 j = find_end_of_inset(document.body, i)
1519 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1523 k = find_re(document.body, rx, i, j)
1525 del document.body[k]
1534 supported_versions = ["2.2.0", "2.2"]
1536 [475, [convert_separator]],
1537 # nothing to do for 476: We consider it a bug that older versions
1538 # did not load amsmath automatically for these commands, and do not
1539 # want to hardcode amsmath off.
1545 [481, [convert_dashes]],
1546 [482, [convert_phrases]],
1547 [483, [convert_specialchar]],
1552 [488, [convert_newgloss]],
1553 [489, [convert_BoxFeatures]],
1554 [490, [convert_origin]],
1556 [492, [convert_colorbox]],
1559 [495, [convert_subref]],
1560 [496, [convert_nounzip]]
1564 [495, []], # nothing to do since the noUnzip parameter was optional
1565 [494, [revert_subref]],
1566 [493, [revert_jss]],
1567 [492, [revert_mathmulticol]],
1568 [491, [revert_colorbox]],
1569 [490, [revert_textcolor]],
1570 [489, [revert_origin]],
1571 [488, [revert_BoxFeatures]],
1572 [487, [revert_newgloss, revert_glossgroup]],
1573 [486, [revert_forest]],
1574 [485, [revert_ex_itemargs]],
1575 [484, [revert_sigplan_doi]],
1576 [483, [revert_georgian]],
1577 [482, [revert_specialchar]],
1578 [481, [revert_phrases]],
1579 [480, [revert_dashes]],
1580 [479, [revert_question_env]],
1581 [478, [revert_beamer_lemma]],
1582 [477, [revert_xarrow]],
1583 [476, [revert_swissgerman]],
1584 [475, [revert_smash]],
1585 [474, [revert_separator]]
1589 if __name__ == "__main__":