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, get_ert, lyx2latex, \
34 lyx2verbatim, length_in_bp
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?
489 if not "theorems-ams-extended-bytype" in document.get_module_list():
495 i = find_token(document.body, "\\begin_layout Question", i)
499 starred = document.body[i] == "\\begin_layout Question*"
501 j = find_end_of_layout(document.body, i)
503 document.warning("Malformed LyX document: Can't find end of Question layout")
507 # if this is not a consecutive env, add start command
511 begcmd = put_cmd_in_ert("\\begin{question*}")
513 begcmd = put_cmd_in_ert("\\begin{question}")
515 # has this a consecutive theorem of same type?
518 consecutive = document.body[j + 2] == "\\begin_layout Question*"
520 consecutive = document.body[j + 2] == "\\begin_layout Question"
522 # if this is not followed by a consecutive env, add end command
525 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
527 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
529 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
531 add_to_preamble(document, "\\providecommand{\questionname}{Question}")
534 add_to_preamble(document, "\\theoremstyle{plain}\n" \
535 "\\newtheorem*{question*}{\\protect\\questionname}")
537 add_to_preamble(document, "\\theoremstyle{plain}\n" \
538 "\\newtheorem{question}{\\protect\\questionname}")
543 def convert_dashes(document):
544 "convert -- and --- to \\twohyphens and \\threehyphens"
546 if document.backend != "latex":
550 while i < len(document.body):
551 words = document.body[i].split()
552 if len(words) > 1 and words[0] == "\\begin_inset" and \
553 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
554 # must not replace anything in insets that store LaTeX contents in .lyx files
555 # (math and command insets withut overridden read() and write() methods
556 # filtering out IPA makes Text::readParToken() more simple
557 # skip ERT as well since it is not needed there
558 j = find_end_of_inset(document.body, i)
560 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
566 j = document.body[i].find("--")
569 front = document.body[i][:j]
570 back = document.body[i][j+2:]
571 # We can have an arbitrary number of consecutive hyphens.
572 # These must be split into the corresponding number of two and three hyphens
573 # We must match what LaTeX does: First try emdash, then endash, then single hyphen
574 if back.find("-") == 0:
577 document.body.insert(i+1, back)
578 document.body[i] = front + "\\threehyphens"
581 document.body.insert(i+1, back)
582 document.body[i] = front + "\\twohyphens"
586 def revert_dashes(document):
587 "convert \\twohyphens and \\threehyphens to -- and ---"
590 while i < len(document.body):
591 words = document.body[i].split()
592 if len(words) > 1 and words[0] == "\\begin_inset" and \
593 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
595 j = find_end_of_inset(document.body, i)
597 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
603 if document.body[i].find("\\twohyphens") >= 0:
604 document.body[i] = document.body[i].replace("\\twohyphens", "--")
606 if document.body[i].find("\\threehyphens") >= 0:
607 document.body[i] = document.body[i].replace("\\threehyphens", "---")
609 if replaced and i+1 < len(document.body) and \
610 (document.body[i+1].find("\\") != 0 or \
611 document.body[i+1].find("\\twohyphens") == 0 or
612 document.body[i+1].find("\\threehyphens") == 0) and \
613 len(document.body[i]) + len(document.body[i+1]) <= 80:
614 document.body[i] = document.body[i] + document.body[i+1]
615 document.body[i+1:i+2] = []
620 # order is important for the last three!
621 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
623 def is_part_of_converted_phrase(line, j, phrase):
624 "is phrase part of an already converted phrase?"
626 converted = "\\SpecialCharNoPassThru \\" + p
627 pos = j + len(phrase) - len(converted)
629 if line[pos:pos+len(converted)] == converted:
634 def convert_phrases(document):
635 "convert special phrases from plain text to \\SpecialCharNoPassThru"
637 if document.backend != "latex":
640 for phrase in phrases:
642 while i < len(document.body):
643 words = document.body[i].split()
644 if len(words) > 1 and words[0] == "\\begin_inset" and \
645 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
646 # must not replace anything in insets that store LaTeX contents in .lyx files
647 # (math and command insets withut overridden read() and write() methods
648 j = find_end_of_inset(document.body, i)
650 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
655 if document.body[i].find("\\") == 0:
658 j = document.body[i].find(phrase)
662 if not is_part_of_converted_phrase(document.body[i], j, phrase):
663 front = document.body[i][:j]
664 back = document.body[i][j+len(phrase):]
666 document.body.insert(i+1, back)
667 # We cannot use SpecialChar since we do not know whether we are outside passThru
668 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
672 def revert_phrases(document):
673 "convert special phrases to plain text"
676 while i < len(document.body):
677 words = document.body[i].split()
678 if len(words) > 1 and words[0] == "\\begin_inset" and \
679 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
680 # see convert_phrases
681 j = find_end_of_inset(document.body, i)
683 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
689 for phrase in phrases:
690 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
691 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
692 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
694 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
695 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
697 if replaced and i+1 < len(document.body) and \
698 (document.body[i+1].find("\\") != 0 or \
699 document.body[i+1].find("\\SpecialChar") == 0) and \
700 len(document.body[i]) + len(document.body[i+1]) <= 80:
701 document.body[i] = document.body[i] + document.body[i+1]
702 document.body[i+1:i+2] = []
707 def convert_specialchar_internal(document, forward):
708 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
709 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
710 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
711 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
712 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
713 "\\LaTeX":"LaTeX" # must be after LaTeX2e
717 while i < len(document.body):
718 words = document.body[i].split()
719 if len(words) > 1 and words[0] == "\\begin_inset" and \
720 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
721 # see convert_phrases
722 j = find_end_of_inset(document.body, i)
724 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
729 for key, value in specialchars.iteritems():
731 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
732 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
734 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
735 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
739 def convert_specialchar(document):
740 "convert special characters to new syntax"
741 convert_specialchar_internal(document, True)
744 def revert_specialchar(document):
745 "convert special characters to old syntax"
746 convert_specialchar_internal(document, False)
749 def revert_georgian(document):
750 "Set the document language to English but assure Georgian output"
752 if document.language == "georgian":
753 document.language = "english"
754 i = find_token(document.header, "\\language georgian", 0)
756 document.header[i] = "\\language english"
757 j = find_token(document.header, "\\language_package default", 0)
759 document.header[j] = "\\language_package babel"
760 k = find_token(document.header, "\\options", 0)
762 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
764 l = find_token(document.header, "\\use_default_options", 0)
765 document.header.insert(l + 1, "\\options georgian")
768 def revert_sigplan_doi(document):
769 " Reverts sigplanconf DOI layout to ERT "
771 if document.textclass != "sigplanconf":
776 i = find_token(document.body, "\\begin_layout DOI", i)
779 j = find_end_of_layout(document.body, i)
781 document.warning("Malformed LyX document: Can't find end of DOI layout")
785 content = lyx2latex(document, document.body[i:j + 1])
786 add_to_preamble(document, ["\\doi{" + content + "}"])
787 del document.body[i:j + 1]
791 def revert_ex_itemargs(document):
792 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
794 if not "linguistics" in document.get_module_list():
798 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
800 i = find_token(document.body, "\\begin_inset Argument item:", i)
803 j = find_end_of_inset(document.body, i)
804 # Find containing paragraph layout
805 parent = get_containing_layout(document.body, i)
807 document.warning("Malformed LyX document: Can't find parent paragraph layout")
811 layoutname = parent[0]
812 if layoutname in example_layouts:
813 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
814 endPlain = find_end_of_layout(document.body, beginPlain)
815 content = document.body[beginPlain + 1 : endPlain]
816 del document.body[i:j+1]
817 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
818 document.body[parbeg : parbeg] = subst
822 def revert_forest(document):
823 " Reverts the forest environment (Linguistics module) to TeX-code "
825 if not "linguistics" in document.get_module_list():
830 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
833 j = find_end_of_inset(document.body, i)
835 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
839 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
840 endPlain = find_end_of_layout(document.body, beginPlain)
841 content = lyx2latex(document, document.body[beginPlain : endPlain])
843 add_to_preamble(document, ["\\usepackage{forest}"])
845 document.body[i:j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
849 def revert_glossgroup(document):
850 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
852 if not "linguistics" in document.get_module_list():
857 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
860 j = find_end_of_inset(document.body, i)
862 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
866 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
867 endPlain = find_end_of_layout(document.body, beginPlain)
868 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
870 document.body[i:j + 1] = ["{", "", content, "", "}"]
874 def revert_newgloss(document):
875 " Reverts the new Glosse insets (Linguistics module) to the old format "
877 if not "linguistics" in document.get_module_list():
880 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
881 for glosse in glosses:
884 i = find_token(document.body, glosse, i)
887 j = find_end_of_inset(document.body, i)
889 document.warning("Malformed LyX document: Can't find end of Glosse inset")
893 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
894 endarg = find_end_of_inset(document.body, arg)
897 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
898 if argbeginPlain == -1:
899 document.warning("Malformed LyX document: Can't find arg plain Layout")
902 argendPlain = find_end_of_inset(document.body, argbeginPlain)
903 argcontent = lyx2verbatim(document, document.body[argbeginPlain : argendPlain - 2])
905 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
906 argcontent, "\\end_layout"]
908 # remove Arg insets and paragraph, if it only contains this inset
909 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
910 del document.body[arg - 1 : endarg + 4]
912 del document.body[arg : endarg + 1]
914 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
915 endPlain = find_end_of_layout(document.body, beginPlain)
916 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
918 document.body[beginPlain + 1:endPlain] = [content]
921 # Dissolve ERT insets
922 for glosse in glosses:
925 i = find_token(document.body, glosse, i)
928 j = find_end_of_inset(document.body, i)
930 document.warning("Malformed LyX document: Can't find end of Glosse inset")
934 ert = find_token(document.body, "\\begin_inset ERT", i, j)
937 ertend = find_end_of_inset(document.body, ert)
939 document.warning("Malformed LyX document: Can't find end of ERT inset")
942 ertcontent = get_ert(document.body, ert, True)
943 document.body[ert : ertend + 1] = [ertcontent]
947 def convert_newgloss(document):
948 " Converts Glosse insets (Linguistics module) to the new format "
950 if not "linguistics" in document.get_module_list():
953 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
954 for glosse in glosses:
957 i = find_token(document.body, glosse, i)
960 j = find_end_of_inset(document.body, i)
962 document.warning("Malformed LyX document: Can't find end of Glosse inset")
969 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
972 endPlain = find_end_of_layout(document.body, beginPlain)
974 document.warning("Malformed LyX document: Can't find end of Glosse layout")
978 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
979 if glt != -1 and document.body[glt + 1].startswith("glt"):
980 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
981 argcontent = document.body[glt + 1 : endPlain]
982 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
983 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
984 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
985 "\\end_layout", "", "\\end_inset"]
987 content = document.body[beginPlain + 1 : endPlain]
988 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
989 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
991 endPlain = find_end_of_layout(document.body, beginPlain)
993 j = find_end_of_inset(document.body, i)
998 def convert_BoxFeatures(document):
999 " adds new box features "
1003 i = find_token(document.body, "height_special", i)
1006 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1010 def revert_BoxFeatures(document):
1011 " outputs new box features as TeX code "
1015 defaultThick = "0.4pt"
1016 defaultShadow = "4pt"
1018 i = find_token(document.body, "height_special", i)
1021 # read out the values
1022 beg = document.body[i+1].find('"');
1023 end = document.body[i+1].rfind('"');
1024 thickness = document.body[i+1][beg+1:end];
1025 beg = document.body[i+2].find('"');
1026 end = document.body[i+2].rfind('"');
1027 separation = document.body[i+2][beg+1:end];
1028 beg = document.body[i+3].find('"');
1029 end = document.body[i+3].rfind('"');
1030 shadowsize = document.body[i+3][beg+1:end];
1031 # delete the specification
1032 del document.body[i+1:i+4]
1034 # first output the closing brace
1035 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1036 document.body[i + 10 : i + 10] = put_cmd_in_ert("}")
1037 # now output the lengths
1038 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1039 document.body[i - 10 : i - 10] = put_cmd_in_ert("{")
1040 if thickness != defaultThick:
1041 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness]
1042 if separation != defaultSep and thickness == defaultThick:
1043 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation]
1044 if separation != defaultSep and thickness != defaultThick:
1045 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1046 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1047 document.body[i - 5 : i - 4] = ["{\\backslash shadowsize " + shadowsize]
1048 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1049 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1050 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1051 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1052 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1053 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1057 def convert_origin(document):
1058 " Insert the origin tag "
1060 i = find_token(document.header, "\\textclass ", 0)
1062 document.warning("Malformed LyX document: No \\textclass!!")
1064 if document.dir == "":
1068 if document.systemlyxdir and document.systemlyxdir != '':
1070 if os.path.isabs(document.dir):
1071 absdir = os.path.normpath(document.dir)
1073 absdir = os.path.normpath(os.path.abspath(document.dir))
1074 if os.path.isabs(document.systemlyxdir):
1075 abssys = os.path.normpath(document.systemlyxdir)
1077 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1078 relpath = os.path.relpath(absdir, abssys)
1079 if relpath.find('..') == 0:
1084 origin = document.dir.replace('\\', '/') + '/'
1086 origin = os.path.join("/systemlyxdir", relpath).replace('\\', '/') + '/'
1088 origin = unicode(origin, sys.getfilesystemencoding())
1089 document.header[i:i] = ["\\origin " + origin]
1092 def revert_origin(document):
1093 " Remove the origin tag "
1095 i = find_token(document.header, "\\origin ", 0)
1097 document.warning("Malformed LyX document: No \\origin!!")
1099 del document.header[i]
1102 color_names = ["brown", "darkgray", "gray", \
1103 "lightgray", "lime", "olive", "orange", \
1104 "pink", "purple", "teal", "violet"]
1106 def revert_textcolor(document):
1107 " revert new \\textcolor colors to TeX code "
1113 i = find_token(document.body, "\\color ", i)
1117 for color in list(color_names):
1118 if document.body[i] == "\\color " + color:
1119 # register that xcolor must be loaded in the preamble
1122 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\usepackage{xcolor}}{}"])
1123 # find the next \\color and/or the next \\end_layout
1124 j = find_token(document.body, "\\color", i + 1)
1125 k = find_token(document.body, "\\end_layout", i + 1)
1126 if j == -1 and k != -1:
1129 # first output the closing brace
1131 document.body[k: k] = put_cmd_in_ert("}")
1133 document.body[j: j] = put_cmd_in_ert("}")
1134 # now output the \textcolor command
1135 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1139 def convert_colorbox(document):
1140 " adds color settings for boxes "
1144 i = find_token(document.body, "shadowsize", i)
1147 document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1151 def revert_colorbox(document):
1152 " outputs color settings for boxes as TeX code "
1155 defaultframecolor = "black"
1156 defaultbackcolor = "none"
1158 binset = find_token(document.body, "\\begin_inset Box", binset)
1162 einset = find_end_of_inset(document.body, binset)
1164 document.warning("Malformed LyX document: Can't find end of box inset!")
1168 blay = find_token(document.body, "\\begin_layout", binset, einset)
1170 document.warning("Malformed LyX document: Can't find start of layout!")
1174 # doing it this way, we make sure only to find a framecolor option
1175 frame = find_token(document.body, "framecolor", binset, blay)
1180 beg = document.body[frame].find('"')
1181 end = document.body[frame].rfind('"')
1182 framecolor = document.body[frame][beg + 1 : end]
1184 # this should be on the next line
1186 beg = document.body[bgcolor].find('"')
1187 end = document.body[bgcolor].rfind('"')
1188 backcolor = document.body[bgcolor][beg + 1 : end]
1191 del document.body[frame : frame + 2]
1192 # adjust end of inset
1195 if document.body[binset] == "\\begin_inset Box Boxed" and \
1196 framecolor != defaultframecolor:
1197 document.body[binset] = "\\begin_inset Box Frameless"
1200 # first output the closing brace
1201 if framecolor == defaultframecolor and backcolor == defaultbackcolor:
1205 # we also neeed to load xcolor in the preamble but only once
1206 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\usepackage{xcolor}}{}"])
1207 document.body[einset + 1 : einset + 1] = put_cmd_in_ert("}")
1208 if framecolor != defaultframecolor:
1209 document.body[binset:binset] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1211 document.body[binset:binset] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
1216 def revert_mathmulticol(document):
1217 " Convert formulas to ERT if they contain multicolumns "
1221 i = find_token(document.body, '\\begin_inset Formula', i)
1224 j = find_end_of_inset(document.body, i)
1226 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1229 lines = document.body[i:j]
1230 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1231 code = "\n".join(lines)
1236 n = code.find("\\multicolumn", k)
1237 # no need to convert degenerated multicolumn cells,
1238 # they work in old LyX versions as "math ERT"
1239 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1240 ert = put_cmd_in_ert(code)
1241 document.body[i:j+1] = ert
1247 i = find_end_of_inset(document.body, i)
1252 def revert_jss(document):
1253 " Reverts JSS In_Preamble commands to ERT in preamble "
1255 if document.textclass != "jss":
1264 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1265 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1268 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1270 endh = find_end_of_inset(document.body, h)
1271 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1272 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1276 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1278 endm = find_end_of_inset(document.body, m)
1279 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1280 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1284 j = find_token(document.body, "\\begin_inset Flex Code", j)
1286 # assure that we are not in a Code Chunk inset
1287 if document.body[j][-1] == "e":
1288 endj = find_end_of_inset(document.body, j)
1289 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1290 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1296 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1298 endk = find_end_of_inset(document.body, k)
1299 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1300 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1304 n = find_token(document.body, "\\begin_inset Flex URL", n)
1306 endn = find_end_of_inset(document.body, n)
1307 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1308 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1310 # now revert the In_Preamble layouts
1312 i = find_token(document.body, "\\begin_layout Title", 0)
1315 j = find_end_of_layout(document.body, i)
1317 document.warning("Malformed LyX document: Can't find end of Title layout")
1320 content = lyx2latex(document, document.body[i:j + 1])
1321 add_to_preamble(document, ["\\title{" + content + "}"])
1322 del document.body[i:j + 1]
1324 i = find_token(document.body, "\\begin_layout Author", 0)
1327 j = find_end_of_layout(document.body, i)
1329 document.warning("Malformed LyX document: Can't find end of Author layout")
1332 content = lyx2latex(document, document.body[i:j + 1])
1333 add_to_preamble(document, ["\\author{" + content + "}"])
1334 del document.body[i:j + 1]
1336 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1339 j = find_end_of_layout(document.body, i)
1341 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1344 content = lyx2latex(document, document.body[i:j + 1])
1345 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1346 del document.body[i:j + 1]
1348 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1351 j = find_end_of_layout(document.body, i)
1353 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1356 content = lyx2latex(document, document.body[i:j + 1])
1357 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1358 del document.body[i:j + 1]
1360 i = find_token(document.body, "\\begin_layout Short Title", 0)
1363 j = find_end_of_layout(document.body, i)
1365 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1368 content = lyx2latex(document, document.body[i:j + 1])
1369 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1370 del document.body[i:j + 1]
1372 i = find_token(document.body, "\\begin_layout Abstract", 0)
1375 j = find_end_of_layout(document.body, i)
1377 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1380 content = lyx2latex(document, document.body[i:j + 1])
1381 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1382 del document.body[i:j + 1]
1384 i = find_token(document.body, "\\begin_layout Keywords", 0)
1387 j = find_end_of_layout(document.body, i)
1389 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1392 content = lyx2latex(document, document.body[i:j + 1])
1393 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1394 del document.body[i:j + 1]
1396 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1399 j = find_end_of_layout(document.body, i)
1401 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1404 content = lyx2latex(document, document.body[i:j + 1])
1405 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1406 del document.body[i:j + 1]
1408 i = find_token(document.body, "\\begin_layout Address", 0)
1411 j = find_end_of_layout(document.body, i)
1413 document.warning("Malformed LyX document: Can't find end of Address layout")
1416 content = lyx2latex(document, document.body[i:j + 1])
1417 add_to_preamble(document, ["\\Address{" + content + "}"])
1418 del document.body[i:j + 1]
1419 # finally handle the code layouts
1424 while m != -1 or j != -1 or h != -1 or k != -1:
1427 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1429 endh = find_end_of_inset(document.body, h)
1430 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1431 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1432 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1436 j = find_token(document.body, "\\begin_layout Code Input", j)
1438 endj = find_end_of_layout(document.body, j)
1439 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1440 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1441 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1442 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1443 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1447 k = find_token(document.body, "\\begin_layout Code Output", k)
1449 endk = find_end_of_layout(document.body, k)
1450 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1451 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1452 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1453 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1454 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1458 m = find_token(document.body, "\\begin_layout Code", m)
1460 endm = find_end_of_layout(document.body, m)
1461 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1462 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1463 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1464 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1465 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1469 def convert_subref(document):
1470 " converts sub: ref prefixes to subref: "
1473 rx = re.compile(r'^name \"sub:(.+)$')
1476 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1479 j = find_end_of_inset(document.body, i)
1481 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1485 for p in range(i, j):
1486 m = rx.match(document.body[p])
1489 document.body[p] = "name \"subsec:" + label
1493 rx = re.compile(r'^reference \"sub:(.+)$')
1496 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1499 j = find_end_of_inset(document.body, i)
1501 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1505 for p in range(i, j):
1506 m = rx.match(document.body[p])
1509 document.body[p] = "reference \"subsec:" + label
1515 def revert_subref(document):
1516 " reverts subref: ref prefixes to sub: "
1519 rx = re.compile(r'^name \"subsec:(.+)$')
1522 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1525 j = find_end_of_inset(document.body, i)
1527 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1531 for p in range(i, j):
1532 m = rx.match(document.body[p])
1535 document.body[p] = "name \"sub:" + label
1540 rx = re.compile(r'^reference \"subsec:(.+)$')
1543 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1546 j = find_end_of_inset(document.body, i)
1548 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1552 for p in range(i, j):
1553 m = rx.match(document.body[p])
1556 document.body[p] = "reference \"sub:" + label
1561 def convert_nounzip(document):
1562 " remove the noUnzip parameter of graphics insets "
1564 rx = re.compile(r'\s*noUnzip\s*$')
1567 i = find_token(document.body, "\\begin_inset Graphics", i)
1570 j = find_end_of_inset(document.body, i)
1572 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1576 k = find_re(document.body, rx, i, j)
1578 del document.body[k]
1583 def convert_revert_external_bbox(document, forward):
1584 " add units to bounding box of external insets "
1586 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1589 i = find_token(document.body, "\\begin_inset External", i)
1592 j = find_end_of_inset(document.body, i)
1594 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1597 k = find_re(document.body, rx, i, j)
1601 tokens = document.body[k].split()
1603 for t in range(1, 5):
1606 for t in range(1, 5):
1607 tokens[t] = length_in_bp(tokens[t])
1608 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1609 tokens[3] + " " + tokens[4]
1613 def convert_external_bbox(document):
1614 convert_revert_external_bbox(document, True)
1617 def revert_external_bbox(document):
1618 convert_revert_external_bbox(document, False)
1621 def revert_tcolorbox_1(document):
1622 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1625 i = find_token(document.header, "tcolorbox", i)
1631 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1634 flexEnd = find_end_of_inset(document.body, flex)
1635 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1636 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1637 flexEnd = find_end_of_inset(document.body, flex)
1639 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1641 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1642 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1646 def revert_tcolorbox_2(document):
1647 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1650 i = find_token(document.header, "tcolorbox", i)
1656 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1659 flexEnd = find_end_of_inset(document.body, flex)
1660 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1661 flexEnd = find_end_of_inset(document.body, flex)
1662 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1663 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1667 def revert_tcolorbox_3(document):
1668 " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1671 i = find_token(document.header, "tcolorbox", i)
1677 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1680 flexEnd = find_end_of_inset(document.body, flex)
1681 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1682 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1683 flexEnd = find_end_of_inset(document.body, flex)
1684 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1685 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1689 def revert_tcolorbox_4(document):
1690 " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1693 i = find_token(document.header, "tcolorbox", i)
1699 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1702 flexEnd = find_end_of_inset(document.body, flex)
1703 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1704 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1705 flexEnd = find_end_of_inset(document.body, flex)
1706 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1707 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1711 def revert_tcolorbox_5(document):
1712 " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1715 i = find_token(document.header, "tcolorbox", i)
1721 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1724 flexEnd = find_end_of_inset(document.body, flex)
1725 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1726 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1727 flexEnd = find_end_of_inset(document.body, flex)
1728 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1729 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1733 def revert_tcolorbox_6(document):
1734 " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1737 i = find_token(document.header, "tcolorbox", i)
1743 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1746 flexEnd = find_end_of_inset(document.body, flex)
1747 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1748 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1749 flexEnd = find_end_of_inset(document.body, flex)
1750 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1751 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1755 def revert_tcolorbox_7(document):
1756 " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1759 i = find_token(document.header, "tcolorbox", i)
1765 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1768 flexEnd = find_end_of_inset(document.body, flex)
1769 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1770 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1771 flexEnd = find_end_of_inset(document.body, flex)
1772 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1773 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1777 def revert_tcolorbox_8(document):
1778 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1784 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1786 j = find_end_of_layout(document.body, i)
1787 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1788 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1789 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1790 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1792 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1794 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1795 k = find_end_of_inset(document.body, j)
1796 k = find_token(document.body, "\\end_inset", k + 1)
1797 k = find_token(document.body, "\\end_inset", k + 1)
1799 k = find_token(document.body, "\\end_inset", k + 1)
1800 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1801 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1802 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1808 def revert_moderncv_1(document):
1809 " Reverts the new inset of moderncv to TeX-code in preamble "
1811 if document.textclass != "moderncv":
1817 # at first revert the new styles
1819 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1822 j = find_end_of_layout(document.body, i)
1824 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1827 content = lyx2latex(document, document.body[i:j + 1])
1828 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1829 del document.body[i:j + 1]
1831 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1834 j = find_end_of_layout(document.body, i)
1836 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1839 content = lyx2latex(document, document.body[i:j + 1])
1840 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1841 del document.body[i:j + 1]
1842 # now change the new styles to the obsolete ones
1844 i = find_token(document.body, "\\begin_layout Name", 0)
1847 j = find_end_of_layout(document.body, i)
1849 document.warning("Malformed LyX document: Can't find end of Name layout")
1852 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1853 if lineArg > j and j != 0:
1856 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1857 # we have to assure that no other inset is in the Argument
1858 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1859 endInset = find_token(document.body, "\\end_inset", beginPlain)
1862 while beginInset < endInset and beginInset != -1:
1863 beginInset = find_token(document.body, "\\begin_inset", k)
1864 endInset = find_token(document.body, "\\end_inset", l)
1867 Arg2 = document.body[l + 5 : l + 6]
1869 document.body[i : i + 1]= ["\\begin_layout FirstName"]
1870 # delete the Argument inset
1871 del( document.body[endInset - 2 : endInset + 3])
1872 del( document.body[lineArg : beginPlain + 1])
1873 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1876 def revert_moderncv_2(document):
1877 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1879 if document.textclass != "moderncv":
1886 i = find_token(document.body, "\\begin_layout Phone", i)
1889 j = find_end_of_layout(document.body, i)
1891 document.warning("Malformed LyX document: Can't find end of Phone layout")
1894 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1895 if lineArg > j and j != 0:
1899 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1900 # we have to assure that no other inset is in the Argument
1901 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1902 endInset = find_token(document.body, "\\end_inset", beginPlain)
1905 while beginInset < endInset and beginInset != -1:
1906 beginInset = find_token(document.body, "\\begin_inset", k)
1907 endInset = find_token(document.body, "\\end_inset", l)
1910 Arg = document.body[beginPlain + 1 : beginPlain + 2]
1912 if Arg[0] == "mobile":
1913 document.body[i : i + 1]= ["\\begin_layout Mobile"]
1915 document.body[i : i + 1]= ["\\begin_layout Fax"]
1916 # delete the Argument inset
1917 del(document.body[endInset - 2 : endInset + 1])
1918 del(document.body[lineArg : beginPlain + 3])
1922 def convert_moderncv_phone(document):
1923 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
1925 if document.textclass != "moderncv":
1932 "Mobile" : "mobile",
1936 rx = re.compile(r'^\\begin_layout (\S+)$')
1938 # substitute \fax and \mobile by \phone[fax] and \phone[mobile], respectively
1939 i = find_token(document.body, "\\begin_layout", i)
1943 m = rx.match(document.body[i])
1947 if val not in list(phone_dict.keys()):
1950 j = find_end_of_layout(document.body, i)
1952 document.warning("Malformed LyX document: Can't find end of Mobile layout")
1956 document.body[i : i + 1] = ["\\begin_layout Phone", "\\begin_inset Argument 1", "status open", "",
1957 "\\begin_layout Plain Layout", phone_dict[val], "\\end_layout", "",
1961 def convert_moderncv_name(document):
1962 " Convert the FirstName and LastName layout of moderncv to the general Name layout "
1964 if document.textclass != "moderncv":
1967 fnb = 0 # Begin of FirstName inset
1968 fne = 0 # End of FirstName inset
1969 lnb = 0 # Begin of LastName (FamilyName) inset
1970 lne = 0 # End of LastName (FamilyName) inset
1971 nb = 0 # Begin of substituting Name inset
1972 ne = 0 # End of substituting Name inset
1973 FirstName = [] # FirstName content
1974 FamilyName = [] # LastName content
1978 fnb = find_token(document.body, "\\begin_layout FirstName", fnb)
1980 fne = find_end_of_layout(document.body, fnb)
1982 document.warning("Malformed LyX document: Can't find end of FirstName layout")
1984 FirstName = document.body[fnb + 1 : fne]
1986 lnb = find_token(document.body, "\\begin_layout FamilyName", lnb)
1988 lne = find_end_of_layout(document.body, lnb)
1990 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
1992 FamilyName = document.body[lnb + 1 : lne]
1993 # Determine the region for the substituting Name layout
1994 if fnb == -1 and lnb == -1: # Neither FirstName nor FamilyName exists -> Do nothing
1996 elif fnb == -1: # Only FamilyName exists -> New Name insets replaces that
1999 elif lnb == -1: # Only FirstName exists -> New Name insets replaces that
2002 elif fne > lne: # FirstName position before FamilyName -> New Name insets spans
2003 nb = lnb # from FamilyName begin
2004 ne = fne # to FirstName end
2005 else: # FirstName position before FamilyName -> New Name insets spans
2006 nb = fnb # from FirstName begin
2007 ne = lne # to FamilyName end
2009 # Insert the substituting layout now. If FirstName exists, use an otpional argument.
2011 document.body[nb : ne + 1] = ["\\begin_layout Name"] + FamilyName + ["\\end_layout", ""]
2013 document.body[nb : ne + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2014 "\\begin_layout Plain Layout"] + FirstName + ["\\end_layout", "",
2015 "\\end_inset", ""] + FamilyName + ["\\end_layout", ""]
2018 def revert_achemso(document):
2019 " Reverts the flex inset Latin to TeX code "
2021 if document.textclass != "achemso":
2026 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2028 j = find_end_of_inset(document.body, i)
2032 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2033 endPlain = find_end_of_layout(document.body, beginPlain)
2034 content = lyx2latex(document, document.body[beginPlain : endPlain])
2035 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2037 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2042 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2043 "\\font_sf_scale", "\\font_tt_scale"]
2044 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2045 fontquotes = [True, True, True, True, False, False]
2047 def convert_fontsettings(document):
2048 " Duplicate font settings "
2050 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2052 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2053 use_non_tex_fonts = "false"
2055 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2057 for f in fontsettings:
2058 i = find_token(document.header, f + " ", 0)
2060 document.warning("Malformed LyX document: No " + f + "!")
2062 # note that with i = -1, this will insert at the end
2064 value = fontdefaults[j]
2066 value = document.header[i][len(f):].strip()
2068 if use_non_tex_fonts == "true":
2069 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2071 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2073 if use_non_tex_fonts == "true":
2074 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2076 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2080 def revert_fontsettings(document):
2081 " Merge font settings "
2083 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2085 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2086 use_non_tex_fonts = "false"
2088 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2090 for f in fontsettings:
2091 i = find_token(document.header, f + " ", 0)
2093 document.warning("Malformed LyX document: No " + f + "!")
2096 line = get_value(document.header, f, i)
2099 q2 = line.find('"', q1+1)
2100 q3 = line.find('"', q2+1)
2101 q4 = line.find('"', q3+1)
2102 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2103 document.warning("Malformed LyX document: Missing quotes!")
2106 if use_non_tex_fonts == "true":
2107 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2109 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2111 if use_non_tex_fonts == "true":
2112 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2114 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2118 def revert_solution(document):
2119 " Reverts the solution environment of the theorem module to TeX code "
2121 # Do we use one of the modules that provides Solution?
2123 mods = document.get_module_list()
2125 if mod == "theorems-std" or mod == "theorems-bytype" \
2126 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2136 i = find_token(document.body, "\\begin_layout Solution", i)
2140 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2141 if is_starred == True:
2143 LyXName = "Solution*"
2144 theoremName = "newtheorem*"
2147 LyXName = "Solution"
2148 theoremName = "newtheorem"
2150 j = find_end_of_layout(document.body, i)
2152 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2156 # if this is not a consecutive env, add start command
2159 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2161 # has this a consecutive theorem of same type?
2162 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2164 # if this is not followed by a consecutive env, add end command
2166 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2168 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2170 add_to_preamble(document, "\\theoremstyle{definition}")
2171 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2172 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2173 (theoremName, LaTeXName))
2174 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2175 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2176 (theoremName, LaTeXName))
2178 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2182 def revert_verbatim_star(document):
2183 from lyx_2_1 import revert_verbatim
2184 revert_verbatim(document, True)
2191 supported_versions = ["2.2.0", "2.2"]
2193 [475, [convert_separator]],
2194 # nothing to do for 476: We consider it a bug that older versions
2195 # did not load amsmath automatically for these commands, and do not
2196 # want to hardcode amsmath off.
2202 [481, [convert_dashes]],
2203 [482, [convert_phrases]],
2204 [483, [convert_specialchar]],
2209 [488, [convert_newgloss]],
2210 [489, [convert_BoxFeatures]],
2211 [490, [convert_origin]],
2213 [492, [convert_colorbox]],
2216 [495, [convert_subref]],
2217 [496, [convert_nounzip]],
2218 [497, [convert_external_bbox]],
2220 [499, [convert_moderncv_phone, convert_moderncv_name]],
2222 [501, [convert_fontsettings]],
2228 [502, [revert_verbatim_star]],
2229 [501, [revert_solution]],
2230 [500, [revert_fontsettings]],
2231 [499, [revert_achemso]],
2232 [498, [revert_moderncv_1, revert_moderncv_2]],
2233 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2234 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2235 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2236 [496, [revert_external_bbox]],
2237 [495, []], # nothing to do since the noUnzip parameter was optional
2238 [494, [revert_subref]],
2239 [493, [revert_jss]],
2240 [492, [revert_mathmulticol]],
2241 [491, [revert_colorbox]],
2242 [490, [revert_textcolor]],
2243 [489, [revert_origin]],
2244 [488, [revert_BoxFeatures]],
2245 [487, [revert_newgloss, revert_glossgroup]],
2246 [486, [revert_forest]],
2247 [485, [revert_ex_itemargs]],
2248 [484, [revert_sigplan_doi]],
2249 [483, [revert_georgian]],
2250 [482, [revert_specialchar]],
2251 [481, [revert_phrases]],
2252 [480, [revert_dashes]],
2253 [479, [revert_question_env]],
2254 [478, [revert_beamer_lemma]],
2255 [477, [revert_xarrow]],
2256 [476, [revert_swissgerman]],
2257 [475, [revert_smash]],
2258 [474, [revert_separator]]
2262 if __name__ == "__main__":