1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # -*- coding: utf-8 -*-
4 # Copyright (C) 2015 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, \
35 # insert_to_preamble, latex_length, revert_flex_inset, \
36 # revert_font_attrs, hex2ratio, str2bool
38 from parser_tools import find_token, find_token_backwards, find_re, \
39 find_end_of_inset, find_end_of_layout, find_nonempty_line, \
40 get_containing_layout, get_value, check_token
42 ####################################################################
43 # Private helper functions
45 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt, nolastopt):
47 Reverts an InsetArgument to TeX-code
49 revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt, notLastOpt)
50 LineOfBegin is the line of the \begin_layout or \begin_inset statement
51 LineOfEnd is the line of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
52 StartArgument is the number of the first argument that needs to be converted
53 EndArgument is the number of the last argument that needs to be converted or the last defined one
54 isEnvironment must be true, if the layout is for a LaTeX environment
55 isOpt must be true, if the argument is an optional one
56 notLastOpt must be true if the argument is mandatory and followed by optional ones
60 while lineArg != -1 and n < nmax + 1:
61 lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
62 if lineArg > endline and endline != 0:
65 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
66 # we have to assure that no other inset is in the Argument
67 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
68 endInset = find_token(document.body, "\\end_inset", beginPlain)
71 while beginInset < endInset and beginInset != -1:
72 beginInset = find_token(document.body, "\\begin_inset", k)
73 endInset = find_token(document.body, "\\end_inset", l)
76 if environment == False:
78 if nolastopt == False:
79 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
81 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
82 del(document.body[lineArg : beginPlain + 1])
85 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
86 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
90 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
91 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
94 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
95 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
101 ###############################################################################
103 ### Conversion and reversion routines
105 ###############################################################################
107 def convert_separator(document):
109 Convert layout separators to separator insets and add (LaTeX) paragraph
110 breaks in order to mimic previous LaTeX export.
113 parins = ["\\begin_inset Separator parbreak", "\\end_inset", ""]
114 parlay = ["\\begin_layout Standard", "\\begin_inset Separator parbreak",
115 "\\end_inset", "", "\\end_layout", ""]
117 "family" : "default",
118 "series" : "default",
127 i = find_token(document.body, "\\begin_deeper", i)
131 j = find_token_backwards(document.body, "\\end_layout", i-1)
133 # reset any text style before inserting the inset
134 lay = get_containing_layout(document.body, j-1)
136 content = "\n".join(document.body[lay[1]:lay[2]])
137 for val in list(sty_dict.keys()):
138 if content.find("\\%s" % val) != -1:
139 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
142 document.body[j:j] = parins
143 i = i + len(parins) + 1
149 i = find_token(document.body, "\\align", i)
153 lay = get_containing_layout(document.body, i)
154 if lay != False and lay[0] == "Plain Layout":
158 j = find_token_backwards(document.body, "\\end_layout", i-1)
160 lay = get_containing_layout(document.body, j-1)
161 if lay != False and lay[0] == "Standard" \
162 and find_token(document.body, "\\align", lay[1], lay[2]) == -1 \
163 and find_token(document.body, "\\begin_inset VSpace", lay[1], lay[2]) == -1:
164 # reset any text style before inserting the inset
165 content = "\n".join(document.body[lay[1]:lay[2]])
166 for val in list(sty_dict.keys()):
167 if content.find("\\%s" % val) != -1:
168 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
171 document.body[j:j] = parins
172 i = i + len(parins) + 1
178 regexp = re.compile(r'^\\begin_layout (?:(-*)|(\s*))(Separator|EndOfSlide)(?:(-*)|(\s*))$', re.IGNORECASE)
182 i = find_re(document.body, regexp, i)
186 j = find_end_of_layout(document.body, i)
188 document.warning("Malformed LyX document: Missing `\\end_layout'.")
191 lay = get_containing_layout(document.body, j-1)
193 lines = document.body[lay[3]:lay[2]]
197 document.body[i:j+1] = parlay
199 document.body[i+1:i+1] = lines
201 i = i + len(parlay) + len(lines) + 1
204 def revert_separator(document):
205 " Revert separator insets to layout separators "
207 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
208 if document.textclass in beamer_classes:
209 beglaysep = "\\begin_layout Separator"
211 beglaysep = "\\begin_layout --Separator--"
213 parsep = [beglaysep, "", "\\end_layout", ""]
214 comert = ["\\begin_inset ERT", "status collapsed", "",
215 "\\begin_layout Plain Layout", "%", "\\end_layout",
216 "", "\\end_inset", ""]
217 empert = ["\\begin_inset ERT", "status collapsed", "",
218 "\\begin_layout Plain Layout", " ", "\\end_layout",
219 "", "\\end_inset", ""]
223 i = find_token(document.body, "\\begin_inset Separator", i)
227 lay = get_containing_layout(document.body, i)
229 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
236 kind = get_value(document.body, "\\begin_inset Separator", i, i+1, "plain").split()[1]
237 before = document.body[beg+1:i]
238 something_before = len(before) > 0 and len("".join(before)) > 0
239 j = find_end_of_inset(document.body, i)
240 after = document.body[j+1:end]
241 something_after = len(after) > 0 and len("".join(after)) > 0
243 beg = beg + len(before) + 1
244 elif something_before:
245 document.body[i:i] = ["\\end_layout", ""]
253 document.body[beg:j+1] = empert
256 document.body[beg:j+1] = comert
260 if layoutname == "Standard":
261 if not something_before:
262 document.body[beg:j+1] = parsep
264 document.body[i:i] = ["", "\\begin_layout Standard"]
267 document.body[beg:j+1] = ["\\begin_layout Standard"]
270 document.body[beg:j+1] = ["\\begin_deeper"]
272 end = end + 1 - (j + 1 - beg)
273 if not something_before:
274 document.body[i:i] = parsep
276 end = end + len(parsep)
277 document.body[i:i] = ["\\begin_layout Standard"]
278 document.body[end+2:end+2] = ["", "\\end_deeper", ""]
281 next_par_is_aligned = False
282 k = find_nonempty_line(document.body, end+1)
283 if k != -1 and check_token(document.body[k], "\\begin_layout"):
284 lay = get_containing_layout(document.body, k)
285 next_par_is_aligned = lay != False and \
286 find_token(document.body, "\\align", lay[1], lay[2]) != -1
287 if k != -1 and not next_par_is_aligned \
288 and not check_token(document.body[k], "\\end_deeper") \
289 and not check_token(document.body[k], "\\begin_deeper"):
290 if layoutname == "Standard":
291 document.body[beg:j+1] = [beglaysep]
294 document.body[beg:j+1] = ["\\begin_deeper", beglaysep]
295 end = end + 2 - (j + 1 - beg)
296 document.body[end+1:end+1] = ["", "\\end_deeper", ""]
300 del document.body[i:end+1]
302 del document.body[i:end-1]
307 def revert_smash(document):
308 " Set amsmath to on if smash commands are used "
310 commands = ["smash[t]", "smash[b]", "notag"]
311 i = find_token(document.header, "\\use_package amsmath", 0)
313 document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
315 value = get_value(document.header, "\\use_package amsmath", i).split()[1]
317 # nothing to do if package is not auto but on or off
321 j = find_token(document.body, '\\begin_inset Formula', j)
324 k = find_end_of_inset(document.body, j)
326 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
329 code = "\n".join(document.body[j:k])
331 if code.find("\\%s" % c) != -1:
332 # set amsmath to on, since it is loaded by the newer format
333 document.header[i] = "\\use_package amsmath 2"
338 def revert_swissgerman(document):
339 " Set language german-ch-old to german "
341 if document.language == "german-ch-old":
342 document.language = "german"
343 i = find_token(document.header, "\\language", 0)
345 document.header[i] = "\\language german"
348 j = find_token(document.body, "\\lang german-ch-old", j)
351 document.body[j] = document.body[j].replace("\\lang german-ch-old", "\\lang german")
355 def revert_use_package(document, pkg, commands, oldauto, supported):
356 # oldauto defines how the version we are reverting to behaves:
357 # if it is true, the old version uses the package automatically.
358 # if it is false, the old version never uses the package.
359 # If "supported" is true, the target version also supports this
361 regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
362 p = find_re(document.header, regexp, 0)
363 value = "1" # default is auto
365 value = get_value(document.header, "\\use_package" , p).split()[1]
367 del document.header[p]
368 if value == "2" and not supported: # on
369 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
370 elif value == "1" and not oldauto: # auto
373 i = find_token(document.body, '\\begin_inset Formula', i)
376 j = find_end_of_inset(document.body, i)
378 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
381 code = "\n".join(document.body[i:j])
383 if code.find("\\%s" % c) != -1:
385 document.header[p] = "\\use_package " + pkg + " 2"
387 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
392 mathtools_commands = ["xhookrightarrow", "xhookleftarrow", "xRightarrow", \
393 "xrightharpoondown", "xrightharpoonup", "xrightleftharpoons", \
394 "xLeftarrow", "xleftharpoondown", "xleftharpoonup", \
395 "xleftrightarrow", "xLeftrightarrow", "xleftrightharpoons", \
398 def revert_xarrow(document):
399 "remove use_package mathtools"
400 revert_use_package(document, "mathtools", mathtools_commands, False, True)
403 def revert_beamer_lemma(document):
404 " Reverts beamer lemma layout to ERT "
406 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
407 if document.textclass not in beamer_classes:
413 i = find_token(document.body, "\\begin_layout Lemma", i)
416 j = find_end_of_layout(document.body, i)
418 document.warning("Malformed LyX document: Can't find end of Lemma layout")
421 arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j)
422 endarg1 = find_end_of_inset(document.body, arg1)
423 arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j)
424 endarg2 = find_end_of_inset(document.body, arg2)
428 beginPlain1 = find_token(document.body, "\\begin_layout Plain Layout", arg1, endarg1)
429 if beginPlain1 == -1:
430 document.warning("Malformed LyX document: Can't find arg1 plain Layout")
433 endPlain1 = find_end_of_inset(document.body, beginPlain1)
434 content1 = document.body[beginPlain1 + 1 : endPlain1 - 2]
435 subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">")
437 beginPlain2 = find_token(document.body, "\\begin_layout Plain Layout", arg2, endarg2)
438 if beginPlain2 == -1:
439 document.warning("Malformed LyX document: Can't find arg2 plain Layout")
442 endPlain2 = find_end_of_inset(document.body, beginPlain2)
443 content2 = document.body[beginPlain2 + 1 : endPlain2 - 2]
444 subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]")
448 del document.body[arg2 : endarg2 + 1]
450 del document.body[arg1 : endarg1 + 1]
452 del document.body[arg1 : endarg1 + 1]
454 del document.body[arg2 : endarg2 + 1]
456 # index of end layout has probably changed
457 j = find_end_of_layout(document.body, i)
459 document.warning("Malformed LyX document: Can't find end of Lemma layout")
465 # if this is not a consecutive env, add start command
467 begcmd = put_cmd_in_ert("\\begin{lemma}")
469 # has this a consecutive lemma?
470 consecutive = document.body[j + 2] == "\\begin_layout Lemma"
472 # if this is not followed by a consecutive env, add end command
474 document.body[j : j + 1] = put_cmd_in_ert("\\end{lemma}") + ["\\end_layout"]
476 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd + subst1 + subst2
482 def revert_question_env(document):
484 Reverts question and question* environments of
485 theorems-ams-extended-bytype module to ERT
488 # Do we use theorems-ams-extended-bytype module?
490 mods = document.get_module_list()
492 if mod == "theorems-ams-extended-bytype":
502 i = find_token(document.body, "\\begin_layout Question", i)
506 starred = document.body[i] == "\\begin_layout Question*"
508 j = find_end_of_layout(document.body, i)
510 document.warning("Malformed LyX document: Can't find end of Question layout")
514 # if this is not a consecutive env, add start command
518 begcmd = put_cmd_in_ert("\\begin{question*}")
520 begcmd = put_cmd_in_ert("\\begin{question}")
522 # has this a consecutive theorem of same type?
525 consecutive = document.body[j + 2] == "\\begin_layout Question*"
527 consecutive = document.body[j + 2] == "\\begin_layout Question"
529 # if this is not followed by a consecutive env, add end command
532 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
534 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
536 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
538 add_to_preamble(document, "\\providecommand{\questionname}{Question}")
541 add_to_preamble(document, "\\theoremstyle{plain}\n" \
542 "\\newtheorem*{question*}{\\protect\\questionname}")
544 add_to_preamble(document, "\\theoremstyle{plain}\n" \
545 "\\newtheorem{question}{\\protect\\questionname}")
550 def convert_dashes(document):
551 "convert -- and --- to \\twohyphens and \\threehyphens"
553 if document.backend != "latex":
557 while i < len(document.body):
558 words = document.body[i].split()
559 if len(words) > 1 and words[0] == "\\begin_inset" and \
560 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
561 # must not replace anything in insets that store LaTeX contents in .lyx files
562 # (math and command insets withut overridden read() and write() methods
563 # filtering out IPA makes Text::readParToken() more simple
564 # skip ERT as well since it is not needed there
565 j = find_end_of_inset(document.body, i)
567 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
573 j = document.body[i].find("--")
576 front = document.body[i][:j]
577 back = document.body[i][j+2:]
578 # We can have an arbitrary number of consecutive hyphens.
579 # These must be split into the corresponding number of two and three hyphens
580 # We must match what LaTeX does: First try emdash, then endash, then single hyphen
581 if back.find("-") == 0:
584 document.body.insert(i+1, back)
585 document.body[i] = front + "\\threehyphens"
588 document.body.insert(i+1, back)
589 document.body[i] = front + "\\twohyphens"
593 def revert_dashes(document):
594 "convert \\twohyphens and \\threehyphens to -- and ---"
597 while i < len(document.body):
598 words = document.body[i].split()
599 if len(words) > 1 and words[0] == "\\begin_inset" and \
600 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
602 j = find_end_of_inset(document.body, i)
604 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
610 if document.body[i].find("\\twohyphens") >= 0:
611 document.body[i] = document.body[i].replace("\\twohyphens", "--")
613 if document.body[i].find("\\threehyphens") >= 0:
614 document.body[i] = document.body[i].replace("\\threehyphens", "---")
616 if replaced and i+1 < len(document.body) and \
617 (document.body[i+1].find("\\") != 0 or \
618 document.body[i+1].find("\\twohyphens") == 0 or
619 document.body[i+1].find("\\threehyphens") == 0) and \
620 len(document.body[i]) + len(document.body[i+1]) <= 80:
621 document.body[i] = document.body[i] + document.body[i+1]
622 document.body[i+1:i+2] = []
627 # order is important for the last three!
628 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
630 def is_part_of_converted_phrase(line, j, phrase):
631 "is phrase part of an already converted phrase?"
633 converted = "\\SpecialCharNoPassThru \\" + p
634 pos = j + len(phrase) - len(converted)
636 if line[pos:pos+len(converted)] == converted:
641 def convert_phrases(document):
642 "convert special phrases from plain text to \\SpecialCharNoPassThru"
644 if document.backend != "latex":
647 for phrase in phrases:
649 while i < len(document.body):
650 words = document.body[i].split()
651 if len(words) > 1 and words[0] == "\\begin_inset" and \
652 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
653 # must not replace anything in insets that store LaTeX contents in .lyx files
654 # (math and command insets withut overridden read() and write() methods
655 j = find_end_of_inset(document.body, i)
657 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
662 if document.body[i].find("\\") == 0:
665 j = document.body[i].find(phrase)
669 if not is_part_of_converted_phrase(document.body[i], j, phrase):
670 front = document.body[i][:j]
671 back = document.body[i][j+len(phrase):]
673 document.body.insert(i+1, back)
674 # We cannot use SpecialChar since we do not know whether we are outside passThru
675 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
679 def revert_phrases(document):
680 "convert special phrases to plain text"
683 while i < len(document.body):
684 words = document.body[i].split()
685 if len(words) > 1 and words[0] == "\\begin_inset" and \
686 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
687 # see convert_phrases
688 j = find_end_of_inset(document.body, i)
690 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
696 for phrase in phrases:
697 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
698 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
699 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
701 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
702 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
704 if replaced and i+1 < len(document.body) and \
705 (document.body[i+1].find("\\") != 0 or \
706 document.body[i+1].find("\\SpecialChar") == 0) and \
707 len(document.body[i]) + len(document.body[i+1]) <= 80:
708 document.body[i] = document.body[i] + document.body[i+1]
709 document.body[i+1:i+2] = []
714 def convert_specialchar_internal(document, forward):
715 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
716 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
717 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
718 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
719 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
720 "\\LaTeX":"LaTeX" # must be after LaTeX2e
724 while i < len(document.body):
725 words = document.body[i].split()
726 if len(words) > 1 and words[0] == "\\begin_inset" and \
727 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
728 # see convert_phrases
729 j = find_end_of_inset(document.body, i)
731 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
736 for key, value in specialchars.iteritems():
738 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
739 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
741 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
742 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
746 def convert_specialchar(document):
747 "convert special characters to new syntax"
748 convert_specialchar_internal(document, True)
751 def revert_specialchar(document):
752 "convert special characters to old syntax"
753 convert_specialchar_internal(document, False)
756 def revert_georgian(document):
757 "Set the document language to English but assure Georgian output"
759 if document.language == "georgian":
760 document.language = "english"
761 i = find_token(document.header, "\\language georgian", 0)
763 document.header[i] = "\\language english"
764 j = find_token(document.header, "\\language_package default", 0)
766 document.header[j] = "\\language_package babel"
767 k = find_token(document.header, "\\options", 0)
769 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
771 l = find_token(document.header, "\\use_default_options", 0)
772 document.header.insert(l + 1, "\\options georgian")
775 def revert_sigplan_doi(document):
776 " Reverts sigplanconf DOI layout to ERT "
778 if document.textclass != "sigplanconf":
783 i = find_token(document.body, "\\begin_layout DOI", i)
786 j = find_end_of_layout(document.body, i)
788 document.warning("Malformed LyX document: Can't find end of DOI layout")
792 content = lyx2latex(document, document.body[i:j + 1])
793 add_to_preamble(document, ["\\doi{" + content + "}"])
794 del document.body[i:j + 1]
798 def revert_ex_itemargs(document):
799 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
801 # Do we use the linguistics module?
803 mods = document.get_module_list()
805 if mod == "linguistics":
813 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
815 i = find_token(document.body, "\\begin_inset Argument item:", i)
818 j = find_end_of_inset(document.body, i)
819 # Find containing paragraph layout
820 parent = get_containing_layout(document.body, i)
822 document.warning("Malformed LyX document: Can't find parent paragraph layout")
826 layoutname = parent[0]
827 if layoutname in example_layouts:
828 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
829 endPlain = find_end_of_layout(document.body, beginPlain)
830 content = document.body[beginPlain + 1 : endPlain]
831 del document.body[i:j+1]
832 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
833 document.body[parbeg : parbeg] = subst
837 def revert_forest(document):
838 " Reverts the forest environment (Linguistics module) to TeX-code "
840 # Do we use the linguistics module?
842 mods = document.get_module_list()
844 if mod == "linguistics":
853 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
856 j = find_end_of_inset(document.body, i)
858 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
862 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
863 endPlain = find_end_of_layout(document.body, beginPlain)
864 content = lyx2latex(document, document.body[beginPlain : endPlain])
866 add_to_preamble(document, ["\\usepackage{forest}"])
868 document.body[i:j + 1] = ["\\begin_inset ERT", "status collapsed", "",
869 "\\begin_layout Plain Layout", "", "\\backslash",
870 "begin{forest}", "\\end_layout", "", "\\begin_layout Plain Layout",
871 content, "\\end_layout", "", "\\begin_layout Plain Layout",
872 "\\backslash", "end{forest}", "", "\\end_layout", "", "\\end_inset"]
876 def revert_glossgroup(document):
877 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
879 # Do we use the linguistics module?
881 mods = document.get_module_list()
883 if mod == "linguistics":
892 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
895 j = find_end_of_inset(document.body, i)
897 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
901 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
902 endPlain = find_end_of_layout(document.body, beginPlain)
903 content = lyx2latex(document, document.body[beginPlain : endPlain])
905 document.body[i:j + 1] = ["{", "", content, "", "}"]
909 def revert_newgloss(document):
910 " Reverts the new Glosse insets (Linguistics module) to the old format "
912 # Do we use the linguistics module?
914 mods = document.get_module_list()
916 if mod == "linguistics":
923 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
924 for glosse in glosses:
927 i = find_token(document.body, glosse, i)
930 j = find_end_of_inset(document.body, i)
932 document.warning("Malformed LyX document: Can't find end of Glosse inset")
936 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
937 endarg = find_end_of_inset(document.body, arg)
940 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
941 if argbeginPlain == -1:
942 document.warning("Malformed LyX document: Can't find arg plain Layout")
945 argendPlain = find_end_of_inset(document.body, argbeginPlain)
946 argcontent = lyx2latex(document, document.body[argbeginPlain : argendPlain - 2])
948 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
949 argcontent, "\\end_layout"]
951 # remove Arg insets and paragraph, if it only contains this inset
952 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
953 del document.body[arg - 1 : endarg + 4]
955 del document.body[arg : endarg + 1]
957 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
958 endPlain = find_end_of_layout(document.body, beginPlain)
959 content = lyx2latex(document, document.body[beginPlain : endPlain])
961 document.body[beginPlain + 1:endPlain] = [content]
965 def convert_newgloss(document):
966 " Converts Glosse insets (Linguistics module) to the new format "
968 # Do we use the linguistics module?
970 mods = document.get_module_list()
972 if mod == "linguistics":
979 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
980 for glosse in glosses:
983 i = find_token(document.body, glosse, i)
986 j = find_end_of_inset(document.body, i)
988 document.warning("Malformed LyX document: Can't find end of Glosse inset")
995 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
998 endPlain = find_end_of_layout(document.body, beginPlain)
1000 document.warning("Malformed LyX document: Can't find end of Glosse layout")
1004 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
1005 if glt != -1 and document.body[glt + 1].startswith("glt"):
1006 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1007 argcontent = document.body[glt + 1 : endPlain]
1008 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
1009 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
1010 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
1011 "\\end_layout", "", "\\end_inset"]
1013 content = document.body[beginPlain + 1 : endPlain]
1014 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
1015 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
1017 endPlain = find_end_of_layout(document.body, beginPlain)
1019 j = find_end_of_inset(document.body, i)
1024 def convert_BoxFeatures(document):
1025 " adds new box features "
1029 i = find_token(document.body, "height_special", i)
1032 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1036 def revert_BoxFeatures(document):
1037 " outputs new box features as TeX code "
1041 defaultThick = "0.4pt"
1042 defaultShadow = "4pt"
1044 i = find_token(document.body, "height_special", i)
1047 # read out the values
1048 beg = document.body[i+1].find('"');
1049 end = document.body[i+1].rfind('"');
1050 thickness = document.body[i+1][beg+1:end];
1051 beg = document.body[i+2].find('"');
1052 end = document.body[i+2].rfind('"');
1053 separation = document.body[i+2][beg+1:end];
1054 beg = document.body[i+3].find('"');
1055 end = document.body[i+3].rfind('"');
1056 shadowsize = document.body[i+3][beg+1:end];
1057 # delete the specification
1058 del document.body[i+1:i+4]
1060 # first output the closing brace
1061 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1062 document.body[i + 10 : i + 10] = put_cmd_in_ert("}")
1063 # now output the lengths
1064 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1065 document.body[i - 10 : i - 10] = put_cmd_in_ert("{")
1066 if thickness != defaultThick:
1067 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness]
1068 if separation != defaultSep and thickness == defaultThick:
1069 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation]
1070 if separation != defaultSep and thickness != defaultThick:
1071 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1072 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1073 document.body[i - 5 : i - 4] = ["{\\backslash shadowsize " + shadowsize]
1074 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1075 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1076 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1077 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1078 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1079 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1083 def convert_origin(document):
1084 " Insert the origin tag "
1086 i = find_token(document.header, "\\textclass ", 0)
1088 document.warning("Malformed LyX document: No \\textclass!!")
1090 if document.dir == "":
1094 if document.systemlyxdir and document.systemlyxdir != '':
1096 if os.path.isabs(document.dir):
1097 absdir = os.path.normpath(document.dir)
1099 absdir = os.path.normpath(os.path.abspath(document.dir))
1100 if os.path.isabs(document.systemlyxdir):
1101 abssys = os.path.normpath(document.systemlyxdir)
1103 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1104 relpath = os.path.relpath(absdir, abssys)
1105 if relpath.find('..') == 0:
1110 origin = document.dir.replace('\\', '/') + '/'
1112 origin = os.path.join("/systemlyxdir", relpath).replace('\\', '/') + '/'
1114 origin = unicode(origin, sys.getfilesystemencoding())
1115 document.header[i:i] = ["\\origin " + origin]
1118 def revert_origin(document):
1119 " Remove the origin tag "
1121 i = find_token(document.header, "\\origin ", 0)
1123 document.warning("Malformed LyX document: No \\origin!!")
1125 del document.header[i]
1128 color_names = ["brown", "darkgray", "gray", \
1129 "lightgray", "lime", "olive", "orange", \
1130 "pink", "purple", "teal", "violet"]
1132 def revert_textcolor(document):
1133 " revert new \\textcolor colors to TeX code "
1139 i = find_token(document.body, "\\color ", i)
1143 for color in list(color_names):
1144 if document.body[i] == "\\color " + color:
1145 # register that xcolor must be loaded in the preamble
1148 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\usepackage{xcolor}}{}"])
1149 # find the next \\color and/or the next \\end_layout
1150 j = find_token(document.body, "\\color", i + 1)
1151 k = find_token(document.body, "\\end_layout", i + 1)
1152 if j == -1 and k != -1:
1155 # first output the closing brace
1157 document.body[k: k] = put_cmd_in_ert("}")
1159 document.body[j: j] = put_cmd_in_ert("}")
1160 # now output the \textcolor command
1161 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1165 def convert_colorbox(document):
1166 " adds color settings for boxes "
1170 i = find_token(document.body, "shadowsize", i)
1173 document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1177 def revert_colorbox(document):
1178 " outputs color settings for boxes as TeX code "
1181 defaultframecolor = "black"
1182 defaultbackcolor = "none"
1184 binset = find_token(document.body, "\\begin_inset Box", binset)
1188 einset = find_end_of_inset(document.body, binset)
1190 document.warning("Malformed LyX document: Can't find end of box inset!")
1194 blay = find_token(document.body, "\\begin_layout", binset, einset)
1196 document.warning("Malformed LyX document: Can't find start of layout!")
1200 # doing it this way, we make sure only to find a framecolor option
1201 frame = find_token(document.body, "framecolor", binset, blay)
1206 beg = document.body[frame].find('"')
1207 end = document.body[frame].rfind('"')
1208 framecolor = document.body[frame][beg + 1 : end]
1210 # this should be on the next line
1212 beg = document.body[bgcolor].find('"')
1213 end = document.body[bgcolor].rfind('"')
1214 backcolor = document.body[bgcolor][beg + 1 : end]
1217 del document.body[frame : frame + 2]
1218 # adjust end of inset
1221 if document.body[binset] == "\\begin_inset Box Boxed" and \
1222 framecolor != defaultframecolor:
1223 document.body[binset] = "\\begin_inset Box Frameless"
1226 # first output the closing brace
1227 if framecolor == defaultframecolor and backcolor == defaultbackcolor:
1231 # we also neeed to load xcolor in the preamble but only once
1232 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\usepackage{xcolor}}{}"])
1233 document.body[einset + 1 : einset + 1] = put_cmd_in_ert("}")
1234 if framecolor != defaultframecolor:
1235 document.body[binset:binset] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1237 document.body[binset:binset] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
1242 def revert_mathmulticol(document):
1243 " Convert formulas to ERT if they contain multicolumns "
1247 i = find_token(document.body, '\\begin_inset Formula', i)
1250 j = find_end_of_inset(document.body, i)
1252 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1255 lines = document.body[i:j]
1256 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1257 code = "\n".join(lines)
1262 n = code.find("\\multicolumn", k)
1263 # no need to convert degenerated multicolumn cells,
1264 # they work in old LyX versions as "math ERT"
1265 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1266 ert = put_cmd_in_ert(code)
1267 document.body[i:j+1] = ert
1273 i = find_end_of_inset(document.body, i)
1278 def revert_jss(document):
1279 " Reverts JSS In_Preamble commands to ERT in preamble "
1281 if document.textclass != "jss":
1290 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1291 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1294 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1296 endh = find_end_of_inset(document.body, h)
1297 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1298 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1302 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1304 endm = find_end_of_inset(document.body, m)
1305 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1306 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1310 j = find_token(document.body, "\\begin_inset Flex Code", j)
1312 # assure that we are not in a Code Chunk inset
1313 if document.body[j][-1] == "e":
1314 endj = find_end_of_inset(document.body, j)
1315 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1316 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1322 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1324 endk = find_end_of_inset(document.body, k)
1325 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1326 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1330 n = find_token(document.body, "\\begin_inset Flex URL", n)
1332 endn = find_end_of_inset(document.body, n)
1333 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1334 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1336 # now revert the In_Preamble layouts
1338 i = find_token(document.body, "\\begin_layout Title", 0)
1341 j = find_end_of_layout(document.body, i)
1343 document.warning("Malformed LyX document: Can't find end of Title layout")
1346 content = lyx2latex(document, document.body[i:j + 1])
1347 add_to_preamble(document, ["\\title{" + content + "}"])
1348 del document.body[i:j + 1]
1350 i = find_token(document.body, "\\begin_layout Author", 0)
1353 j = find_end_of_layout(document.body, i)
1355 document.warning("Malformed LyX document: Can't find end of Author layout")
1358 content = lyx2latex(document, document.body[i:j + 1])
1359 add_to_preamble(document, ["\\author{" + content + "}"])
1360 del document.body[i:j + 1]
1362 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1365 j = find_end_of_layout(document.body, i)
1367 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1370 content = lyx2latex(document, document.body[i:j + 1])
1371 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1372 del document.body[i:j + 1]
1374 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1377 j = find_end_of_layout(document.body, i)
1379 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1382 content = lyx2latex(document, document.body[i:j + 1])
1383 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1384 del document.body[i:j + 1]
1386 i = find_token(document.body, "\\begin_layout Short Title", 0)
1389 j = find_end_of_layout(document.body, i)
1391 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1394 content = lyx2latex(document, document.body[i:j + 1])
1395 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1396 del document.body[i:j + 1]
1398 i = find_token(document.body, "\\begin_layout Abstract", 0)
1401 j = find_end_of_layout(document.body, i)
1403 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1406 content = lyx2latex(document, document.body[i:j + 1])
1407 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1408 del document.body[i:j + 1]
1410 i = find_token(document.body, "\\begin_layout Keywords", 0)
1413 j = find_end_of_layout(document.body, i)
1415 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1418 content = lyx2latex(document, document.body[i:j + 1])
1419 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1420 del document.body[i:j + 1]
1422 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1425 j = find_end_of_layout(document.body, i)
1427 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1430 content = lyx2latex(document, document.body[i:j + 1])
1431 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1432 del document.body[i:j + 1]
1434 i = find_token(document.body, "\\begin_layout Address", 0)
1437 j = find_end_of_layout(document.body, i)
1439 document.warning("Malformed LyX document: Can't find end of Address layout")
1442 content = lyx2latex(document, document.body[i:j + 1])
1443 add_to_preamble(document, ["\\Address{" + content + "}"])
1444 del document.body[i:j + 1]
1445 # finally handle the code layouts
1450 while m != -1 or j != -1 or h != -1 or k != -1:
1453 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1455 endh = find_end_of_inset(document.body, h)
1456 document.body[endh + 1 : endh] = ["\\end_layout"]
1457 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1458 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1459 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1463 j = find_token(document.body, "\\begin_layout Code Input", j)
1465 endj = find_end_of_layout(document.body, j)
1466 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1467 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1468 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1469 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1470 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1474 k = find_token(document.body, "\\begin_layout Code Output", k)
1476 endk = find_end_of_layout(document.body, k)
1477 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1478 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1479 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1480 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1481 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1485 m = find_token(document.body, "\\begin_layout Code", m)
1487 endm = find_end_of_layout(document.body, m)
1488 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1489 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1490 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1491 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1492 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1496 def convert_subref(document):
1497 " converts sub: ref prefixes to subref: "
1500 rx = re.compile(r'^name \"sub:(.+)$')
1503 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1506 j = find_end_of_inset(document.body, i)
1508 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1512 for p in range(i, j):
1513 m = rx.match(document.body[p])
1516 document.body[p] = "name \"subsec:" + label
1520 rx = re.compile(r'^reference \"sub:(.+)$')
1523 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1526 j = find_end_of_inset(document.body, i)
1528 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1532 for p in range(i, j):
1533 m = rx.match(document.body[p])
1536 document.body[p] = "reference \"subsec:" + label
1542 def revert_subref(document):
1543 " reverts subref: ref prefixes to sub: "
1546 rx = re.compile(r'^name \"subsec:(.+)$')
1549 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1552 j = find_end_of_inset(document.body, i)
1554 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1558 for p in range(i, j):
1559 m = rx.match(document.body[p])
1562 document.body[p] = "name \"sub:" + label
1567 rx = re.compile(r'^reference \"subsec:(.+)$')
1570 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1573 j = find_end_of_inset(document.body, i)
1575 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1579 for p in range(i, j):
1580 m = rx.match(document.body[p])
1583 document.body[p] = "reference \"sub:" + label
1588 def convert_nounzip(document):
1589 " remove the noUnzip parameter of graphics insets "
1591 rx = re.compile(r'\s*noUnzip\s*$')
1594 i = find_token(document.body, "\\begin_inset Graphics", i)
1597 j = find_end_of_inset(document.body, i)
1599 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1603 k = find_re(document.body, rx, i, j)
1605 del document.body[k]
1610 def convert_revert_external_bbox(document, forward):
1611 " add units to bounding box of external insets "
1613 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1616 i = find_token(document.body, "\\begin_inset External", i)
1619 j = find_end_of_inset(document.body, i)
1621 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1624 k = find_re(document.body, rx, i, j)
1628 tokens = document.body[k].split()
1630 for t in range(1, 5):
1633 for t in range(1, 5):
1634 tokens[t] = length_in_bp(tokens[t])
1635 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1636 tokens[3] + " " + tokens[4]
1640 def convert_external_bbox(document):
1641 convert_revert_external_bbox(document, True)
1644 def revert_external_bbox(document):
1645 convert_revert_external_bbox(document, False)
1648 def revert_tcolorbox_1(document):
1649 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1652 i = find_token(document.header, "tcolorbox", i)
1658 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1661 flexEnd = find_end_of_inset(document.body, flex)
1662 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1663 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1664 flexEnd = find_end_of_inset(document.body, flex)
1666 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1668 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1669 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1673 def revert_tcolorbox_2(document):
1674 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1677 i = find_token(document.header, "tcolorbox", i)
1683 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1686 flexEnd = find_end_of_inset(document.body, flex)
1687 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1688 flexEnd = find_end_of_inset(document.body, flex)
1689 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1690 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1694 def revert_tcolorbox_3(document):
1695 " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1698 i = find_token(document.header, "tcolorbox", i)
1704 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1707 flexEnd = find_end_of_inset(document.body, flex)
1708 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1709 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1710 flexEnd = find_end_of_inset(document.body, flex)
1711 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1712 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1716 def revert_tcolorbox_4(document):
1717 " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1720 i = find_token(document.header, "tcolorbox", i)
1726 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1729 flexEnd = find_end_of_inset(document.body, flex)
1730 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1731 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1732 flexEnd = find_end_of_inset(document.body, flex)
1733 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1734 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1738 def revert_tcolorbox_5(document):
1739 " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1742 i = find_token(document.header, "tcolorbox", i)
1748 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1751 flexEnd = find_end_of_inset(document.body, flex)
1752 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1753 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1754 flexEnd = find_end_of_inset(document.body, flex)
1755 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1756 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1760 def revert_tcolorbox_6(document):
1761 " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1764 i = find_token(document.header, "tcolorbox", i)
1770 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1773 flexEnd = find_end_of_inset(document.body, flex)
1774 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1775 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1776 flexEnd = find_end_of_inset(document.body, flex)
1777 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1778 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1782 def revert_tcolorbox_7(document):
1783 " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1786 i = find_token(document.header, "tcolorbox", i)
1792 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1795 flexEnd = find_end_of_inset(document.body, flex)
1796 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1797 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1798 flexEnd = find_end_of_inset(document.body, flex)
1799 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1800 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1804 def revert_tcolorbox_8(document):
1805 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1811 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1813 j = find_end_of_layout(document.body, i)
1814 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1815 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1816 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1817 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1819 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1821 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1822 k = find_end_of_inset(document.body, j)
1823 k = find_token(document.body, "\\end_inset", k + 1)
1824 k = find_token(document.body, "\\end_inset", k + 1)
1826 k = find_token(document.body, "\\end_inset", k + 1)
1827 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1828 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1829 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1835 def revert_moderncv_1(document):
1836 " Reverts the new inset of moderncv to TeX-code in preamble "
1838 if document.textclass != "moderncv":
1844 # at first revert the new styles
1846 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1849 j = find_end_of_layout(document.body, i)
1851 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1854 content = lyx2latex(document, document.body[i:j + 1])
1855 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1856 del document.body[i:j + 1]
1858 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1861 j = find_end_of_layout(document.body, i)
1863 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1866 content = lyx2latex(document, document.body[i:j + 1])
1867 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1868 del document.body[i:j + 1]
1869 # now change the new styles to the obsolete ones
1871 i = find_token(document.body, "\\begin_layout Name", 0)
1874 j = find_end_of_layout(document.body, i)
1876 document.warning("Malformed LyX document: Can't find end of Name layout")
1879 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1880 if lineArg > j and j != 0:
1883 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1884 # we have to assure that no other inset is in the Argument
1885 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1886 endInset = find_token(document.body, "\\end_inset", beginPlain)
1889 while beginInset < endInset and beginInset != -1:
1890 beginInset = find_token(document.body, "\\begin_inset", k)
1891 endInset = find_token(document.body, "\\end_inset", l)
1894 Arg2 = document.body[l + 5 : l + 6]
1896 document.body[i : i + 1]= ["\\begin_layout FirstName"]
1897 # delete the Argument inset
1898 del( document.body[endInset - 2 : endInset + 3])
1899 del( document.body[lineArg : beginPlain + 1])
1900 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1903 def revert_moderncv_2(document):
1904 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1906 if document.textclass != "moderncv":
1913 i = find_token(document.body, "\\begin_layout Phone", i)
1916 j = find_end_of_layout(document.body, i)
1918 document.warning("Malformed LyX document: Can't find end of Phone layout")
1921 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1922 if lineArg > j and j != 0:
1926 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1927 # we have to assure that no other inset is in the Argument
1928 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1929 endInset = find_token(document.body, "\\end_inset", beginPlain)
1932 while beginInset < endInset and beginInset != -1:
1933 beginInset = find_token(document.body, "\\begin_inset", k)
1934 endInset = find_token(document.body, "\\end_inset", l)
1937 Arg = document.body[beginPlain + 1 : beginPlain + 2]
1939 if Arg[0] == "mobile":
1940 document.body[i : i + 1]= ["\\begin_layout Mobile"]
1942 document.body[i : i + 1]= ["\\begin_layout Fax"]
1943 # delete the Argument inset
1944 del(document.body[endInset - 2 : endInset + 1])
1945 del(document.body[lineArg : beginPlain + 3])
1949 def convert_moderncv(document):
1950 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
1952 if document.textclass != "moderncv":
1959 i = find_token(document.body, "\\begin_layout Mobile", i)
1962 j = find_end_of_layout(document.body, i)
1964 document.warning("Malformed LyX document: Can't find end of Mobile layout")
1967 document.body[i + 1 : i + 1] = ["\\begin_inset Argument 1", "status open", "",
1968 "\\begin_layout Plain Layout", "mobile", "\\end_layout", "",
1971 i = find_token(document.body, "\\begin_layout Fax", i)
1974 j = find_end_of_layout(document.body, i)
1976 document.warning("Malformed LyX document: Can't find end of Fax layout")
1979 document.body[i + 1 : i + 1] = ["\\begin_inset Argument 1", "status open", "",
1980 "\\begin_layout Plain Layout", "fax", "\\end_layout", "",
1982 # \firstname and \familyname
1983 i1 = find_token(document.body, "\\begin_layout FirstName", 0)
1986 j1 = find_end_of_layout(document.body, i1)
1988 document.warning("Malformed LyX document: Can't find end of FirstName layout")
1991 FirstName = document.body[i1 + 1 : i1 + 2]
1992 i2 = find_token(document.body, "\\begin_layout FamilyName", 0)
1995 j2 = find_end_of_layout(document.body, i2)
1997 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
2000 FamilyName = document.body[i2 + 1 : i2 + 2]
2007 document.body[k + 1 : k + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2008 "\\begin_layout Plain Layout", FirstName[0], "\\end_layout", "",
2009 "\\end_inset", "", FamilyName[0], "\\end_layout", ""]
2010 #document.body[i2 + 1 : i2 + 1] = ["hellok: ", str(k)]
2011 del(document.body[l : k])
2017 def revert_achemso(document):
2018 " Reverts the flex inset Latin to TeX code "
2020 if document.textclass != "achemso":
2025 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2027 j = find_end_of_inset(document.body, i)
2031 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2032 endPlain = find_end_of_layout(document.body, beginPlain)
2033 content = lyx2latex(document, document.body[beginPlain : endPlain])
2034 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2036 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2041 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2042 "\\font_sf_scale", "\\font_tt_scale"]
2043 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2044 fontquotes = [True, True, True, True, False, False]
2046 def convert_fontsettings(document):
2047 " Duplicate font settings "
2049 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2051 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2052 use_non_tex_fonts = "false"
2054 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2056 for f in fontsettings:
2057 i = find_token(document.header, f + " ", 0)
2059 document.warning("Malformed LyX document: No " + f + "!")
2062 value = document.header[i][len(f):].strip()
2064 if use_non_tex_fonts == "true":
2065 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2067 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2069 if use_non_tex_fonts == "true":
2070 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2072 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2076 def revert_fontsettings(document):
2077 " Merge font settings "
2079 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2081 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2082 use_non_tex_fonts = "false"
2084 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2086 for f in fontsettings:
2087 i = find_token(document.header, f + " ", 0)
2089 document.warning("Malformed LyX document: No " + f + "!")
2092 line = get_value(document.header, f, i)
2095 q2 = line.find('"', q1+1)
2096 q3 = line.find('"', q2+1)
2097 q4 = line.find('"', q3+1)
2098 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2099 document.warning("Malformed LyX document: Missing quotes!")
2102 if use_non_tex_fonts == "true":
2103 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2105 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2107 if use_non_tex_fonts == "true":
2108 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2110 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2114 def revert_solution(document):
2115 " Reverts the solution environment of the theorem module to TeX code "
2117 # Do we use one of the modules that provides Solution?
2119 mods = document.get_module_list()
2121 if mod == "theorems-std" or mod == "theorems-bytype" \
2122 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2132 i = find_token(document.body, "\\begin_layout Solution", i)
2136 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2137 if is_starred == True:
2139 LyXName = "Solution*"
2140 theoremName = "newtheorem*"
2143 LyXName = "Solution"
2144 theoremName = "newtheorem"
2146 j = find_end_of_layout(document.body, i)
2148 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2152 # if this is not a consecutive env, add start command
2155 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2157 # has this a consecutive theorem of same type?
2158 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2160 # if this is not followed by a consecutive env, add end command
2162 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2164 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2166 add_to_preamble(document, "\\theoremstyle{definition}")
2167 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2168 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2169 (theoremName, LaTeXName))
2170 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2171 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2172 (theoremName, LaTeXName))
2174 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2178 def revert_verbatim_star(document):
2179 from lyx_2_1 import revert_verbatim
2180 revert_verbatim(document, True)
2187 supported_versions = ["2.2.0", "2.2"]
2189 [475, [convert_separator]],
2190 # nothing to do for 476: We consider it a bug that older versions
2191 # did not load amsmath automatically for these commands, and do not
2192 # want to hardcode amsmath off.
2198 [481, [convert_dashes]],
2199 [482, [convert_phrases]],
2200 [483, [convert_specialchar]],
2205 [488, [convert_newgloss]],
2206 [489, [convert_BoxFeatures]],
2207 [490, [convert_origin]],
2209 [492, [convert_colorbox]],
2212 [495, [convert_subref]],
2213 [496, [convert_nounzip]],
2214 [497, [convert_external_bbox]],
2216 [499, [convert_moderncv]],
2218 [501, [convert_fontsettings]],
2224 [502, [revert_verbatim_star]],
2225 [501, [revert_solution]],
2226 [500, [revert_fontsettings]],
2227 [499, [revert_achemso]],
2228 [498, [revert_moderncv_1, revert_moderncv_2]],
2229 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2230 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2231 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2232 [496, [revert_external_bbox]],
2233 [495, []], # nothing to do since the noUnzip parameter was optional
2234 [494, [revert_subref]],
2235 [493, [revert_jss]],
2236 [492, [revert_mathmulticol]],
2237 [491, [revert_colorbox]],
2238 [490, [revert_textcolor]],
2239 [489, [revert_origin]],
2240 [488, [revert_BoxFeatures]],
2241 [487, [revert_newgloss, revert_glossgroup]],
2242 [486, [revert_forest]],
2243 [485, [revert_ex_itemargs]],
2244 [484, [revert_sigplan_doi]],
2245 [483, [revert_georgian]],
2246 [482, [revert_specialchar]],
2247 [481, [revert_phrases]],
2248 [480, [revert_dashes]],
2249 [479, [revert_question_env]],
2250 [478, [revert_beamer_lemma]],
2251 [477, [revert_xarrow]],
2252 [476, [revert_swissgerman]],
2253 [475, [revert_smash]],
2254 [474, [revert_separator]]
2258 if __name__ == "__main__":