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?
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] = ["\\begin_inset ERT", "status collapsed", "",
846 "\\begin_layout Plain Layout", "", "\\backslash",
847 "begin{forest}", "\\end_layout", "", "\\begin_layout Plain Layout",
848 content, "\\end_layout", "", "\\begin_layout Plain Layout",
849 "\\backslash", "end{forest}", "", "\\end_layout", "", "\\end_inset"]
853 def revert_glossgroup(document):
854 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
856 if not "linguistics" in document.get_module_list():
861 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
864 j = find_end_of_inset(document.body, i)
866 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
870 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
871 endPlain = find_end_of_layout(document.body, beginPlain)
872 content = lyx2latex(document, document.body[beginPlain : endPlain])
874 document.body[i:j + 1] = ["{", "", content, "", "}"]
878 def revert_newgloss(document):
879 " Reverts the new Glosse insets (Linguistics module) to the old format "
881 if not "linguistics" in document.get_module_list():
884 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
885 for glosse in glosses:
888 i = find_token(document.body, glosse, i)
891 j = find_end_of_inset(document.body, i)
893 document.warning("Malformed LyX document: Can't find end of Glosse inset")
897 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
898 endarg = find_end_of_inset(document.body, arg)
901 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
902 if argbeginPlain == -1:
903 document.warning("Malformed LyX document: Can't find arg plain Layout")
906 argendPlain = find_end_of_inset(document.body, argbeginPlain)
907 argcontent = lyx2latex(document, document.body[argbeginPlain : argendPlain - 2])
909 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
910 argcontent, "\\end_layout"]
912 # remove Arg insets and paragraph, if it only contains this inset
913 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
914 del document.body[arg - 1 : endarg + 4]
916 del document.body[arg : endarg + 1]
918 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
919 endPlain = find_end_of_layout(document.body, beginPlain)
920 content = lyx2latex(document, document.body[beginPlain : endPlain])
922 document.body[beginPlain + 1:endPlain] = [content]
926 def convert_newgloss(document):
927 " Converts Glosse insets (Linguistics module) to the new format "
929 if not "linguistics" in document.get_module_list():
932 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
933 for glosse in glosses:
936 i = find_token(document.body, glosse, i)
939 j = find_end_of_inset(document.body, i)
941 document.warning("Malformed LyX document: Can't find end of Glosse inset")
948 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
951 endPlain = find_end_of_layout(document.body, beginPlain)
953 document.warning("Malformed LyX document: Can't find end of Glosse layout")
957 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
958 if glt != -1 and document.body[glt + 1].startswith("glt"):
959 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
960 argcontent = document.body[glt + 1 : endPlain]
961 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
962 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
963 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
964 "\\end_layout", "", "\\end_inset"]
966 content = document.body[beginPlain + 1 : endPlain]
967 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
968 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
970 endPlain = find_end_of_layout(document.body, beginPlain)
972 j = find_end_of_inset(document.body, i)
977 def convert_BoxFeatures(document):
978 " adds new box features "
982 i = find_token(document.body, "height_special", i)
985 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
989 def revert_BoxFeatures(document):
990 " outputs new box features as TeX code "
994 defaultThick = "0.4pt"
995 defaultShadow = "4pt"
997 i = find_token(document.body, "height_special", i)
1000 # read out the values
1001 beg = document.body[i+1].find('"');
1002 end = document.body[i+1].rfind('"');
1003 thickness = document.body[i+1][beg+1:end];
1004 beg = document.body[i+2].find('"');
1005 end = document.body[i+2].rfind('"');
1006 separation = document.body[i+2][beg+1:end];
1007 beg = document.body[i+3].find('"');
1008 end = document.body[i+3].rfind('"');
1009 shadowsize = document.body[i+3][beg+1:end];
1010 # delete the specification
1011 del document.body[i+1:i+4]
1013 # first output the closing brace
1014 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1015 document.body[i + 10 : i + 10] = put_cmd_in_ert("}")
1016 # now output the lengths
1017 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1018 document.body[i - 10 : i - 10] = put_cmd_in_ert("{")
1019 if thickness != defaultThick:
1020 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness]
1021 if separation != defaultSep and thickness == defaultThick:
1022 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation]
1023 if separation != defaultSep and thickness != defaultThick:
1024 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1025 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1026 document.body[i - 5 : i - 4] = ["{\\backslash shadowsize " + shadowsize]
1027 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1028 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1029 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1030 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1031 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1032 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1036 def convert_origin(document):
1037 " Insert the origin tag "
1039 i = find_token(document.header, "\\textclass ", 0)
1041 document.warning("Malformed LyX document: No \\textclass!!")
1043 if document.dir == "":
1047 if document.systemlyxdir and document.systemlyxdir != '':
1049 if os.path.isabs(document.dir):
1050 absdir = os.path.normpath(document.dir)
1052 absdir = os.path.normpath(os.path.abspath(document.dir))
1053 if os.path.isabs(document.systemlyxdir):
1054 abssys = os.path.normpath(document.systemlyxdir)
1056 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1057 relpath = os.path.relpath(absdir, abssys)
1058 if relpath.find('..') == 0:
1063 origin = document.dir.replace('\\', '/') + '/'
1065 origin = os.path.join("/systemlyxdir", relpath).replace('\\', '/') + '/'
1067 origin = unicode(origin, sys.getfilesystemencoding())
1068 document.header[i:i] = ["\\origin " + origin]
1071 def revert_origin(document):
1072 " Remove the origin tag "
1074 i = find_token(document.header, "\\origin ", 0)
1076 document.warning("Malformed LyX document: No \\origin!!")
1078 del document.header[i]
1081 color_names = ["brown", "darkgray", "gray", \
1082 "lightgray", "lime", "olive", "orange", \
1083 "pink", "purple", "teal", "violet"]
1085 def revert_textcolor(document):
1086 " revert new \\textcolor colors to TeX code "
1092 i = find_token(document.body, "\\color ", i)
1096 for color in list(color_names):
1097 if document.body[i] == "\\color " + color:
1098 # register that xcolor must be loaded in the preamble
1101 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\usepackage{xcolor}}{}"])
1102 # find the next \\color and/or the next \\end_layout
1103 j = find_token(document.body, "\\color", i + 1)
1104 k = find_token(document.body, "\\end_layout", i + 1)
1105 if j == -1 and k != -1:
1108 # first output the closing brace
1110 document.body[k: k] = put_cmd_in_ert("}")
1112 document.body[j: j] = put_cmd_in_ert("}")
1113 # now output the \textcolor command
1114 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1118 def convert_colorbox(document):
1119 " adds color settings for boxes "
1123 i = find_token(document.body, "shadowsize", i)
1126 document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1130 def revert_colorbox(document):
1131 " outputs color settings for boxes as TeX code "
1134 defaultframecolor = "black"
1135 defaultbackcolor = "none"
1137 binset = find_token(document.body, "\\begin_inset Box", binset)
1141 einset = find_end_of_inset(document.body, binset)
1143 document.warning("Malformed LyX document: Can't find end of box inset!")
1147 blay = find_token(document.body, "\\begin_layout", binset, einset)
1149 document.warning("Malformed LyX document: Can't find start of layout!")
1153 # doing it this way, we make sure only to find a framecolor option
1154 frame = find_token(document.body, "framecolor", binset, blay)
1159 beg = document.body[frame].find('"')
1160 end = document.body[frame].rfind('"')
1161 framecolor = document.body[frame][beg + 1 : end]
1163 # this should be on the next line
1165 beg = document.body[bgcolor].find('"')
1166 end = document.body[bgcolor].rfind('"')
1167 backcolor = document.body[bgcolor][beg + 1 : end]
1170 del document.body[frame : frame + 2]
1171 # adjust end of inset
1174 if document.body[binset] == "\\begin_inset Box Boxed" and \
1175 framecolor != defaultframecolor:
1176 document.body[binset] = "\\begin_inset Box Frameless"
1179 # first output the closing brace
1180 if framecolor == defaultframecolor and backcolor == defaultbackcolor:
1184 # we also neeed to load xcolor in the preamble but only once
1185 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\usepackage{xcolor}}{}"])
1186 document.body[einset + 1 : einset + 1] = put_cmd_in_ert("}")
1187 if framecolor != defaultframecolor:
1188 document.body[binset:binset] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1190 document.body[binset:binset] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
1195 def revert_mathmulticol(document):
1196 " Convert formulas to ERT if they contain multicolumns "
1200 i = find_token(document.body, '\\begin_inset Formula', i)
1203 j = find_end_of_inset(document.body, i)
1205 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1208 lines = document.body[i:j]
1209 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1210 code = "\n".join(lines)
1215 n = code.find("\\multicolumn", k)
1216 # no need to convert degenerated multicolumn cells,
1217 # they work in old LyX versions as "math ERT"
1218 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1219 ert = put_cmd_in_ert(code)
1220 document.body[i:j+1] = ert
1226 i = find_end_of_inset(document.body, i)
1231 def revert_jss(document):
1232 " Reverts JSS In_Preamble commands to ERT in preamble "
1234 if document.textclass != "jss":
1243 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1244 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1247 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1249 endh = find_end_of_inset(document.body, h)
1250 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1251 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1255 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1257 endm = find_end_of_inset(document.body, m)
1258 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1259 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1263 j = find_token(document.body, "\\begin_inset Flex Code", j)
1265 # assure that we are not in a Code Chunk inset
1266 if document.body[j][-1] == "e":
1267 endj = find_end_of_inset(document.body, j)
1268 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1269 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1275 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1277 endk = find_end_of_inset(document.body, k)
1278 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1279 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1283 n = find_token(document.body, "\\begin_inset Flex URL", n)
1285 endn = find_end_of_inset(document.body, n)
1286 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1287 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1289 # now revert the In_Preamble layouts
1291 i = find_token(document.body, "\\begin_layout Title", 0)
1294 j = find_end_of_layout(document.body, i)
1296 document.warning("Malformed LyX document: Can't find end of Title layout")
1299 content = lyx2latex(document, document.body[i:j + 1])
1300 add_to_preamble(document, ["\\title{" + content + "}"])
1301 del document.body[i:j + 1]
1303 i = find_token(document.body, "\\begin_layout Author", 0)
1306 j = find_end_of_layout(document.body, i)
1308 document.warning("Malformed LyX document: Can't find end of Author layout")
1311 content = lyx2latex(document, document.body[i:j + 1])
1312 add_to_preamble(document, ["\\author{" + content + "}"])
1313 del document.body[i:j + 1]
1315 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1318 j = find_end_of_layout(document.body, i)
1320 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1323 content = lyx2latex(document, document.body[i:j + 1])
1324 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1325 del document.body[i:j + 1]
1327 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1330 j = find_end_of_layout(document.body, i)
1332 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1335 content = lyx2latex(document, document.body[i:j + 1])
1336 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1337 del document.body[i:j + 1]
1339 i = find_token(document.body, "\\begin_layout Short Title", 0)
1342 j = find_end_of_layout(document.body, i)
1344 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1347 content = lyx2latex(document, document.body[i:j + 1])
1348 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1349 del document.body[i:j + 1]
1351 i = find_token(document.body, "\\begin_layout Abstract", 0)
1354 j = find_end_of_layout(document.body, i)
1356 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1359 content = lyx2latex(document, document.body[i:j + 1])
1360 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1361 del document.body[i:j + 1]
1363 i = find_token(document.body, "\\begin_layout Keywords", 0)
1366 j = find_end_of_layout(document.body, i)
1368 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1371 content = lyx2latex(document, document.body[i:j + 1])
1372 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1373 del document.body[i:j + 1]
1375 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1378 j = find_end_of_layout(document.body, i)
1380 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1383 content = lyx2latex(document, document.body[i:j + 1])
1384 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1385 del document.body[i:j + 1]
1387 i = find_token(document.body, "\\begin_layout Address", 0)
1390 j = find_end_of_layout(document.body, i)
1392 document.warning("Malformed LyX document: Can't find end of Address layout")
1395 content = lyx2latex(document, document.body[i:j + 1])
1396 add_to_preamble(document, ["\\Address{" + content + "}"])
1397 del document.body[i:j + 1]
1398 # finally handle the code layouts
1403 while m != -1 or j != -1 or h != -1 or k != -1:
1406 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1408 endh = find_end_of_inset(document.body, h)
1409 document.body[endh + 1 : endh] = ["\\end_layout"]
1410 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1411 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1412 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1416 j = find_token(document.body, "\\begin_layout Code Input", j)
1418 endj = find_end_of_layout(document.body, j)
1419 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1420 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1421 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1422 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1423 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1427 k = find_token(document.body, "\\begin_layout Code Output", k)
1429 endk = find_end_of_layout(document.body, k)
1430 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1431 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1432 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1433 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1434 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1438 m = find_token(document.body, "\\begin_layout Code", m)
1440 endm = find_end_of_layout(document.body, m)
1441 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1442 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1443 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1444 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1445 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1449 def convert_subref(document):
1450 " converts sub: ref prefixes to subref: "
1453 rx = re.compile(r'^name \"sub:(.+)$')
1456 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1459 j = find_end_of_inset(document.body, i)
1461 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1465 for p in range(i, j):
1466 m = rx.match(document.body[p])
1469 document.body[p] = "name \"subsec:" + label
1473 rx = re.compile(r'^reference \"sub:(.+)$')
1476 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1479 j = find_end_of_inset(document.body, i)
1481 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1485 for p in range(i, j):
1486 m = rx.match(document.body[p])
1489 document.body[p] = "reference \"subsec:" + label
1495 def revert_subref(document):
1496 " reverts subref: ref prefixes to sub: "
1499 rx = re.compile(r'^name \"subsec:(.+)$')
1502 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1505 j = find_end_of_inset(document.body, i)
1507 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1511 for p in range(i, j):
1512 m = rx.match(document.body[p])
1515 document.body[p] = "name \"sub:" + label
1520 rx = re.compile(r'^reference \"subsec:(.+)$')
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 \"sub:" + label
1541 def convert_nounzip(document):
1542 " remove the noUnzip parameter of graphics insets "
1544 rx = re.compile(r'\s*noUnzip\s*$')
1547 i = find_token(document.body, "\\begin_inset Graphics", i)
1550 j = find_end_of_inset(document.body, i)
1552 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1556 k = find_re(document.body, rx, i, j)
1558 del document.body[k]
1563 def convert_revert_external_bbox(document, forward):
1564 " add units to bounding box of external insets "
1566 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1569 i = find_token(document.body, "\\begin_inset External", i)
1572 j = find_end_of_inset(document.body, i)
1574 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1577 k = find_re(document.body, rx, i, j)
1581 tokens = document.body[k].split()
1583 for t in range(1, 5):
1586 for t in range(1, 5):
1587 tokens[t] = length_in_bp(tokens[t])
1588 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1589 tokens[3] + " " + tokens[4]
1593 def convert_external_bbox(document):
1594 convert_revert_external_bbox(document, True)
1597 def revert_external_bbox(document):
1598 convert_revert_external_bbox(document, False)
1601 def revert_tcolorbox_1(document):
1602 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1605 i = find_token(document.header, "tcolorbox", i)
1611 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1614 flexEnd = find_end_of_inset(document.body, flex)
1615 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1616 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1617 flexEnd = find_end_of_inset(document.body, flex)
1619 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1621 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1622 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1626 def revert_tcolorbox_2(document):
1627 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1630 i = find_token(document.header, "tcolorbox", i)
1636 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1639 flexEnd = find_end_of_inset(document.body, flex)
1640 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1641 flexEnd = find_end_of_inset(document.body, flex)
1642 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1643 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1647 def revert_tcolorbox_3(document):
1648 " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1651 i = find_token(document.header, "tcolorbox", i)
1657 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1660 flexEnd = find_end_of_inset(document.body, flex)
1661 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1662 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1663 flexEnd = find_end_of_inset(document.body, flex)
1664 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1665 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1669 def revert_tcolorbox_4(document):
1670 " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1673 i = find_token(document.header, "tcolorbox", i)
1679 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1682 flexEnd = find_end_of_inset(document.body, flex)
1683 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1684 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1685 flexEnd = find_end_of_inset(document.body, flex)
1686 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1687 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1691 def revert_tcolorbox_5(document):
1692 " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1695 i = find_token(document.header, "tcolorbox", i)
1701 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1704 flexEnd = find_end_of_inset(document.body, flex)
1705 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1706 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1707 flexEnd = find_end_of_inset(document.body, flex)
1708 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1709 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1713 def revert_tcolorbox_6(document):
1714 " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1717 i = find_token(document.header, "tcolorbox", i)
1723 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1726 flexEnd = find_end_of_inset(document.body, flex)
1727 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1728 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1729 flexEnd = find_end_of_inset(document.body, flex)
1730 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1731 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1735 def revert_tcolorbox_7(document):
1736 " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1739 i = find_token(document.header, "tcolorbox", i)
1745 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1748 flexEnd = find_end_of_inset(document.body, flex)
1749 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1750 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1751 flexEnd = find_end_of_inset(document.body, flex)
1752 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1753 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1757 def revert_tcolorbox_8(document):
1758 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1764 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1766 j = find_end_of_layout(document.body, i)
1767 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1768 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1769 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1770 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1772 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1774 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1775 k = find_end_of_inset(document.body, j)
1776 k = find_token(document.body, "\\end_inset", k + 1)
1777 k = find_token(document.body, "\\end_inset", k + 1)
1779 k = find_token(document.body, "\\end_inset", k + 1)
1780 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1781 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1782 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1788 def revert_moderncv_1(document):
1789 " Reverts the new inset of moderncv to TeX-code in preamble "
1791 if document.textclass != "moderncv":
1797 # at first revert the new styles
1799 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1802 j = find_end_of_layout(document.body, i)
1804 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1807 content = lyx2latex(document, document.body[i:j + 1])
1808 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1809 del document.body[i:j + 1]
1811 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1814 j = find_end_of_layout(document.body, i)
1816 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1819 content = lyx2latex(document, document.body[i:j + 1])
1820 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1821 del document.body[i:j + 1]
1822 # now change the new styles to the obsolete ones
1824 i = find_token(document.body, "\\begin_layout Name", 0)
1827 j = find_end_of_layout(document.body, i)
1829 document.warning("Malformed LyX document: Can't find end of Name layout")
1832 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1833 if lineArg > j and j != 0:
1836 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1837 # we have to assure that no other inset is in the Argument
1838 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1839 endInset = find_token(document.body, "\\end_inset", beginPlain)
1842 while beginInset < endInset and beginInset != -1:
1843 beginInset = find_token(document.body, "\\begin_inset", k)
1844 endInset = find_token(document.body, "\\end_inset", l)
1847 Arg2 = document.body[l + 5 : l + 6]
1849 document.body[i : i + 1]= ["\\begin_layout FirstName"]
1850 # delete the Argument inset
1851 del( document.body[endInset - 2 : endInset + 3])
1852 del( document.body[lineArg : beginPlain + 1])
1853 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1856 def revert_moderncv_2(document):
1857 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1859 if document.textclass != "moderncv":
1866 i = find_token(document.body, "\\begin_layout Phone", i)
1869 j = find_end_of_layout(document.body, i)
1871 document.warning("Malformed LyX document: Can't find end of Phone layout")
1874 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1875 if lineArg > j and j != 0:
1879 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1880 # we have to assure that no other inset is in the Argument
1881 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1882 endInset = find_token(document.body, "\\end_inset", beginPlain)
1885 while beginInset < endInset and beginInset != -1:
1886 beginInset = find_token(document.body, "\\begin_inset", k)
1887 endInset = find_token(document.body, "\\end_inset", l)
1890 Arg = document.body[beginPlain + 1 : beginPlain + 2]
1892 if Arg[0] == "mobile":
1893 document.body[i : i + 1]= ["\\begin_layout Mobile"]
1895 document.body[i : i + 1]= ["\\begin_layout Fax"]
1896 # delete the Argument inset
1897 del(document.body[endInset - 2 : endInset + 1])
1898 del(document.body[lineArg : beginPlain + 3])
1902 def convert_moderncv(document):
1903 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
1905 if document.textclass != "moderncv":
1912 i = find_token(document.body, "\\begin_layout Mobile", i)
1915 j = find_end_of_layout(document.body, i)
1917 document.warning("Malformed LyX document: Can't find end of Mobile layout")
1920 document.body[i + 1 : i + 1] = ["\\begin_inset Argument 1", "status open", "",
1921 "\\begin_layout Plain Layout", "mobile", "\\end_layout", "",
1924 i = find_token(document.body, "\\begin_layout Fax", i)
1927 j = find_end_of_layout(document.body, i)
1929 document.warning("Malformed LyX document: Can't find end of Fax layout")
1932 document.body[i + 1 : i + 1] = ["\\begin_inset Argument 1", "status open", "",
1933 "\\begin_layout Plain Layout", "fax", "\\end_layout", "",
1935 # \firstname and \familyname
1936 i1 = find_token(document.body, "\\begin_layout FirstName", 0)
1939 j1 = find_end_of_layout(document.body, i1)
1941 document.warning("Malformed LyX document: Can't find end of FirstName layout")
1944 FirstName = document.body[i1 + 1 : i1 + 2]
1945 i2 = find_token(document.body, "\\begin_layout FamilyName", 0)
1948 j2 = find_end_of_layout(document.body, i2)
1950 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
1953 FamilyName = document.body[i2 + 1 : i2 + 2]
1960 document.body[k + 1 : k + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
1961 "\\begin_layout Plain Layout", FirstName[0], "\\end_layout", "",
1962 "\\end_inset", "", FamilyName[0], "\\end_layout", ""]
1963 #document.body[i2 + 1 : i2 + 1] = ["hellok: ", str(k)]
1964 del(document.body[l : k])
1970 def revert_achemso(document):
1971 " Reverts the flex inset Latin to TeX code "
1973 if document.textclass != "achemso":
1978 i = find_token(document.body, "\\begin_inset Flex Latin", i)
1980 j = find_end_of_inset(document.body, i)
1984 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1985 endPlain = find_end_of_layout(document.body, beginPlain)
1986 content = lyx2latex(document, document.body[beginPlain : endPlain])
1987 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
1989 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
1994 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
1995 "\\font_sf_scale", "\\font_tt_scale"]
1996 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
1997 fontquotes = [True, True, True, True, False, False]
1999 def convert_fontsettings(document):
2000 " Duplicate font settings "
2002 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2004 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2005 use_non_tex_fonts = "false"
2007 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2009 for f in fontsettings:
2010 i = find_token(document.header, f + " ", 0)
2012 document.warning("Malformed LyX document: No " + f + "!")
2014 # note that with i = -1, this will insert at the end
2016 value = fontdefaults[j]
2018 value = document.header[i][len(f):].strip()
2020 if use_non_tex_fonts == "true":
2021 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2023 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2025 if use_non_tex_fonts == "true":
2026 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2028 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2032 def revert_fontsettings(document):
2033 " Merge font settings "
2035 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2037 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2038 use_non_tex_fonts = "false"
2040 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2042 for f in fontsettings:
2043 i = find_token(document.header, f + " ", 0)
2045 document.warning("Malformed LyX document: No " + f + "!")
2048 line = get_value(document.header, f, i)
2051 q2 = line.find('"', q1+1)
2052 q3 = line.find('"', q2+1)
2053 q4 = line.find('"', q3+1)
2054 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2055 document.warning("Malformed LyX document: Missing quotes!")
2058 if use_non_tex_fonts == "true":
2059 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2061 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2063 if use_non_tex_fonts == "true":
2064 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2066 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2070 def revert_solution(document):
2071 " Reverts the solution environment of the theorem module to TeX code "
2073 # Do we use one of the modules that provides Solution?
2075 mods = document.get_module_list()
2077 if mod == "theorems-std" or mod == "theorems-bytype" \
2078 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2088 i = find_token(document.body, "\\begin_layout Solution", i)
2092 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2093 if is_starred == True:
2095 LyXName = "Solution*"
2096 theoremName = "newtheorem*"
2099 LyXName = "Solution"
2100 theoremName = "newtheorem"
2102 j = find_end_of_layout(document.body, i)
2104 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2108 # if this is not a consecutive env, add start command
2111 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2113 # has this a consecutive theorem of same type?
2114 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2116 # if this is not followed by a consecutive env, add end command
2118 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2120 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2122 add_to_preamble(document, "\\theoremstyle{definition}")
2123 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2124 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2125 (theoremName, LaTeXName))
2126 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2127 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2128 (theoremName, LaTeXName))
2130 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2134 def revert_verbatim_star(document):
2135 from lyx_2_1 import revert_verbatim
2136 revert_verbatim(document, True)
2143 supported_versions = ["2.2.0", "2.2"]
2145 [475, [convert_separator]],
2146 # nothing to do for 476: We consider it a bug that older versions
2147 # did not load amsmath automatically for these commands, and do not
2148 # want to hardcode amsmath off.
2154 [481, [convert_dashes]],
2155 [482, [convert_phrases]],
2156 [483, [convert_specialchar]],
2161 [488, [convert_newgloss]],
2162 [489, [convert_BoxFeatures]],
2163 [490, [convert_origin]],
2165 [492, [convert_colorbox]],
2168 [495, [convert_subref]],
2169 [496, [convert_nounzip]],
2170 [497, [convert_external_bbox]],
2172 [499, [convert_moderncv]],
2174 [501, [convert_fontsettings]],
2180 [502, [revert_verbatim_star]],
2181 [501, [revert_solution]],
2182 [500, [revert_fontsettings]],
2183 [499, [revert_achemso]],
2184 [498, [revert_moderncv_1, revert_moderncv_2]],
2185 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2186 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2187 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2188 [496, [revert_external_bbox]],
2189 [495, []], # nothing to do since the noUnzip parameter was optional
2190 [494, [revert_subref]],
2191 [493, [revert_jss]],
2192 [492, [revert_mathmulticol]],
2193 [491, [revert_colorbox]],
2194 [490, [revert_textcolor]],
2195 [489, [revert_origin]],
2196 [488, [revert_BoxFeatures]],
2197 [487, [revert_newgloss, revert_glossgroup]],
2198 [486, [revert_forest]],
2199 [485, [revert_ex_itemargs]],
2200 [484, [revert_sigplan_doi]],
2201 [483, [revert_georgian]],
2202 [482, [revert_specialchar]],
2203 [481, [revert_phrases]],
2204 [480, [revert_dashes]],
2205 [479, [revert_question_env]],
2206 [478, [revert_beamer_lemma]],
2207 [477, [revert_xarrow]],
2208 [476, [revert_swissgerman]],
2209 [475, [revert_smash]],
2210 [474, [revert_separator]]
2214 if __name__ == "__main__":