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, convert_info_insets
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_longtable_label_internal(document, forward):
109 Convert reference to "LongTableNoNumber" into "Unnumbered" if forward is True
112 old_reference = "\\begin_inset Caption LongTableNoNumber"
113 new_reference = "\\begin_inset Caption Unnumbered"
115 # if the purpose is to revert swap the strings roles
117 old_reference, new_reference = new_reference, old_reference
121 i = find_token(document.body, old_reference, i)
126 document.body[i] = new_reference
129 def convert_longtable_label(document):
130 convert_longtable_label_internal(document, True)
133 def revert_longtable_label(document):
134 convert_longtable_label_internal(document, False)
137 def convert_separator(document):
139 Convert layout separators to separator insets and add (LaTeX) paragraph
140 breaks in order to mimic previous LaTeX export.
143 parins = ["\\begin_inset Separator parbreak", "\\end_inset", ""]
144 parlay = ["\\begin_layout Standard", "\\begin_inset Separator parbreak",
145 "\\end_inset", "", "\\end_layout", ""]
147 "family" : "default",
148 "series" : "default",
157 i = find_token(document.body, "\\begin_deeper", i)
161 j = find_token_backwards(document.body, "\\end_layout", i-1)
163 # reset any text style before inserting the inset
164 lay = get_containing_layout(document.body, j-1)
166 content = "\n".join(document.body[lay[1]:lay[2]])
167 for val in list(sty_dict.keys()):
168 if content.find("\\%s" % val) != -1:
169 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
172 document.body[j:j] = parins
173 i = i + len(parins) + 1
179 i = find_token(document.body, "\\align", i)
183 lay = get_containing_layout(document.body, i)
184 if lay != False and lay[0] == "Plain Layout":
188 j = find_token_backwards(document.body, "\\end_layout", i-1)
190 lay = get_containing_layout(document.body, j-1)
191 if lay != False and lay[0] == "Standard" \
192 and find_token(document.body, "\\align", lay[1], lay[2]) == -1 \
193 and find_token(document.body, "\\begin_inset VSpace", lay[1], lay[2]) == -1:
194 # reset any text style before inserting the inset
195 content = "\n".join(document.body[lay[1]:lay[2]])
196 for val in list(sty_dict.keys()):
197 if content.find("\\%s" % val) != -1:
198 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
201 document.body[j:j] = parins
202 i = i + len(parins) + 1
208 regexp = re.compile(r'^\\begin_layout (?:(-*)|(\s*))(Separator|EndOfSlide)(?:(-*)|(\s*))$', re.IGNORECASE)
212 i = find_re(document.body, regexp, i)
216 j = find_end_of_layout(document.body, i)
218 document.warning("Malformed LyX document: Missing `\\end_layout'.")
221 lay = get_containing_layout(document.body, j-1)
223 lines = document.body[lay[3]:lay[2]]
227 document.body[i:j+1] = parlay
229 document.body[i+1:i+1] = lines
231 i = i + len(parlay) + len(lines) + 1
234 def revert_separator(document):
235 " Revert separator insets to layout separators "
237 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
238 if document.textclass in beamer_classes:
239 beglaysep = "\\begin_layout Separator"
241 beglaysep = "\\begin_layout --Separator--"
243 parsep = [beglaysep, "", "\\end_layout", ""]
244 comert = ["\\begin_inset ERT", "status collapsed", "",
245 "\\begin_layout Plain Layout", "%", "\\end_layout",
246 "", "\\end_inset", ""]
247 empert = ["\\begin_inset ERT", "status collapsed", "",
248 "\\begin_layout Plain Layout", " ", "\\end_layout",
249 "", "\\end_inset", ""]
253 i = find_token(document.body, "\\begin_inset Separator", i)
257 lay = get_containing_layout(document.body, i)
259 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
266 kind = get_value(document.body, "\\begin_inset Separator", i, i+1, "plain").split()[1]
267 before = document.body[beg+1:i]
268 something_before = len(before) > 0 and len("".join(before)) > 0
269 j = find_end_of_inset(document.body, i)
270 after = document.body[j+1:end]
271 something_after = len(after) > 0 and len("".join(after)) > 0
273 beg = beg + len(before) + 1
274 elif something_before:
275 document.body[i:i] = ["\\end_layout", ""]
283 document.body[beg:j+1] = empert
286 document.body[beg:j+1] = comert
290 if layoutname == "Standard":
291 if not something_before:
292 document.body[beg:j+1] = parsep
294 document.body[i:i] = ["", "\\begin_layout Standard"]
297 document.body[beg:j+1] = ["\\begin_layout Standard"]
300 document.body[beg:j+1] = ["\\begin_deeper"]
302 end = end + 1 - (j + 1 - beg)
303 if not something_before:
304 document.body[i:i] = parsep
306 end = end + len(parsep)
307 document.body[i:i] = ["\\begin_layout Standard"]
308 document.body[end+2:end+2] = ["", "\\end_deeper", ""]
311 next_par_is_aligned = False
312 k = find_nonempty_line(document.body, end+1)
313 if k != -1 and check_token(document.body[k], "\\begin_layout"):
314 lay = get_containing_layout(document.body, k)
315 next_par_is_aligned = lay != False and \
316 find_token(document.body, "\\align", lay[1], lay[2]) != -1
317 if k != -1 and not next_par_is_aligned \
318 and not check_token(document.body[k], "\\end_deeper") \
319 and not check_token(document.body[k], "\\begin_deeper"):
320 if layoutname == "Standard":
321 document.body[beg:j+1] = [beglaysep]
324 document.body[beg:j+1] = ["\\begin_deeper", beglaysep]
325 end = end + 2 - (j + 1 - beg)
326 document.body[end+1:end+1] = ["", "\\end_deeper", ""]
330 del document.body[i:end+1]
332 del document.body[i:end-1]
337 def revert_smash(document):
338 " Set amsmath to on if smash commands are used "
340 commands = ["smash[t]", "smash[b]", "notag"]
341 i = find_token(document.header, "\\use_package amsmath", 0)
343 document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
345 value = get_value(document.header, "\\use_package amsmath", i).split()[1]
347 # nothing to do if package is not auto but on or off
351 j = find_token(document.body, '\\begin_inset Formula', j)
354 k = find_end_of_inset(document.body, j)
356 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
359 code = "\n".join(document.body[j:k])
361 if code.find("\\%s" % c) != -1:
362 # set amsmath to on, since it is loaded by the newer format
363 document.header[i] = "\\use_package amsmath 2"
368 def revert_swissgerman(document):
369 " Set language german-ch-old to german "
371 if document.language == "german-ch-old":
372 document.language = "german"
373 i = find_token(document.header, "\\language", 0)
375 document.header[i] = "\\language german"
378 j = find_token(document.body, "\\lang german-ch-old", j)
381 document.body[j] = document.body[j].replace("\\lang german-ch-old", "\\lang german")
385 def revert_use_package(document, pkg, commands, oldauto, supported):
386 # oldauto defines how the version we are reverting to behaves:
387 # if it is true, the old version uses the package automatically.
388 # if it is false, the old version never uses the package.
389 # If "supported" is true, the target version also supports this
391 regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
392 p = find_re(document.header, regexp, 0)
393 value = "1" # default is auto
395 value = get_value(document.header, "\\use_package" , p).split()[1]
397 del document.header[p]
398 if value == "2" and not supported: # on
399 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
400 elif value == "1" and not oldauto: # auto
403 i = find_token(document.body, '\\begin_inset Formula', i)
406 j = find_end_of_inset(document.body, i)
408 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
411 code = "\n".join(document.body[i:j])
413 if code.find("\\%s" % c) != -1:
415 document.header[p] = "\\use_package " + pkg + " 2"
417 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
422 mathtools_commands = ["xhookrightarrow", "xhookleftarrow", "xRightarrow", \
423 "xrightharpoondown", "xrightharpoonup", "xrightleftharpoons", \
424 "xLeftarrow", "xleftharpoondown", "xleftharpoonup", \
425 "xleftrightarrow", "xLeftrightarrow", "xleftrightharpoons", \
428 def revert_xarrow(document):
429 "remove use_package mathtools"
430 revert_use_package(document, "mathtools", mathtools_commands, False, True)
433 def revert_beamer_lemma(document):
434 " Reverts beamer lemma layout to ERT "
436 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
437 if document.textclass not in beamer_classes:
443 i = find_token(document.body, "\\begin_layout Lemma", i)
446 j = find_end_of_layout(document.body, i)
448 document.warning("Malformed LyX document: Can't find end of Lemma layout")
451 arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j)
452 endarg1 = find_end_of_inset(document.body, arg1)
453 arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j)
454 endarg2 = find_end_of_inset(document.body, arg2)
458 beginPlain1 = find_token(document.body, "\\begin_layout Plain Layout", arg1, endarg1)
459 if beginPlain1 == -1:
460 document.warning("Malformed LyX document: Can't find arg1 plain Layout")
463 endPlain1 = find_end_of_inset(document.body, beginPlain1)
464 content1 = document.body[beginPlain1 + 1 : endPlain1 - 2]
465 subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">")
467 beginPlain2 = find_token(document.body, "\\begin_layout Plain Layout", arg2, endarg2)
468 if beginPlain2 == -1:
469 document.warning("Malformed LyX document: Can't find arg2 plain Layout")
472 endPlain2 = find_end_of_inset(document.body, beginPlain2)
473 content2 = document.body[beginPlain2 + 1 : endPlain2 - 2]
474 subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]")
478 del document.body[arg2 : endarg2 + 1]
480 del document.body[arg1 : endarg1 + 1]
482 del document.body[arg1 : endarg1 + 1]
484 del document.body[arg2 : endarg2 + 1]
486 # index of end layout has probably changed
487 j = find_end_of_layout(document.body, i)
489 document.warning("Malformed LyX document: Can't find end of Lemma layout")
495 # if this is not a consecutive env, add start command
497 begcmd = put_cmd_in_ert("\\begin{lemma}")
499 # has this a consecutive lemma?
500 consecutive = document.body[j + 2] == "\\begin_layout Lemma"
502 # if this is not followed by a consecutive env, add end command
504 document.body[j : j + 1] = put_cmd_in_ert("\\end{lemma}") + ["\\end_layout"]
506 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd + subst1 + subst2
512 def revert_question_env(document):
514 Reverts question and question* environments of
515 theorems-ams-extended-bytype module to ERT
518 # Do we use theorems-ams-extended-bytype module?
519 if not "theorems-ams-extended-bytype" in document.get_module_list():
525 i = find_token(document.body, "\\begin_layout Question", i)
529 starred = document.body[i] == "\\begin_layout Question*"
531 j = find_end_of_layout(document.body, i)
533 document.warning("Malformed LyX document: Can't find end of Question layout")
537 # if this is not a consecutive env, add start command
541 begcmd = put_cmd_in_ert("\\begin{question*}")
543 begcmd = put_cmd_in_ert("\\begin{question}")
545 # has this a consecutive theorem of same type?
548 consecutive = document.body[j + 2] == "\\begin_layout Question*"
550 consecutive = document.body[j + 2] == "\\begin_layout Question"
552 # if this is not followed by a consecutive env, add end command
555 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
557 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
559 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
561 add_to_preamble(document, "\\providecommand{\questionname}{Question}")
564 add_to_preamble(document, "\\theoremstyle{plain}\n" \
565 "\\newtheorem*{question*}{\\protect\\questionname}")
567 add_to_preamble(document, "\\theoremstyle{plain}\n" \
568 "\\newtheorem{question}{\\protect\\questionname}")
573 def convert_dashes(document):
574 "convert -- and --- to \\twohyphens and \\threehyphens"
576 if document.backend != "latex":
580 while i < len(document.body):
581 words = document.body[i].split()
582 if len(words) > 1 and words[0] == "\\begin_inset" and \
583 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
584 # must not replace anything in insets that store LaTeX contents in .lyx files
585 # (math and command insets withut overridden read() and write() methods
586 # filtering out IPA makes Text::readParToken() more simple
587 # skip ERT as well since it is not needed there
588 j = find_end_of_inset(document.body, i)
590 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
596 j = document.body[i].find("--")
599 front = document.body[i][:j]
600 back = document.body[i][j+2:]
601 # We can have an arbitrary number of consecutive hyphens.
602 # These must be split into the corresponding number of two and three hyphens
603 # We must match what LaTeX does: First try emdash, then endash, then single hyphen
604 if back.find("-") == 0:
607 document.body.insert(i+1, back)
608 document.body[i] = front + "\\threehyphens"
611 document.body.insert(i+1, back)
612 document.body[i] = front + "\\twohyphens"
616 def revert_dashes(document):
617 "convert \\twohyphens and \\threehyphens to -- and ---"
620 while i < len(document.body):
621 words = document.body[i].split()
622 if len(words) > 1 and words[0] == "\\begin_inset" and \
623 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
625 j = find_end_of_inset(document.body, i)
627 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
633 if document.body[i].find("\\twohyphens") >= 0:
634 document.body[i] = document.body[i].replace("\\twohyphens", "--")
636 if document.body[i].find("\\threehyphens") >= 0:
637 document.body[i] = document.body[i].replace("\\threehyphens", "---")
639 if replaced and i+1 < len(document.body) and \
640 (document.body[i+1].find("\\") != 0 or \
641 document.body[i+1].find("\\twohyphens") == 0 or
642 document.body[i+1].find("\\threehyphens") == 0) and \
643 len(document.body[i]) + len(document.body[i+1]) <= 80:
644 document.body[i] = document.body[i] + document.body[i+1]
645 document.body[i+1:i+2] = []
650 # order is important for the last three!
651 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
653 def is_part_of_converted_phrase(line, j, phrase):
654 "is phrase part of an already converted phrase?"
656 converted = "\\SpecialCharNoPassThru \\" + p
657 pos = j + len(phrase) - len(converted)
659 if line[pos:pos+len(converted)] == converted:
664 def convert_phrases(document):
665 "convert special phrases from plain text to \\SpecialCharNoPassThru"
667 if document.backend != "latex":
670 for phrase in phrases:
672 while i < len(document.body):
673 words = document.body[i].split()
674 if len(words) > 1 and words[0] == "\\begin_inset" and \
675 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
676 # must not replace anything in insets that store LaTeX contents in .lyx files
677 # (math and command insets withut overridden read() and write() methods
678 j = find_end_of_inset(document.body, i)
680 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
685 if document.body[i].find("\\") == 0:
688 j = document.body[i].find(phrase)
692 if not is_part_of_converted_phrase(document.body[i], j, phrase):
693 front = document.body[i][:j]
694 back = document.body[i][j+len(phrase):]
696 document.body.insert(i+1, back)
697 # We cannot use SpecialChar since we do not know whether we are outside passThru
698 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
702 def revert_phrases(document):
703 "convert special phrases to plain text"
706 while i < len(document.body):
707 words = document.body[i].split()
708 if len(words) > 1 and words[0] == "\\begin_inset" and \
709 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
710 # see convert_phrases
711 j = find_end_of_inset(document.body, i)
713 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
719 for phrase in phrases:
720 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
721 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
722 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
724 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
725 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
727 if replaced and i+1 < len(document.body) and \
728 (document.body[i+1].find("\\") != 0 or \
729 document.body[i+1].find("\\SpecialChar") == 0) and \
730 len(document.body[i]) + len(document.body[i+1]) <= 80:
731 document.body[i] = document.body[i] + document.body[i+1]
732 document.body[i+1:i+2] = []
737 def convert_specialchar_internal(document, forward):
738 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
739 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
740 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
741 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
742 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
743 "\\LaTeX":"LaTeX" # must be after LaTeX2e
747 while i < len(document.body):
748 words = document.body[i].split()
749 if len(words) > 1 and words[0] == "\\begin_inset" and \
750 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
751 # see convert_phrases
752 j = find_end_of_inset(document.body, i)
754 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
759 for key, value in specialchars.iteritems():
761 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
762 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
764 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
765 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
769 def convert_specialchar(document):
770 "convert special characters to new syntax"
771 convert_specialchar_internal(document, True)
774 def revert_specialchar(document):
775 "convert special characters to old syntax"
776 convert_specialchar_internal(document, False)
779 def revert_georgian(document):
780 "Set the document language to English but assure Georgian output"
782 if document.language == "georgian":
783 document.language = "english"
784 i = find_token(document.header, "\\language georgian", 0)
786 document.header[i] = "\\language english"
787 j = find_token(document.header, "\\language_package default", 0)
789 document.header[j] = "\\language_package babel"
790 k = find_token(document.header, "\\options", 0)
792 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
794 l = find_token(document.header, "\\use_default_options", 0)
795 document.header.insert(l + 1, "\\options georgian")
798 def revert_sigplan_doi(document):
799 " Reverts sigplanconf DOI layout to ERT "
801 if document.textclass != "sigplanconf":
806 i = find_token(document.body, "\\begin_layout DOI", i)
809 j = find_end_of_layout(document.body, i)
811 document.warning("Malformed LyX document: Can't find end of DOI layout")
815 content = lyx2latex(document, document.body[i:j + 1])
816 add_to_preamble(document, ["\\doi{" + content + "}"])
817 del document.body[i:j + 1]
821 def revert_ex_itemargs(document):
822 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
824 if not "linguistics" in document.get_module_list():
828 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
830 i = find_token(document.body, "\\begin_inset Argument item:", i)
833 j = find_end_of_inset(document.body, i)
834 # Find containing paragraph layout
835 parent = get_containing_layout(document.body, i)
837 document.warning("Malformed LyX document: Can't find parent paragraph layout")
841 layoutname = parent[0]
842 if layoutname in example_layouts:
843 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
844 endPlain = find_end_of_layout(document.body, beginPlain)
845 content = document.body[beginPlain + 1 : endPlain]
846 del document.body[i:j+1]
847 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
848 document.body[parbeg : parbeg] = subst
852 def revert_forest(document):
853 " Reverts the forest environment (Linguistics module) to TeX-code "
855 if not "linguistics" in document.get_module_list():
860 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
863 j = find_end_of_inset(document.body, i)
865 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
869 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
870 endPlain = find_end_of_layout(document.body, beginPlain)
871 content = lyx2latex(document, document.body[beginPlain : endPlain])
873 add_to_preamble(document, ["\\usepackage{forest}"])
875 document.body[i:j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
879 def revert_glossgroup(document):
880 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
882 if not "linguistics" in document.get_module_list():
887 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
890 j = find_end_of_inset(document.body, i)
892 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
896 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
897 endPlain = find_end_of_layout(document.body, beginPlain)
898 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
900 document.body[i:j + 1] = ["{", "", content, "", "}"]
904 def revert_newgloss(document):
905 " Reverts the new Glosse insets (Linguistics module) to the old format "
907 if not "linguistics" in document.get_module_list():
910 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
911 for glosse in glosses:
914 i = find_token(document.body, glosse, i)
917 j = find_end_of_inset(document.body, i)
919 document.warning("Malformed LyX document: Can't find end of Glosse inset")
923 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
924 endarg = find_end_of_inset(document.body, arg)
927 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
928 if argbeginPlain == -1:
929 document.warning("Malformed LyX document: Can't find arg plain Layout")
932 argendPlain = find_end_of_inset(document.body, argbeginPlain)
933 argcontent = lyx2verbatim(document, document.body[argbeginPlain : argendPlain - 2])
935 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
936 argcontent, "\\end_layout"]
938 # remove Arg insets and paragraph, if it only contains this inset
939 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
940 del document.body[arg - 1 : endarg + 4]
942 del document.body[arg : endarg + 1]
944 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
945 endPlain = find_end_of_layout(document.body, beginPlain)
946 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
948 document.body[beginPlain + 1:endPlain] = [content]
951 # Dissolve ERT insets
952 for glosse in glosses:
955 i = find_token(document.body, glosse, i)
958 j = find_end_of_inset(document.body, i)
960 document.warning("Malformed LyX document: Can't find end of Glosse inset")
964 ert = find_token(document.body, "\\begin_inset ERT", i, j)
967 ertend = find_end_of_inset(document.body, ert)
969 document.warning("Malformed LyX document: Can't find end of ERT inset")
972 ertcontent = get_ert(document.body, ert, True)
973 document.body[ert : ertend + 1] = [ertcontent]
977 def convert_newgloss(document):
978 " Converts Glosse insets (Linguistics module) to the new format "
980 if not "linguistics" in document.get_module_list():
983 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
984 for glosse in glosses:
987 i = find_token(document.body, glosse, i)
990 j = find_end_of_inset(document.body, i)
992 document.warning("Malformed LyX document: Can't find end of Glosse inset")
999 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
1000 if beginPlain == -1:
1002 endPlain = find_end_of_layout(document.body, beginPlain)
1004 document.warning("Malformed LyX document: Can't find end of Glosse layout")
1008 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
1009 if glt != -1 and document.body[glt + 1].startswith("glt"):
1010 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1011 argcontent = document.body[glt + 1 : endPlain]
1012 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
1013 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
1014 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
1015 "\\end_layout", "", "\\end_inset"]
1017 content = document.body[beginPlain + 1 : endPlain]
1018 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
1019 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
1021 endPlain = find_end_of_layout(document.body, beginPlain)
1023 j = find_end_of_inset(document.body, i)
1028 def convert_BoxFeatures(document):
1029 " adds new box features "
1033 i = find_token(document.body, "height_special", i)
1036 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1040 def revert_BoxFeatures(document):
1041 " outputs new box features as TeX code "
1045 defaultThick = "0.4pt"
1046 defaultShadow = "4pt"
1048 i = find_token(document.body, "height_special", i)
1051 # read out the values
1052 beg = document.body[i+1].find('"');
1053 end = document.body[i+1].rfind('"');
1054 thickness = document.body[i+1][beg+1:end];
1055 beg = document.body[i+2].find('"');
1056 end = document.body[i+2].rfind('"');
1057 separation = document.body[i+2][beg+1:end];
1058 beg = document.body[i+3].find('"');
1059 end = document.body[i+3].rfind('"');
1060 shadowsize = document.body[i+3][beg+1:end];
1061 # delete the specification
1062 del document.body[i+1:i+4]
1064 # first output the closing brace
1065 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1066 document.body[i + 10 : i + 10] = put_cmd_in_ert("}")
1067 # now output the lengths
1068 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1069 document.body[i - 10 : i - 10] = put_cmd_in_ert("{")
1070 if thickness != defaultThick:
1071 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness]
1072 if separation != defaultSep and thickness == defaultThick:
1073 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation]
1074 if separation != defaultSep and thickness != defaultThick:
1075 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1076 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1077 document.body[i - 5 : i - 4] = ["{\\backslash shadowsize " + shadowsize]
1078 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1079 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1080 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1081 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1082 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1083 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1087 def convert_origin(document):
1088 " Insert the origin tag "
1090 i = find_token(document.header, "\\textclass ", 0)
1092 document.warning("Malformed LyX document: No \\textclass!!")
1094 if document.dir == "":
1098 if document.systemlyxdir and document.systemlyxdir != '':
1100 if os.path.isabs(document.dir):
1101 absdir = os.path.normpath(document.dir)
1103 absdir = os.path.normpath(os.path.abspath(document.dir))
1104 if os.path.isabs(document.systemlyxdir):
1105 abssys = os.path.normpath(document.systemlyxdir)
1107 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1108 relpath = os.path.relpath(absdir, abssys)
1109 if relpath.find('..') == 0:
1114 origin = document.dir.replace('\\', '/') + '/'
1116 origin = os.path.join("/systemlyxdir", relpath).replace('\\', '/') + '/'
1118 origin = unicode(origin, sys.getfilesystemencoding())
1119 document.header[i:i] = ["\\origin " + origin]
1122 def revert_origin(document):
1123 " Remove the origin tag "
1125 i = find_token(document.header, "\\origin ", 0)
1127 document.warning("Malformed LyX document: No \\origin!!")
1129 del document.header[i]
1132 color_names = ["brown", "darkgray", "gray", \
1133 "lightgray", "lime", "olive", "orange", \
1134 "pink", "purple", "teal", "violet"]
1136 def revert_textcolor(document):
1137 " revert new \\textcolor colors to TeX code "
1143 i = find_token(document.body, "\\color ", i)
1147 for color in list(color_names):
1148 if document.body[i] == "\\color " + color:
1149 # register that xcolor must be loaded in the preamble
1152 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\usepackage{xcolor}}{}"])
1153 # find the next \\color and/or the next \\end_layout
1154 j = find_token(document.body, "\\color", i + 1)
1155 k = find_token(document.body, "\\end_layout", i + 1)
1156 if j == -1 and k != -1:
1159 # first output the closing brace
1161 document.body[k: k] = put_cmd_in_ert("}")
1163 document.body[j: j] = put_cmd_in_ert("}")
1164 # now output the \textcolor command
1165 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1169 def convert_colorbox(document):
1170 " adds color settings for boxes "
1174 i = find_token(document.body, "shadowsize", i)
1177 document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1181 def revert_colorbox(document):
1182 " outputs color settings for boxes as TeX code "
1185 defaultframecolor = "black"
1186 defaultbackcolor = "none"
1188 binset = find_token(document.body, "\\begin_inset Box", binset)
1192 einset = find_end_of_inset(document.body, binset)
1194 document.warning("Malformed LyX document: Can't find end of box inset!")
1198 blay = find_token(document.body, "\\begin_layout", binset, einset)
1200 document.warning("Malformed LyX document: Can't find start of layout!")
1204 # doing it this way, we make sure only to find a framecolor option
1205 frame = find_token(document.body, "framecolor", binset, blay)
1210 beg = document.body[frame].find('"')
1211 end = document.body[frame].rfind('"')
1212 framecolor = document.body[frame][beg + 1 : end]
1214 # this should be on the next line
1216 beg = document.body[bgcolor].find('"')
1217 end = document.body[bgcolor].rfind('"')
1218 backcolor = document.body[bgcolor][beg + 1 : end]
1221 del document.body[frame : frame + 2]
1222 # adjust end of inset
1225 if document.body[binset] == "\\begin_inset Box Boxed" and \
1226 framecolor != defaultframecolor:
1227 document.body[binset] = "\\begin_inset Box Frameless"
1230 # first output the closing brace
1231 if framecolor == defaultframecolor and backcolor == defaultbackcolor:
1235 # we also neeed to load xcolor in the preamble but only once
1236 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\usepackage{xcolor}}{}"])
1237 document.body[einset + 1 : einset + 1] = put_cmd_in_ert("}")
1238 if framecolor != defaultframecolor:
1239 document.body[binset:binset] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1241 document.body[binset:binset] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
1246 def revert_mathmulticol(document):
1247 " Convert formulas to ERT if they contain multicolumns "
1251 i = find_token(document.body, '\\begin_inset Formula', i)
1254 j = find_end_of_inset(document.body, i)
1256 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1259 lines = document.body[i:j]
1260 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1261 code = "\n".join(lines)
1266 n = code.find("\\multicolumn", k)
1267 # no need to convert degenerated multicolumn cells,
1268 # they work in old LyX versions as "math ERT"
1269 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1270 ert = put_cmd_in_ert(code)
1271 document.body[i:j+1] = ert
1277 i = find_end_of_inset(document.body, i)
1282 def revert_jss(document):
1283 " Reverts JSS In_Preamble commands to ERT in preamble "
1285 if document.textclass != "jss":
1294 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1295 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1298 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1300 endh = find_end_of_inset(document.body, h)
1301 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1302 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1306 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1308 endm = find_end_of_inset(document.body, m)
1309 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1310 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1314 j = find_token(document.body, "\\begin_inset Flex Code", j)
1316 # assure that we are not in a Code Chunk inset
1317 if document.body[j][-1] == "e":
1318 endj = find_end_of_inset(document.body, j)
1319 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1320 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1326 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1328 endk = find_end_of_inset(document.body, k)
1329 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1330 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1334 n = find_token(document.body, "\\begin_inset Flex URL", n)
1336 endn = find_end_of_inset(document.body, n)
1337 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1338 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1340 # now revert the In_Preamble layouts
1342 i = find_token(document.body, "\\begin_layout Title", 0)
1345 j = find_end_of_layout(document.body, i)
1347 document.warning("Malformed LyX document: Can't find end of Title layout")
1350 content = lyx2latex(document, document.body[i:j + 1])
1351 add_to_preamble(document, ["\\title{" + content + "}"])
1352 del document.body[i:j + 1]
1354 i = find_token(document.body, "\\begin_layout Author", 0)
1357 j = find_end_of_layout(document.body, i)
1359 document.warning("Malformed LyX document: Can't find end of Author layout")
1362 content = lyx2latex(document, document.body[i:j + 1])
1363 add_to_preamble(document, ["\\author{" + content + "}"])
1364 del document.body[i:j + 1]
1366 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1369 j = find_end_of_layout(document.body, i)
1371 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1374 content = lyx2latex(document, document.body[i:j + 1])
1375 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1376 del document.body[i:j + 1]
1378 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1381 j = find_end_of_layout(document.body, i)
1383 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1386 content = lyx2latex(document, document.body[i:j + 1])
1387 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1388 del document.body[i:j + 1]
1390 i = find_token(document.body, "\\begin_layout Short Title", 0)
1393 j = find_end_of_layout(document.body, i)
1395 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1398 content = lyx2latex(document, document.body[i:j + 1])
1399 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1400 del document.body[i:j + 1]
1402 i = find_token(document.body, "\\begin_layout Abstract", 0)
1405 j = find_end_of_layout(document.body, i)
1407 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1410 content = lyx2latex(document, document.body[i:j + 1])
1411 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1412 del document.body[i:j + 1]
1414 i = find_token(document.body, "\\begin_layout Keywords", 0)
1417 j = find_end_of_layout(document.body, i)
1419 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1422 content = lyx2latex(document, document.body[i:j + 1])
1423 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1424 del document.body[i:j + 1]
1426 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1429 j = find_end_of_layout(document.body, i)
1431 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1434 content = lyx2latex(document, document.body[i:j + 1])
1435 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1436 del document.body[i:j + 1]
1438 i = find_token(document.body, "\\begin_layout Address", 0)
1441 j = find_end_of_layout(document.body, i)
1443 document.warning("Malformed LyX document: Can't find end of Address layout")
1446 content = lyx2latex(document, document.body[i:j + 1])
1447 add_to_preamble(document, ["\\Address{" + content + "}"])
1448 del document.body[i:j + 1]
1449 # finally handle the code layouts
1454 while m != -1 or j != -1 or h != -1 or k != -1:
1457 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1459 endh = find_end_of_inset(document.body, h)
1460 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1461 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1462 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1466 j = find_token(document.body, "\\begin_layout Code Input", j)
1468 endj = find_end_of_layout(document.body, j)
1469 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1470 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1471 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1472 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1473 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1477 k = find_token(document.body, "\\begin_layout Code Output", k)
1479 endk = find_end_of_layout(document.body, k)
1480 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1481 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1482 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1483 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1484 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1488 m = find_token(document.body, "\\begin_layout Code", m)
1490 endm = find_end_of_layout(document.body, m)
1491 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1492 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1493 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1494 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1495 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1499 def convert_subref(document):
1500 " converts sub: ref prefixes to subref: "
1503 rx = re.compile(r'^name \"sub:(.+)$')
1506 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1509 j = find_end_of_inset(document.body, i)
1511 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1515 for p in range(i, j):
1516 m = rx.match(document.body[p])
1519 document.body[p] = "name \"subsec:" + label
1523 rx = re.compile(r'^reference \"sub:(.+)$')
1526 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1529 j = find_end_of_inset(document.body, i)
1531 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1535 for p in range(i, j):
1536 m = rx.match(document.body[p])
1539 document.body[p] = "reference \"subsec:" + label
1545 def revert_subref(document):
1546 " reverts subref: ref prefixes to sub: "
1549 rx = re.compile(r'^name \"subsec:(.+)$')
1552 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1555 j = find_end_of_inset(document.body, i)
1557 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1561 for p in range(i, j):
1562 m = rx.match(document.body[p])
1565 document.body[p] = "name \"sub:" + label
1570 rx = re.compile(r'^reference \"subsec:(.+)$')
1573 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1576 j = find_end_of_inset(document.body, i)
1578 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1582 for p in range(i, j):
1583 m = rx.match(document.body[p])
1586 document.body[p] = "reference \"sub:" + label
1591 def convert_nounzip(document):
1592 " remove the noUnzip parameter of graphics insets "
1594 rx = re.compile(r'\s*noUnzip\s*$')
1597 i = find_token(document.body, "\\begin_inset Graphics", i)
1600 j = find_end_of_inset(document.body, i)
1602 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1606 k = find_re(document.body, rx, i, j)
1608 del document.body[k]
1613 def convert_revert_external_bbox(document, forward):
1614 " add units to bounding box of external insets "
1616 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1619 i = find_token(document.body, "\\begin_inset External", i)
1622 j = find_end_of_inset(document.body, i)
1624 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1627 k = find_re(document.body, rx, i, j)
1631 tokens = document.body[k].split()
1633 for t in range(1, 5):
1636 for t in range(1, 5):
1637 tokens[t] = length_in_bp(tokens[t])
1638 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1639 tokens[3] + " " + tokens[4]
1643 def convert_external_bbox(document):
1644 convert_revert_external_bbox(document, True)
1647 def revert_external_bbox(document):
1648 convert_revert_external_bbox(document, False)
1651 def revert_tcolorbox_1(document):
1652 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1655 i = find_token(document.header, "tcolorbox", i)
1661 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1664 flexEnd = find_end_of_inset(document.body, flex)
1665 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1666 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1667 flexEnd = find_end_of_inset(document.body, flex)
1669 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1671 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1672 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1676 def revert_tcolorbox_2(document):
1677 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1680 i = find_token(document.header, "tcolorbox", i)
1686 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1689 flexEnd = find_end_of_inset(document.body, flex)
1690 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1691 flexEnd = find_end_of_inset(document.body, flex)
1692 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1693 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1697 def revert_tcolorbox_3(document):
1698 " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1701 i = find_token(document.header, "tcolorbox", i)
1707 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1710 flexEnd = find_end_of_inset(document.body, flex)
1711 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1712 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1713 flexEnd = find_end_of_inset(document.body, flex)
1714 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1715 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1719 def revert_tcolorbox_4(document):
1720 " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1723 i = find_token(document.header, "tcolorbox", i)
1729 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1732 flexEnd = find_end_of_inset(document.body, flex)
1733 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1734 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1735 flexEnd = find_end_of_inset(document.body, flex)
1736 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1737 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1741 def revert_tcolorbox_5(document):
1742 " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1745 i = find_token(document.header, "tcolorbox", i)
1751 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1754 flexEnd = find_end_of_inset(document.body, flex)
1755 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1756 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1757 flexEnd = find_end_of_inset(document.body, flex)
1758 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1759 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1763 def revert_tcolorbox_6(document):
1764 " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1767 i = find_token(document.header, "tcolorbox", i)
1773 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1776 flexEnd = find_end_of_inset(document.body, flex)
1777 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1778 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1779 flexEnd = find_end_of_inset(document.body, flex)
1780 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1781 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1785 def revert_tcolorbox_7(document):
1786 " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1789 i = find_token(document.header, "tcolorbox", i)
1795 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1798 flexEnd = find_end_of_inset(document.body, flex)
1799 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1800 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1801 flexEnd = find_end_of_inset(document.body, flex)
1802 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1803 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1807 def revert_tcolorbox_8(document):
1808 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1814 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1816 j = find_end_of_layout(document.body, i)
1817 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1818 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1819 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1820 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1822 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1824 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1825 k = find_end_of_inset(document.body, j)
1826 k = find_token(document.body, "\\end_inset", k + 1)
1827 k = find_token(document.body, "\\end_inset", k + 1)
1829 k = find_token(document.body, "\\end_inset", k + 1)
1830 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1831 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1832 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1838 def revert_moderncv_1(document):
1839 " Reverts the new inset of moderncv to TeX-code in preamble "
1841 if document.textclass != "moderncv":
1847 # at first revert the new styles
1849 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1852 j = find_end_of_layout(document.body, i)
1854 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1857 content = lyx2latex(document, document.body[i:j + 1])
1858 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1859 del document.body[i:j + 1]
1861 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1864 j = find_end_of_layout(document.body, i)
1866 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1869 content = lyx2latex(document, document.body[i:j + 1])
1870 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1871 del document.body[i:j + 1]
1872 # now change the new styles to the obsolete ones
1874 i = find_token(document.body, "\\begin_layout Name", 0)
1877 j = find_end_of_layout(document.body, i)
1879 document.warning("Malformed LyX document: Can't find end of Name layout")
1882 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1883 if lineArg > j and j != 0:
1886 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1887 # we have to assure that no other inset is in the Argument
1888 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1889 endInset = find_token(document.body, "\\end_inset", beginPlain)
1892 while beginInset < endInset and beginInset != -1:
1893 beginInset = find_token(document.body, "\\begin_inset", k)
1894 endInset = find_token(document.body, "\\end_inset", l)
1897 Arg2 = document.body[l + 5 : l + 6]
1899 document.body[i : i + 1]= ["\\begin_layout FirstName"]
1900 # delete the Argument inset
1901 del( document.body[endInset - 2 : endInset + 3])
1902 del( document.body[lineArg : beginPlain + 1])
1903 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1906 def revert_moderncv_2(document):
1907 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1909 if document.textclass != "moderncv":
1916 i = find_token(document.body, "\\begin_layout Phone", i)
1919 j = find_end_of_layout(document.body, i)
1921 document.warning("Malformed LyX document: Can't find end of Phone layout")
1924 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1925 if lineArg > j and j != 0:
1929 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1930 # we have to assure that no other inset is in the Argument
1931 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1932 endInset = find_token(document.body, "\\end_inset", beginPlain)
1935 while beginInset < endInset and beginInset != -1:
1936 beginInset = find_token(document.body, "\\begin_inset", k)
1937 endInset = find_token(document.body, "\\end_inset", l)
1940 Arg = document.body[beginPlain + 1 : beginPlain + 2]
1942 if Arg[0] == "mobile":
1943 document.body[i : i + 1]= ["\\begin_layout Mobile"]
1945 document.body[i : i + 1]= ["\\begin_layout Fax"]
1946 # delete the Argument inset
1947 del(document.body[endInset - 2 : endInset + 1])
1948 del(document.body[lineArg : beginPlain + 3])
1952 def convert_moderncv_phone(document):
1953 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
1955 if document.textclass != "moderncv":
1962 "Mobile" : "mobile",
1966 rx = re.compile(r'^\\begin_layout (\S+)$')
1968 # substitute \fax and \mobile by \phone[fax] and \phone[mobile], respectively
1969 i = find_token(document.body, "\\begin_layout", i)
1973 m = rx.match(document.body[i])
1977 if val not in list(phone_dict.keys()):
1980 j = find_end_of_layout(document.body, i)
1982 document.warning("Malformed LyX document: Can't find end of Mobile layout")
1986 document.body[i : i + 1] = ["\\begin_layout Phone", "\\begin_inset Argument 1", "status open", "",
1987 "\\begin_layout Plain Layout", phone_dict[val], "\\end_layout", "",
1991 def convert_moderncv_name(document):
1992 " Convert the FirstName and LastName layout of moderncv to the general Name layout "
1994 if document.textclass != "moderncv":
1997 fnb = 0 # Begin of FirstName inset
1998 fne = 0 # End of FirstName inset
1999 lnb = 0 # Begin of LastName (FamilyName) inset
2000 lne = 0 # End of LastName (FamilyName) inset
2001 nb = 0 # Begin of substituting Name inset
2002 ne = 0 # End of substituting Name inset
2003 FirstName = [] # FirstName content
2004 FamilyName = [] # LastName content
2008 fnb = find_token(document.body, "\\begin_layout FirstName", fnb)
2010 fne = find_end_of_layout(document.body, fnb)
2012 document.warning("Malformed LyX document: Can't find end of FirstName layout")
2014 FirstName = document.body[fnb + 1 : fne]
2016 lnb = find_token(document.body, "\\begin_layout FamilyName", lnb)
2018 lne = find_end_of_layout(document.body, lnb)
2020 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
2022 FamilyName = document.body[lnb + 1 : lne]
2023 # Determine the region for the substituting Name layout
2024 if fnb == -1 and lnb == -1: # Neither FirstName nor FamilyName exists -> Do nothing
2026 elif fnb == -1: # Only FamilyName exists -> New Name insets replaces that
2029 elif lnb == -1: # Only FirstName exists -> New Name insets replaces that
2032 elif fne > lne: # FirstName position before FamilyName -> New Name insets spans
2033 nb = lnb # from FamilyName begin
2034 ne = fne # to FirstName end
2035 else: # FirstName position before FamilyName -> New Name insets spans
2036 nb = fnb # from FirstName begin
2037 ne = lne # to FamilyName end
2039 # Insert the substituting layout now. If FirstName exists, use an otpional argument.
2041 document.body[nb : ne + 1] = ["\\begin_layout Name"] + FamilyName + ["\\end_layout", ""]
2043 document.body[nb : ne + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2044 "\\begin_layout Plain Layout"] + FirstName + ["\\end_layout", "",
2045 "\\end_inset", ""] + FamilyName + ["\\end_layout", ""]
2048 def revert_achemso(document):
2049 " Reverts the flex inset Latin to TeX code "
2051 if document.textclass != "achemso":
2056 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2058 j = find_end_of_inset(document.body, i)
2062 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2063 endPlain = find_end_of_layout(document.body, beginPlain)
2064 content = lyx2latex(document, document.body[beginPlain : endPlain])
2065 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2067 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2072 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2073 "\\font_sf_scale", "\\font_tt_scale"]
2074 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2075 fontquotes = [True, True, True, True, False, False]
2077 def convert_fontsettings(document):
2078 " Duplicate font settings "
2080 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2082 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2083 use_non_tex_fonts = "false"
2085 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2087 for f in fontsettings:
2088 i = find_token(document.header, f + " ", 0)
2090 document.warning("Malformed LyX document: No " + f + "!")
2092 # note that with i = -1, this will insert at the end
2094 value = fontdefaults[j]
2096 value = document.header[i][len(f):].strip()
2098 if use_non_tex_fonts == "true":
2099 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2101 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2103 if use_non_tex_fonts == "true":
2104 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2106 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2110 def revert_fontsettings(document):
2111 " Merge font settings "
2113 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2115 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2116 use_non_tex_fonts = "false"
2118 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2120 for f in fontsettings:
2121 i = find_token(document.header, f + " ", 0)
2123 document.warning("Malformed LyX document: No " + f + "!")
2126 line = get_value(document.header, f, i)
2129 q2 = line.find('"', q1+1)
2130 q3 = line.find('"', q2+1)
2131 q4 = line.find('"', q3+1)
2132 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2133 document.warning("Malformed LyX document: Missing quotes!")
2136 if use_non_tex_fonts == "true":
2137 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2139 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2141 if use_non_tex_fonts == "true":
2142 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2144 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2148 def revert_solution(document):
2149 " Reverts the solution environment of the theorem module to TeX code "
2151 # Do we use one of the modules that provides Solution?
2153 mods = document.get_module_list()
2155 if mod == "theorems-std" or mod == "theorems-bytype" \
2156 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2166 i = find_token(document.body, "\\begin_layout Solution", i)
2170 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2171 if is_starred == True:
2173 LyXName = "Solution*"
2174 theoremName = "newtheorem*"
2177 LyXName = "Solution"
2178 theoremName = "newtheorem"
2180 j = find_end_of_layout(document.body, i)
2182 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2186 # if this is not a consecutive env, add start command
2189 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2191 # has this a consecutive theorem of same type?
2192 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2194 # if this is not followed by a consecutive env, add end command
2196 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2198 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2200 add_to_preamble(document, "\\theoremstyle{definition}")
2201 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2202 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2203 (theoremName, LaTeXName))
2204 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2205 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2206 (theoremName, LaTeXName))
2208 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2212 def revert_verbatim_star(document):
2213 from lyx_2_1 import revert_verbatim
2214 revert_verbatim(document, True)
2217 def convert_save_props(document):
2218 " Add save_transient_properties parameter. "
2219 i = find_token(document.header, '\\begin_header', 0)
2221 document.warning("Malformed lyx document: Missing '\\begin_header'.")
2223 document.header.insert(i + 1, '\\save_transient_properties true')
2226 def revert_save_props(document):
2227 " Remove save_transient_properties parameter. "
2228 i = find_token(document.header, "\\save_transient_properties", 0)
2231 del document.header[i]
2234 def convert_info_tabular_feature(document):
2236 return arg.replace("inset-modify tabular", "tabular-feature")
2237 convert_info_insets(document, "shortcut(s)?|icon", f)
2240 def revert_info_tabular_feature(document):
2242 return arg.replace("tabular-feature", "inset-modify tabular")
2243 convert_info_insets(document, "shortcut(s)?|icon", f)
2250 supported_versions = ["2.2.0", "2.2"]
2252 [475, [convert_separator]],
2253 # nothing to do for 476: We consider it a bug that older versions
2254 # did not load amsmath automatically for these commands, and do not
2255 # want to hardcode amsmath off.
2261 [481, [convert_dashes]],
2262 [482, [convert_phrases]],
2263 [483, [convert_specialchar]],
2268 [488, [convert_newgloss]],
2269 [489, [convert_BoxFeatures]],
2270 [490, [convert_origin]],
2272 [492, [convert_colorbox]],
2275 [495, [convert_subref]],
2276 [496, [convert_nounzip]],
2277 [497, [convert_external_bbox]],
2279 [499, [convert_moderncv_phone, convert_moderncv_name]],
2281 [501, [convert_fontsettings]],
2284 [504, [convert_save_props]],
2286 [506, [convert_info_tabular_feature]],
2287 [507, [convert_longtable_label]]
2291 [506, [revert_longtable_label]],
2292 [505, [revert_info_tabular_feature]],
2294 [503, [revert_save_props]],
2295 [502, [revert_verbatim_star]],
2296 [501, [revert_solution]],
2297 [500, [revert_fontsettings]],
2298 [499, [revert_achemso]],
2299 [498, [revert_moderncv_1, revert_moderncv_2]],
2300 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2301 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2302 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2303 [496, [revert_external_bbox]],
2304 [495, []], # nothing to do since the noUnzip parameter was optional
2305 [494, [revert_subref]],
2306 [493, [revert_jss]],
2307 [492, [revert_mathmulticol]],
2308 [491, [revert_colorbox]],
2309 [490, [revert_textcolor]],
2310 [489, [revert_origin]],
2311 [488, [revert_BoxFeatures]],
2312 [487, [revert_newgloss, revert_glossgroup]],
2313 [486, [revert_forest]],
2314 [485, [revert_ex_itemargs]],
2315 [484, [revert_sigplan_doi]],
2316 [483, [revert_georgian]],
2317 [482, [revert_specialchar]],
2318 [481, [revert_phrases]],
2319 [480, [revert_dashes]],
2320 [479, [revert_question_env]],
2321 [478, [revert_beamer_lemma]],
2322 [477, [revert_xarrow]],
2323 [476, [revert_swissgerman]],
2324 [475, [revert_smash]],
2325 [474, [revert_separator]]
2329 if __name__ == "__main__":