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 # Provide support for both python 2 and 3
43 PY2 = sys.version_info[0] == 2
44 # End of code to support for both python 2 and 3
46 ####################################################################
47 # Private helper functions
49 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt, nolastopt):
51 Reverts an InsetArgument to TeX-code
53 revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt, notLastOpt)
54 LineOfBegin is the line of the \begin_layout or \begin_inset statement
55 LineOfEnd is the line of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
56 StartArgument is the number of the first argument that needs to be converted
57 EndArgument is the number of the last argument that needs to be converted or the last defined one
58 isEnvironment must be true, if the layout is for a LaTeX environment
59 isOpt must be true, if the argument is an optional one
60 notLastOpt must be true if the argument is mandatory and followed by optional ones
64 while lineArg != -1 and n < nmax + 1:
65 lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
66 if lineArg > endline and endline != 0:
69 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
70 # we have to assure that no other inset is in the Argument
71 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
72 endInset = find_token(document.body, "\\end_inset", beginPlain)
75 while beginInset < endInset and beginInset != -1:
76 beginInset = find_token(document.body, "\\begin_inset", k)
77 endInset = find_token(document.body, "\\end_inset", l)
80 if environment == False:
82 if nolastopt == False:
83 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
85 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
86 del(document.body[lineArg : beginPlain + 1])
89 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
90 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("{")
98 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
99 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
105 ###############################################################################
107 ### Conversion and reversion routines
109 ###############################################################################
111 def convert_longtable_label_internal(document, forward):
113 Convert reference to "LongTableNoNumber" into "Unnumbered" if forward is True
116 old_reference = "\\begin_inset Caption LongTableNoNumber"
117 new_reference = "\\begin_inset Caption Unnumbered"
119 # if the purpose is to revert swap the strings roles
121 old_reference, new_reference = new_reference, old_reference
125 i = find_token(document.body, old_reference, i)
130 document.body[i] = new_reference
133 def convert_longtable_label(document):
134 convert_longtable_label_internal(document, True)
137 def revert_longtable_label(document):
138 convert_longtable_label_internal(document, False)
141 def convert_separator(document):
143 Convert layout separators to separator insets and add (LaTeX) paragraph
144 breaks in order to mimic previous LaTeX export.
147 parins = ["\\begin_inset Separator parbreak", "\\end_inset", ""]
148 parlay = ["\\begin_layout Standard", "\\begin_inset Separator parbreak",
149 "\\end_inset", "", "\\end_layout", ""]
151 "family" : "default",
152 "series" : "default",
161 i = find_token(document.body, "\\begin_deeper", i)
165 j = find_token_backwards(document.body, "\\end_layout", i-1)
167 # reset any text style before inserting the inset
168 lay = get_containing_layout(document.body, j-1)
170 content = "\n".join(document.body[lay[1]:lay[2]])
171 for val in list(sty_dict.keys()):
172 if content.find("\\%s" % val) != -1:
173 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
176 document.body[j:j] = parins
177 i = i + len(parins) + 1
183 i = find_token(document.body, "\\align", i)
187 lay = get_containing_layout(document.body, i)
188 if lay != False and lay[0] == "Plain Layout":
192 j = find_token_backwards(document.body, "\\end_layout", i-1)
194 lay = get_containing_layout(document.body, j-1)
195 if lay != False and lay[0] == "Standard" \
196 and find_token(document.body, "\\align", lay[1], lay[2]) == -1 \
197 and find_token(document.body, "\\begin_inset VSpace", lay[1], lay[2]) == -1:
198 # reset any text style before inserting the inset
199 content = "\n".join(document.body[lay[1]:lay[2]])
200 for val in list(sty_dict.keys()):
201 if content.find("\\%s" % val) != -1:
202 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
205 document.body[j:j] = parins
206 i = i + len(parins) + 1
212 regexp = re.compile(r'^\\begin_layout (?:(-*)|(\s*))(Separator|EndOfSlide)(?:(-*)|(\s*))$', re.IGNORECASE)
216 i = find_re(document.body, regexp, i)
220 j = find_end_of_layout(document.body, i)
222 document.warning("Malformed LyX document: Missing `\\end_layout'.")
225 lay = get_containing_layout(document.body, j-1)
227 lines = document.body[lay[3]:lay[2]]
231 document.body[i:j+1] = parlay
233 document.body[i+1:i+1] = lines
235 i = i + len(parlay) + len(lines) + 1
238 def revert_separator(document):
239 " Revert separator insets to layout separators "
241 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
242 if document.textclass in beamer_classes:
243 beglaysep = "\\begin_layout Separator"
245 beglaysep = "\\begin_layout --Separator--"
247 parsep = [beglaysep, "", "\\end_layout", ""]
248 comert = ["\\begin_inset ERT", "status collapsed", "",
249 "\\begin_layout Plain Layout", "%", "\\end_layout",
250 "", "\\end_inset", ""]
251 empert = ["\\begin_inset ERT", "status collapsed", "",
252 "\\begin_layout Plain Layout", " ", "\\end_layout",
253 "", "\\end_inset", ""]
257 i = find_token(document.body, "\\begin_inset Separator", i)
261 lay = get_containing_layout(document.body, i)
263 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
270 kind = get_value(document.body, "\\begin_inset Separator", i, i+1, "plain").split()[1]
271 before = document.body[beg+1:i]
272 something_before = len(before) > 0 and len("".join(before)) > 0
273 j = find_end_of_inset(document.body, i)
274 after = document.body[j+1:end]
275 something_after = len(after) > 0 and len("".join(after)) > 0
277 beg = beg + len(before) + 1
278 elif something_before:
279 document.body[i:i] = ["\\end_layout", ""]
287 document.body[beg:j+1] = empert
290 document.body[beg:j+1] = comert
294 if layoutname == "Standard":
295 if not something_before:
296 document.body[beg:j+1] = parsep
298 document.body[i:i] = ["", "\\begin_layout Standard"]
301 document.body[beg:j+1] = ["\\begin_layout Standard"]
304 document.body[beg:j+1] = ["\\begin_deeper"]
306 end = end + 1 - (j + 1 - beg)
307 if not something_before:
308 document.body[i:i] = parsep
310 end = end + len(parsep)
311 document.body[i:i] = ["\\begin_layout Standard"]
312 document.body[end+2:end+2] = ["", "\\end_deeper", ""]
315 next_par_is_aligned = False
316 k = find_nonempty_line(document.body, end+1)
317 if k != -1 and check_token(document.body[k], "\\begin_layout"):
318 lay = get_containing_layout(document.body, k)
319 next_par_is_aligned = lay != False and \
320 find_token(document.body, "\\align", lay[1], lay[2]) != -1
321 if k != -1 and not next_par_is_aligned \
322 and not check_token(document.body[k], "\\end_deeper") \
323 and not check_token(document.body[k], "\\begin_deeper"):
324 if layoutname == "Standard":
325 document.body[beg:j+1] = [beglaysep]
328 document.body[beg:j+1] = ["\\begin_deeper", beglaysep]
329 end = end + 2 - (j + 1 - beg)
330 document.body[end+1:end+1] = ["", "\\end_deeper", ""]
334 del document.body[i:end+1]
336 del document.body[i:end-1]
341 def convert_parbreak(document):
343 Convert parbreak separators not specifically used to separate
344 environments to latexpar separators.
346 parbreakinset = "\\begin_inset Separator parbreak"
349 i = find_token(document.body, parbreakinset, i)
352 lay = get_containing_layout(document.body, i)
354 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
357 if lay[0] == "Standard":
358 # Convert only if not alone in the paragraph
359 k1 = find_nonempty_line(document.body, lay[1] + 1, i + 1)
360 k2 = find_nonempty_line(document.body, i + 1, lay[2])
361 if (k1 < i) or (k2 > i + 1) or not check_token(document.body[i], parbreakinset):
362 document.body[i] = document.body[i].replace("parbreak", "latexpar")
364 document.body[i] = document.body[i].replace("parbreak", "latexpar")
368 def revert_parbreak(document):
370 Revert latexpar separators to parbreak separators.
374 i = find_token(document.body, "\\begin_inset Separator latexpar", i)
377 document.body[i] = document.body[i].replace("latexpar", "parbreak")
381 def revert_smash(document):
382 " Set amsmath to on if smash commands are used "
384 commands = ["smash[t]", "smash[b]", "notag"]
385 i = find_token(document.header, "\\use_package amsmath", 0)
387 document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
389 value = get_value(document.header, "\\use_package amsmath", i).split()[1]
391 # nothing to do if package is not auto but on or off
395 j = find_token(document.body, '\\begin_inset Formula', j)
398 k = find_end_of_inset(document.body, j)
400 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
403 code = "\n".join(document.body[j:k])
405 if code.find("\\%s" % c) != -1:
406 # set amsmath to on, since it is loaded by the newer format
407 document.header[i] = "\\use_package amsmath 2"
412 def revert_swissgerman(document):
413 " Set language german-ch-old to german "
415 if document.language == "german-ch-old":
416 document.language = "german"
417 i = find_token(document.header, "\\language", 0)
419 document.header[i] = "\\language german"
422 j = find_token(document.body, "\\lang german-ch-old", j)
425 document.body[j] = document.body[j].replace("\\lang german-ch-old", "\\lang german")
429 def revert_use_package(document, pkg, commands, oldauto, supported):
430 # oldauto defines how the version we are reverting to behaves:
431 # if it is true, the old version uses the package automatically.
432 # if it is false, the old version never uses the package.
433 # If "supported" is true, the target version also supports this
435 regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
436 p = find_re(document.header, regexp, 0)
437 value = "1" # default is auto
439 value = get_value(document.header, "\\use_package" , p).split()[1]
441 del document.header[p]
442 if value == "2" and not supported: # on
443 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
444 elif value == "1" and not oldauto: # auto
447 i = find_token(document.body, '\\begin_inset Formula', i)
450 j = find_end_of_inset(document.body, i)
452 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
455 code = "\n".join(document.body[i:j])
457 if code.find("\\%s" % c) != -1:
459 document.header[p] = "\\use_package " + pkg + " 2"
461 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
466 mathtools_commands = ["xhookrightarrow", "xhookleftarrow", "xRightarrow", \
467 "xrightharpoondown", "xrightharpoonup", "xrightleftharpoons", \
468 "xLeftarrow", "xleftharpoondown", "xleftharpoonup", \
469 "xleftrightarrow", "xLeftrightarrow", "xleftrightharpoons", \
472 def revert_xarrow(document):
473 "remove use_package mathtools"
474 revert_use_package(document, "mathtools", mathtools_commands, False, True)
477 def revert_beamer_lemma(document):
478 " Reverts beamer lemma layout to ERT "
480 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
481 if document.textclass not in beamer_classes:
487 i = find_token(document.body, "\\begin_layout Lemma", i)
490 j = find_end_of_layout(document.body, i)
492 document.warning("Malformed LyX document: Can't find end of Lemma layout")
495 arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j)
496 endarg1 = find_end_of_inset(document.body, arg1)
497 arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j)
498 endarg2 = find_end_of_inset(document.body, arg2)
502 beginPlain1 = find_token(document.body, "\\begin_layout Plain Layout", arg1, endarg1)
503 if beginPlain1 == -1:
504 document.warning("Malformed LyX document: Can't find arg1 plain Layout")
507 endPlain1 = find_end_of_inset(document.body, beginPlain1)
508 content1 = document.body[beginPlain1 + 1 : endPlain1 - 2]
509 subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">")
511 beginPlain2 = find_token(document.body, "\\begin_layout Plain Layout", arg2, endarg2)
512 if beginPlain2 == -1:
513 document.warning("Malformed LyX document: Can't find arg2 plain Layout")
516 endPlain2 = find_end_of_inset(document.body, beginPlain2)
517 content2 = document.body[beginPlain2 + 1 : endPlain2 - 2]
518 subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]")
522 del document.body[arg2 : endarg2 + 1]
524 del document.body[arg1 : endarg1 + 1]
526 del document.body[arg1 : endarg1 + 1]
528 del document.body[arg2 : endarg2 + 1]
530 # index of end layout has probably changed
531 j = find_end_of_layout(document.body, i)
533 document.warning("Malformed LyX document: Can't find end of Lemma layout")
539 # if this is not a consecutive env, add start command
541 begcmd = put_cmd_in_ert("\\begin{lemma}")
543 # has this a consecutive lemma?
544 consecutive = document.body[j + 2] == "\\begin_layout Lemma"
546 # if this is not followed by a consecutive env, add end command
548 document.body[j : j + 1] = put_cmd_in_ert("\\end{lemma}") + ["\\end_layout"]
550 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd + subst1 + subst2
556 def revert_question_env(document):
558 Reverts question and question* environments of
559 theorems-ams-extended-bytype module to ERT
562 # Do we use theorems-ams-extended-bytype module?
563 if not "theorems-ams-extended-bytype" in document.get_module_list():
569 i = find_token(document.body, "\\begin_layout Question", i)
573 starred = document.body[i] == "\\begin_layout Question*"
575 j = find_end_of_layout(document.body, i)
577 document.warning("Malformed LyX document: Can't find end of Question layout")
581 # if this is not a consecutive env, add start command
585 begcmd = put_cmd_in_ert("\\begin{question*}")
587 begcmd = put_cmd_in_ert("\\begin{question}")
589 # has this a consecutive theorem of same type?
592 consecutive = document.body[j + 2] == "\\begin_layout Question*"
594 consecutive = document.body[j + 2] == "\\begin_layout Question"
596 # if this is not followed by a consecutive env, add end command
599 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
601 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
603 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
605 add_to_preamble(document, "\\providecommand{\questionname}{Question}")
608 add_to_preamble(document, "\\theoremstyle{plain}\n" \
609 "\\newtheorem*{question*}{\\protect\\questionname}")
611 add_to_preamble(document, "\\theoremstyle{plain}\n" \
612 "\\newtheorem{question}{\\protect\\questionname}")
617 def convert_dashes(document):
618 "convert -- and --- to \\twohyphens and \\threehyphens"
620 if document.backend != "latex":
624 while i < len(document.body):
625 words = document.body[i].split()
626 if len(words) > 1 and words[0] == "\\begin_inset" and \
627 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
628 # must not replace anything in insets that store LaTeX contents in .lyx files
629 # (math and command insets withut overridden read() and write() methods
630 # filtering out IPA makes Text::readParToken() more simple
631 # skip ERT as well since it is not needed there
632 j = find_end_of_inset(document.body, i)
634 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
640 j = document.body[i].find("--")
643 front = document.body[i][:j]
644 back = document.body[i][j+2:]
645 # We can have an arbitrary number of consecutive hyphens.
646 # These must be split into the corresponding number of two and three hyphens
647 # We must match what LaTeX does: First try emdash, then endash, then single hyphen
648 if back.find("-") == 0:
651 document.body.insert(i+1, back)
652 document.body[i] = front + "\\threehyphens"
655 document.body.insert(i+1, back)
656 document.body[i] = front + "\\twohyphens"
660 def revert_dashes(document):
661 "convert \\twohyphens and \\threehyphens to -- and ---"
664 while i < len(document.body):
665 words = document.body[i].split()
666 if len(words) > 1 and words[0] == "\\begin_inset" and \
667 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
669 j = find_end_of_inset(document.body, i)
671 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
677 if document.body[i].find("\\twohyphens") >= 0:
678 document.body[i] = document.body[i].replace("\\twohyphens", "--")
680 if document.body[i].find("\\threehyphens") >= 0:
681 document.body[i] = document.body[i].replace("\\threehyphens", "---")
683 if replaced and i+1 < len(document.body) and \
684 (document.body[i+1].find("\\") != 0 or \
685 document.body[i+1].find("\\twohyphens") == 0 or
686 document.body[i+1].find("\\threehyphens") == 0) and \
687 len(document.body[i]) + len(document.body[i+1]) <= 80:
688 document.body[i] = document.body[i] + document.body[i+1]
689 document.body[i+1:i+2] = []
694 # order is important for the last three!
695 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
697 def is_part_of_converted_phrase(line, j, phrase):
698 "is phrase part of an already converted phrase?"
700 converted = "\\SpecialCharNoPassThru \\" + p
701 pos = j + len(phrase) - len(converted)
703 if line[pos:pos+len(converted)] == converted:
708 def convert_phrases(document):
709 "convert special phrases from plain text to \\SpecialCharNoPassThru"
711 if document.backend != "latex":
714 for phrase in phrases:
716 while i < len(document.body):
717 words = document.body[i].split()
718 if len(words) > 1 and words[0] == "\\begin_inset" and \
719 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
720 # must not replace anything in insets that store LaTeX contents in .lyx files
721 # (math and command insets withut overridden read() and write() methods
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 if document.body[i].find("\\") == 0:
732 j = document.body[i].find(phrase)
736 if not is_part_of_converted_phrase(document.body[i], j, phrase):
737 front = document.body[i][:j]
738 back = document.body[i][j+len(phrase):]
740 document.body.insert(i+1, back)
741 # We cannot use SpecialChar since we do not know whether we are outside passThru
742 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
746 def revert_phrases(document):
747 "convert special phrases to plain text"
750 while i < len(document.body):
751 words = document.body[i].split()
752 if len(words) > 1 and words[0] == "\\begin_inset" and \
753 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
754 # see convert_phrases
755 j = find_end_of_inset(document.body, i)
757 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
763 for phrase in phrases:
764 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
765 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
766 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
768 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
769 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
771 if replaced and i+1 < len(document.body) and \
772 (document.body[i+1].find("\\") != 0 or \
773 document.body[i+1].find("\\SpecialChar") == 0) and \
774 len(document.body[i]) + len(document.body[i+1]) <= 80:
775 document.body[i] = document.body[i] + document.body[i+1]
776 document.body[i+1:i+2] = []
781 def convert_specialchar_internal(document, forward):
782 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
783 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
784 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
785 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
786 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
787 "\\LaTeX":"LaTeX" # must be after LaTeX2e
791 while i < len(document.body):
792 words = document.body[i].split()
793 if len(words) > 1 and words[0] == "\\begin_inset" and \
794 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
795 # see convert_phrases
796 j = find_end_of_inset(document.body, i)
798 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
803 for key, value in specialchars.items():
805 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
806 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
808 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
809 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
813 def convert_specialchar(document):
814 "convert special characters to new syntax"
815 convert_specialchar_internal(document, True)
818 def revert_specialchar(document):
819 "convert special characters to old syntax"
820 convert_specialchar_internal(document, False)
823 def revert_georgian(document):
824 "Set the document language to English but assure Georgian output"
826 if document.language == "georgian":
827 document.language = "english"
828 i = find_token(document.header, "\\language georgian", 0)
830 document.header[i] = "\\language english"
831 j = find_token(document.header, "\\language_package default", 0)
833 document.header[j] = "\\language_package babel"
834 k = find_token(document.header, "\\options", 0)
836 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
838 l = find_token(document.header, "\\use_default_options", 0)
839 document.header.insert(l + 1, "\\options georgian")
842 def revert_sigplan_doi(document):
843 " Reverts sigplanconf DOI layout to ERT "
845 if document.textclass != "sigplanconf":
850 i = find_token(document.body, "\\begin_layout DOI", i)
853 j = find_end_of_layout(document.body, i)
855 document.warning("Malformed LyX document: Can't find end of DOI layout")
859 content = lyx2latex(document, document.body[i:j + 1])
860 add_to_preamble(document, ["\\doi{" + content + "}"])
861 del document.body[i:j + 1]
865 def revert_ex_itemargs(document):
866 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
868 if not "linguistics" in document.get_module_list():
872 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
874 i = find_token(document.body, "\\begin_inset Argument item:", i)
877 j = find_end_of_inset(document.body, i)
878 # Find containing paragraph layout
879 parent = get_containing_layout(document.body, i)
881 document.warning("Malformed LyX document: Can't find parent paragraph layout")
885 layoutname = parent[0]
886 if layoutname in example_layouts:
887 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
888 endPlain = find_end_of_layout(document.body, beginPlain)
889 content = document.body[beginPlain + 1 : endPlain]
890 del document.body[i:j+1]
891 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
892 document.body[parbeg : parbeg] = subst
896 def revert_forest(document):
897 " Reverts the forest environment (Linguistics module) to TeX-code "
899 if not "linguistics" in document.get_module_list():
904 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
907 j = find_end_of_inset(document.body, i)
909 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
913 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
914 endPlain = find_end_of_layout(document.body, beginPlain)
915 content = lyx2latex(document, document.body[beginPlain : endPlain])
917 add_to_preamble(document, ["\\usepackage{forest}"])
919 document.body[i:j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
923 def revert_glossgroup(document):
924 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
926 if not "linguistics" in document.get_module_list():
931 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
934 j = find_end_of_inset(document.body, i)
936 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
940 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
941 endPlain = find_end_of_layout(document.body, beginPlain)
942 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
944 document.body[i:j + 1] = ["{", "", content, "", "}"]
948 def revert_newgloss(document):
949 " Reverts the new Glosse insets (Linguistics module) to the old format "
951 if not "linguistics" in document.get_module_list():
954 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
955 for glosse in glosses:
958 i = find_token(document.body, glosse, i)
961 j = find_end_of_inset(document.body, i)
963 document.warning("Malformed LyX document: Can't find end of Glosse inset")
967 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
968 endarg = find_end_of_inset(document.body, arg)
971 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
972 if argbeginPlain == -1:
973 document.warning("Malformed LyX document: Can't find arg plain Layout")
976 argendPlain = find_end_of_inset(document.body, argbeginPlain)
977 argcontent = lyx2verbatim(document, document.body[argbeginPlain : argendPlain - 2])
979 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
980 argcontent, "\\end_layout"]
982 # remove Arg insets and paragraph, if it only contains this inset
983 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
984 del document.body[arg - 1 : endarg + 4]
986 del document.body[arg : endarg + 1]
988 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
989 endPlain = find_end_of_layout(document.body, beginPlain)
990 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
992 document.body[beginPlain + 1:endPlain] = [content]
995 # Dissolve ERT insets
996 for glosse in glosses:
999 i = find_token(document.body, glosse, i)
1002 j = find_end_of_inset(document.body, i)
1004 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1008 ert = find_token(document.body, "\\begin_inset ERT", i, j)
1011 ertend = find_end_of_inset(document.body, ert)
1013 document.warning("Malformed LyX document: Can't find end of ERT inset")
1016 ertcontent = get_ert(document.body, ert, True)
1017 document.body[ert : ertend + 1] = [ertcontent]
1021 def convert_newgloss(document):
1022 " Converts Glosse insets (Linguistics module) to the new format "
1024 if not "linguistics" in document.get_module_list():
1027 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1028 for glosse in glosses:
1031 i = find_token(document.body, glosse, i)
1034 j = find_end_of_inset(document.body, i)
1036 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1043 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
1044 if beginPlain == -1:
1046 endPlain = find_end_of_layout(document.body, beginPlain)
1048 document.warning("Malformed LyX document: Can't find end of Glosse layout")
1052 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
1053 if glt != -1 and document.body[glt + 1].startswith("glt"):
1054 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1055 argcontent = document.body[glt + 1 : endPlain]
1056 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
1057 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
1058 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
1059 "\\end_layout", "", "\\end_inset"]
1061 content = document.body[beginPlain + 1 : endPlain]
1062 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
1063 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
1065 endPlain = find_end_of_layout(document.body, beginPlain)
1067 j = find_end_of_inset(document.body, i)
1072 def convert_BoxFeatures(document):
1073 " adds new box features "
1077 i = find_token(document.body, "height_special", i)
1080 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1084 def revert_BoxFeatures(document):
1085 " outputs new box features as TeX code "
1089 defaultThick = "0.4pt"
1090 defaultShadow = "4pt"
1092 i = find_token(document.body, "height_special", i)
1095 # read out the values
1096 beg = document.body[i+1].find('"');
1097 end = document.body[i+1].rfind('"');
1098 thickness = document.body[i+1][beg+1:end];
1099 beg = document.body[i+2].find('"');
1100 end = document.body[i+2].rfind('"');
1101 separation = document.body[i+2][beg+1:end];
1102 beg = document.body[i+3].find('"');
1103 end = document.body[i+3].rfind('"');
1104 shadowsize = document.body[i+3][beg+1:end];
1105 # delete the specification
1106 del document.body[i+1:i+4]
1108 # first output the closing brace
1109 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1110 document.body[i + 10 : i + 10] = put_cmd_in_ert("}")
1111 # now output the lengths
1112 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1113 document.body[i - 10 : i - 10] = put_cmd_in_ert("{")
1114 if thickness != defaultThick:
1115 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness]
1116 if separation != defaultSep and thickness == defaultThick:
1117 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation]
1118 if separation != defaultSep and thickness != defaultThick:
1119 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1120 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1121 document.body[i - 5 : i - 4] = ["{\\backslash shadowsize " + shadowsize]
1122 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1123 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1124 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1125 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1126 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1127 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1131 def convert_origin(document):
1132 " Insert the origin tag "
1134 i = find_token(document.header, "\\textclass ", 0)
1136 document.warning("Malformed LyX document: No \\textclass!!")
1138 if document.dir == "":
1142 if document.systemlyxdir and document.systemlyxdir != '':
1144 if os.path.isabs(document.dir):
1145 absdir = os.path.normpath(document.dir)
1147 absdir = os.path.normpath(os.path.abspath(document.dir))
1148 if os.path.isabs(document.systemlyxdir):
1149 abssys = os.path.normpath(document.systemlyxdir)
1151 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1152 relpath = os.path.relpath(absdir, abssys)
1153 if relpath.find('..') == 0:
1158 origin = document.dir.replace('\\', '/') + '/'
1160 origin = os.path.join("/systemlyxdir", relpath).replace('\\', '/') + '/'
1161 if os.name != 'nt' and PY2:
1162 origin = unicode(origin, sys.getfilesystemencoding())
1163 document.header[i:i] = ["\\origin " + origin]
1166 def revert_origin(document):
1167 " Remove the origin tag "
1169 i = find_token(document.header, "\\origin ", 0)
1171 document.warning("Malformed LyX document: No \\origin!!")
1173 del document.header[i]
1176 color_names = ["brown", "darkgray", "gray", \
1177 "lightgray", "lime", "olive", "orange", \
1178 "pink", "purple", "teal", "violet"]
1180 def revert_textcolor(document):
1181 " revert new \\textcolor colors to TeX code "
1187 i = find_token(document.body, "\\color ", i)
1191 for color in list(color_names):
1192 if document.body[i] == "\\color " + color:
1193 # register that xcolor must be loaded in the preamble
1196 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1197 # find the next \\color and/or the next \\end_layout
1198 j = find_token(document.body, "\\color", i + 1)
1199 k = find_token(document.body, "\\end_layout", i + 1)
1200 if j == -1 and k != -1:
1203 # first output the closing brace
1205 document.body[k: k] = put_cmd_in_ert("}")
1207 document.body[j: j] = put_cmd_in_ert("}")
1208 # now output the \textcolor command
1209 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1213 def convert_colorbox(document):
1214 " adds color settings for boxes "
1218 i = find_token(document.body, "shadowsize", i)
1221 document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1225 def revert_colorbox(document):
1226 " outputs color settings for boxes as TeX code "
1229 defaultframecolor = "black"
1230 defaultbackcolor = "none"
1232 binset = find_token(document.body, "\\begin_inset Box", binset)
1236 einset = find_end_of_inset(document.body, binset)
1238 document.warning("Malformed LyX document: Can't find end of box inset!")
1242 blay = find_token(document.body, "\\begin_layout", binset, einset)
1244 document.warning("Malformed LyX document: Can't find start of layout!")
1248 # doing it this way, we make sure only to find a framecolor option
1249 frame = find_token(document.body, "framecolor", binset, blay)
1254 beg = document.body[frame].find('"')
1255 end = document.body[frame].rfind('"')
1256 framecolor = document.body[frame][beg + 1 : end]
1258 # this should be on the next line
1260 beg = document.body[bgcolor].find('"')
1261 end = document.body[bgcolor].rfind('"')
1262 backcolor = document.body[bgcolor][beg + 1 : end]
1265 del document.body[frame : frame + 2]
1266 # adjust end of inset
1269 if document.body[binset] == "\\begin_inset Box Boxed" and \
1270 framecolor != defaultframecolor:
1271 document.body[binset] = "\\begin_inset Box Frameless"
1274 # first output the closing brace
1275 if framecolor == defaultframecolor and backcolor == defaultbackcolor:
1279 # we also neeed to load xcolor in the preamble but only once
1280 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1281 document.body[einset + 1 : einset + 1] = put_cmd_in_ert("}")
1282 if framecolor != defaultframecolor:
1283 document.body[binset:binset] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1285 document.body[binset:binset] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
1290 def revert_mathmulticol(document):
1291 " Convert formulas to ERT if they contain multicolumns "
1295 i = find_token(document.body, '\\begin_inset Formula', i)
1298 j = find_end_of_inset(document.body, i)
1300 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1303 lines = document.body[i:j]
1304 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1305 code = "\n".join(lines)
1310 n = code.find("\\multicolumn", k)
1311 # no need to convert degenerated multicolumn cells,
1312 # they work in old LyX versions as "math ERT"
1313 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1314 ert = put_cmd_in_ert(code)
1315 document.body[i:j+1] = ert
1321 i = find_end_of_inset(document.body, i)
1326 def revert_jss(document):
1327 " Reverts JSS In_Preamble commands to ERT in preamble "
1329 if document.textclass != "jss":
1338 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1339 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1342 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1344 endh = find_end_of_inset(document.body, h)
1345 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1346 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1350 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1352 endm = find_end_of_inset(document.body, m)
1353 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1354 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1358 j = find_token(document.body, "\\begin_inset Flex Code", j)
1360 # assure that we are not in a Code Chunk inset
1361 if document.body[j][-1] == "e":
1362 endj = find_end_of_inset(document.body, j)
1363 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1364 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1370 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1372 endk = find_end_of_inset(document.body, k)
1373 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1374 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1378 n = find_token(document.body, "\\begin_inset Flex URL", n)
1380 endn = find_end_of_inset(document.body, n)
1381 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1382 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1384 # now revert the In_Preamble layouts
1386 i = find_token(document.body, "\\begin_layout Title", 0)
1389 j = find_end_of_layout(document.body, i)
1391 document.warning("Malformed LyX document: Can't find end of Title layout")
1394 content = lyx2latex(document, document.body[i:j + 1])
1395 add_to_preamble(document, ["\\title{" + content + "}"])
1396 del document.body[i:j + 1]
1398 i = find_token(document.body, "\\begin_layout Author", 0)
1401 j = find_end_of_layout(document.body, i)
1403 document.warning("Malformed LyX document: Can't find end of Author layout")
1406 content = lyx2latex(document, document.body[i:j + 1])
1407 add_to_preamble(document, ["\\author{" + content + "}"])
1408 del document.body[i:j + 1]
1410 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1413 j = find_end_of_layout(document.body, i)
1415 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1418 content = lyx2latex(document, document.body[i:j + 1])
1419 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1420 del document.body[i:j + 1]
1422 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1425 j = find_end_of_layout(document.body, i)
1427 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1430 content = lyx2latex(document, document.body[i:j + 1])
1431 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1432 del document.body[i:j + 1]
1434 i = find_token(document.body, "\\begin_layout Short Title", 0)
1437 j = find_end_of_layout(document.body, i)
1439 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1442 content = lyx2latex(document, document.body[i:j + 1])
1443 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1444 del document.body[i:j + 1]
1446 i = find_token(document.body, "\\begin_layout Abstract", 0)
1449 j = find_end_of_layout(document.body, i)
1451 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1454 content = lyx2latex(document, document.body[i:j + 1])
1455 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1456 del document.body[i:j + 1]
1458 i = find_token(document.body, "\\begin_layout Keywords", 0)
1461 j = find_end_of_layout(document.body, i)
1463 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1466 content = lyx2latex(document, document.body[i:j + 1])
1467 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1468 del document.body[i:j + 1]
1470 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1473 j = find_end_of_layout(document.body, i)
1475 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1478 content = lyx2latex(document, document.body[i:j + 1])
1479 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1480 del document.body[i:j + 1]
1482 i = find_token(document.body, "\\begin_layout Address", 0)
1485 j = find_end_of_layout(document.body, i)
1487 document.warning("Malformed LyX document: Can't find end of Address layout")
1490 content = lyx2latex(document, document.body[i:j + 1])
1491 add_to_preamble(document, ["\\Address{" + content + "}"])
1492 del document.body[i:j + 1]
1493 # finally handle the code layouts
1498 while m != -1 or j != -1 or h != -1 or k != -1:
1501 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1503 endh = find_end_of_inset(document.body, h)
1504 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1505 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1506 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1510 j = find_token(document.body, "\\begin_layout Code Input", j)
1512 endj = find_end_of_layout(document.body, j)
1513 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1514 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1515 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1516 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1517 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1521 k = find_token(document.body, "\\begin_layout Code Output", k)
1523 endk = find_end_of_layout(document.body, k)
1524 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1525 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1526 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1527 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1528 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1532 m = find_token(document.body, "\\begin_layout Code", m)
1534 endm = find_end_of_layout(document.body, m)
1535 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1536 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1537 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1538 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1539 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1543 def convert_subref(document):
1544 " converts sub: ref prefixes to subref: "
1547 rx = re.compile(r'^name \"sub:(.+)$')
1550 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1553 j = find_end_of_inset(document.body, i)
1555 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1559 for p in range(i, j):
1560 m = rx.match(document.body[p])
1563 document.body[p] = "name \"subsec:" + label
1567 rx = re.compile(r'^reference \"sub:(.+)$')
1570 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1573 j = find_end_of_inset(document.body, i)
1575 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1579 for p in range(i, j):
1580 m = rx.match(document.body[p])
1583 document.body[p] = "reference \"subsec:" + label
1589 def revert_subref(document):
1590 " reverts subref: ref prefixes to sub: "
1593 rx = re.compile(r'^name \"subsec:(.+)$')
1596 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1599 j = find_end_of_inset(document.body, i)
1601 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1605 for p in range(i, j):
1606 m = rx.match(document.body[p])
1609 document.body[p] = "name \"sub:" + label
1614 rx = re.compile(r'^reference \"subsec:(.+)$')
1617 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1620 j = find_end_of_inset(document.body, i)
1622 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1626 for p in range(i, j):
1627 m = rx.match(document.body[p])
1630 document.body[p] = "reference \"sub:" + label
1635 def convert_nounzip(document):
1636 " remove the noUnzip parameter of graphics insets "
1638 rx = re.compile(r'\s*noUnzip\s*$')
1641 i = find_token(document.body, "\\begin_inset Graphics", i)
1644 j = find_end_of_inset(document.body, i)
1646 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1650 k = find_re(document.body, rx, i, j)
1652 del document.body[k]
1657 def convert_revert_external_bbox(document, forward):
1658 " add units to bounding box of external insets "
1660 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1663 i = find_token(document.body, "\\begin_inset External", i)
1666 j = find_end_of_inset(document.body, i)
1668 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1671 k = find_re(document.body, rx, i, j)
1675 tokens = document.body[k].split()
1677 for t in range(1, 5):
1680 for t in range(1, 5):
1681 tokens[t] = length_in_bp(tokens[t])
1682 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1683 tokens[3] + " " + tokens[4]
1687 def convert_external_bbox(document):
1688 convert_revert_external_bbox(document, True)
1691 def revert_external_bbox(document):
1692 convert_revert_external_bbox(document, False)
1695 def revert_tcolorbox_1(document):
1696 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1699 i = find_token(document.header, "tcolorbox", i)
1705 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1708 flexEnd = find_end_of_inset(document.body, flex)
1709 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1710 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1711 flexEnd = find_end_of_inset(document.body, flex)
1713 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1715 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1716 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1720 def revert_tcolorbox_2(document):
1721 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1724 i = find_token(document.header, "tcolorbox", i)
1730 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1733 flexEnd = find_end_of_inset(document.body, flex)
1734 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1735 flexEnd = find_end_of_inset(document.body, flex)
1736 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1737 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1741 def revert_tcolorbox_3(document):
1742 " Reverts the Flex:Custom_Color_Box_1 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 1", 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{cBoxA}")
1759 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1763 def revert_tcolorbox_4(document):
1764 " Reverts the Flex:Custom_Color_Box_2 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 2", 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{cBoxB}")
1781 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1785 def revert_tcolorbox_5(document):
1786 " Reverts the Flex:Custom_Color_Box_3 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 3", 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{cBoxC}")
1803 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1807 def revert_tcolorbox_6(document):
1808 " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1811 i = find_token(document.header, "tcolorbox", i)
1817 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1820 flexEnd = find_end_of_inset(document.body, flex)
1821 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1822 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1823 flexEnd = find_end_of_inset(document.body, flex)
1824 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1825 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1829 def revert_tcolorbox_7(document):
1830 " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1833 i = find_token(document.header, "tcolorbox", i)
1839 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1842 flexEnd = find_end_of_inset(document.body, flex)
1843 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1844 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1845 flexEnd = find_end_of_inset(document.body, flex)
1846 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1847 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1851 def revert_tcolorbox_8(document):
1852 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1858 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1860 j = find_end_of_layout(document.body, i)
1861 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1862 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1863 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1864 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1866 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1868 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1869 k = find_end_of_inset(document.body, j)
1870 k = find_token(document.body, "\\end_inset", k + 1)
1871 k = find_token(document.body, "\\end_inset", k + 1)
1873 k = find_token(document.body, "\\end_inset", k + 1)
1874 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1875 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1876 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1882 def revert_moderncv_1(document):
1883 " Reverts the new inset of moderncv to TeX-code in preamble "
1885 if document.textclass != "moderncv":
1891 # at first revert the new styles
1893 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1896 j = find_end_of_layout(document.body, i)
1898 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1901 content = lyx2latex(document, document.body[i:j + 1])
1902 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1903 del document.body[i:j + 1]
1905 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1908 j = find_end_of_layout(document.body, i)
1910 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1913 content = lyx2latex(document, document.body[i:j + 1])
1914 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1915 del document.body[i:j + 1]
1916 # now change the new styles to the obsolete ones
1918 i = find_token(document.body, "\\begin_layout Name", 0)
1921 j = find_end_of_layout(document.body, i)
1923 document.warning("Malformed LyX document: Can't find end of Name layout")
1926 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1927 if lineArg > j and j != 0:
1930 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1931 # we have to assure that no other inset is in the Argument
1932 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1933 endInset = find_token(document.body, "\\end_inset", beginPlain)
1936 while beginInset < endInset and beginInset != -1:
1937 beginInset = find_token(document.body, "\\begin_inset", k)
1938 endInset = find_token(document.body, "\\end_inset", l)
1941 Arg2 = document.body[l + 5 : l + 6]
1943 document.body[i : i + 1]= ["\\begin_layout FirstName"]
1944 # delete the Argument inset
1945 del( document.body[endInset - 2 : endInset + 3])
1946 del( document.body[lineArg : beginPlain + 1])
1947 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1950 def revert_moderncv_2(document):
1951 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1953 if document.textclass != "moderncv":
1960 i = find_token(document.body, "\\begin_layout Phone", i)
1963 j = find_end_of_layout(document.body, i)
1965 document.warning("Malformed LyX document: Can't find end of Phone layout")
1968 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1969 if lineArg > j and j != 0:
1973 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1974 # we have to assure that no other inset is in the Argument
1975 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1976 endInset = find_token(document.body, "\\end_inset", beginPlain)
1979 while beginInset < endInset and beginInset != -1:
1980 beginInset = find_token(document.body, "\\begin_inset", k)
1981 endInset = find_token(document.body, "\\end_inset", l)
1984 Arg = document.body[beginPlain + 1 : beginPlain + 2]
1986 if Arg[0] == "mobile":
1987 document.body[i : i + 1]= ["\\begin_layout Mobile"]
1989 document.body[i : i + 1]= ["\\begin_layout Fax"]
1990 # delete the Argument inset
1991 del(document.body[endInset - 2 : endInset + 1])
1992 del(document.body[lineArg : beginPlain + 3])
1996 def convert_moderncv_phone(document):
1997 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
1999 if document.textclass != "moderncv":
2006 "Mobile" : "mobile",
2010 rx = re.compile(r'^\\begin_layout (\S+)$')
2012 # substitute \fax and \mobile by \phone[fax] and \phone[mobile], respectively
2013 i = find_token(document.body, "\\begin_layout", i)
2017 m = rx.match(document.body[i])
2021 if val not in list(phone_dict.keys()):
2024 j = find_end_of_layout(document.body, i)
2026 document.warning("Malformed LyX document: Can't find end of Mobile layout")
2030 document.body[i : i + 1] = ["\\begin_layout Phone", "\\begin_inset Argument 1", "status open", "",
2031 "\\begin_layout Plain Layout", phone_dict[val], "\\end_layout", "",
2035 def convert_moderncv_name(document):
2036 " Convert the FirstName and LastName layout of moderncv to the general Name layout "
2038 if document.textclass != "moderncv":
2041 fnb = 0 # Begin of FirstName inset
2042 fne = 0 # End of FirstName inset
2043 lnb = 0 # Begin of LastName (FamilyName) inset
2044 lne = 0 # End of LastName (FamilyName) inset
2045 nb = 0 # Begin of substituting Name inset
2046 ne = 0 # End of substituting Name inset
2047 FirstName = [] # FirstName content
2048 FamilyName = [] # LastName content
2052 fnb = find_token(document.body, "\\begin_layout FirstName", fnb)
2054 fne = find_end_of_layout(document.body, fnb)
2056 document.warning("Malformed LyX document: Can't find end of FirstName layout")
2058 FirstName = document.body[fnb + 1 : fne]
2060 lnb = find_token(document.body, "\\begin_layout FamilyName", lnb)
2062 lne = find_end_of_layout(document.body, lnb)
2064 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
2066 FamilyName = document.body[lnb + 1 : lne]
2067 # Determine the region for the substituting Name layout
2068 if fnb == -1 and lnb == -1: # Neither FirstName nor FamilyName exists -> Do nothing
2070 elif fnb == -1: # Only FamilyName exists -> New Name insets replaces that
2073 elif lnb == -1: # Only FirstName exists -> New Name insets replaces that
2076 elif fne > lne: # FirstName position before FamilyName -> New Name insets spans
2077 nb = lnb # from FamilyName begin
2078 ne = fne # to FirstName end
2079 else: # FirstName position before FamilyName -> New Name insets spans
2080 nb = fnb # from FirstName begin
2081 ne = lne # to FamilyName end
2083 # Insert the substituting layout now. If FirstName exists, use an otpional argument.
2085 document.body[nb : ne + 1] = ["\\begin_layout Name"] + FamilyName + ["\\end_layout", ""]
2087 document.body[nb : ne + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2088 "\\begin_layout Plain Layout"] + FirstName + ["\\end_layout", "",
2089 "\\end_inset", ""] + FamilyName + ["\\end_layout", ""]
2092 def revert_achemso(document):
2093 " Reverts the flex inset Latin to TeX code "
2095 if document.textclass != "achemso":
2100 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2102 j = find_end_of_inset(document.body, i)
2106 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2107 endPlain = find_end_of_layout(document.body, beginPlain)
2108 content = lyx2latex(document, document.body[beginPlain : endPlain])
2109 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2111 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2116 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2117 "\\font_sf_scale", "\\font_tt_scale"]
2118 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2119 fontquotes = [True, True, True, True, False, False]
2121 def convert_fontsettings(document):
2122 " Duplicate font settings "
2124 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2126 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2127 use_non_tex_fonts = "false"
2129 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2131 for f in fontsettings:
2132 i = find_token(document.header, f + " ", 0)
2134 document.warning("Malformed LyX document: No " + f + "!")
2136 # note that with i = -1, this will insert at the end
2138 value = fontdefaults[j]
2140 value = document.header[i][len(f):].strip()
2142 if use_non_tex_fonts == "true":
2143 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2145 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2147 if use_non_tex_fonts == "true":
2148 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2150 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2154 def revert_fontsettings(document):
2155 " Merge font settings "
2157 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2159 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2160 use_non_tex_fonts = "false"
2162 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2164 for f in fontsettings:
2165 i = find_token(document.header, f + " ", 0)
2167 document.warning("Malformed LyX document: No " + f + "!")
2170 line = get_value(document.header, f, i)
2173 q2 = line.find('"', q1+1)
2174 q3 = line.find('"', q2+1)
2175 q4 = line.find('"', q3+1)
2176 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2177 document.warning("Malformed LyX document: Missing quotes!")
2180 if use_non_tex_fonts == "true":
2181 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2183 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2185 if use_non_tex_fonts == "true":
2186 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2188 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2192 def revert_solution(document):
2193 " Reverts the solution environment of the theorem module to TeX code "
2195 # Do we use one of the modules that provides Solution?
2197 mods = document.get_module_list()
2199 if mod == "theorems-std" or mod == "theorems-bytype" \
2200 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2210 i = find_token(document.body, "\\begin_layout Solution", i)
2214 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2215 if is_starred == True:
2217 LyXName = "Solution*"
2218 theoremName = "newtheorem*"
2221 LyXName = "Solution"
2222 theoremName = "newtheorem"
2224 j = find_end_of_layout(document.body, i)
2226 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2230 # if this is not a consecutive env, add start command
2233 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2235 # has this a consecutive theorem of same type?
2236 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2238 # if this is not followed by a consecutive env, add end command
2240 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2242 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2244 add_to_preamble(document, "\\theoremstyle{definition}")
2245 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2246 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2247 (theoremName, LaTeXName))
2248 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2249 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2250 (theoremName, LaTeXName))
2252 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2256 def revert_verbatim_star(document):
2257 from lyx_2_1 import revert_verbatim
2258 revert_verbatim(document, True)
2261 def convert_save_props(document):
2262 " Add save_transient_properties parameter. "
2263 i = find_token(document.header, '\\begin_header', 0)
2265 document.warning("Malformed lyx document: Missing '\\begin_header'.")
2267 document.header.insert(i + 1, '\\save_transient_properties true')
2270 def revert_save_props(document):
2271 " Remove save_transient_properties parameter. "
2272 i = find_token(document.header, "\\save_transient_properties", 0)
2275 del document.header[i]
2278 def convert_info_tabular_feature(document):
2280 return arg.replace("inset-modify tabular", "tabular-feature")
2281 convert_info_insets(document, "shortcut(s)?|icon", f)
2284 def revert_info_tabular_feature(document):
2286 return arg.replace("tabular-feature", "inset-modify tabular")
2287 convert_info_insets(document, "shortcut(s)?|icon", f)
2294 supported_versions = ["2.2.0", "2.2"]
2296 [475, [convert_separator]],
2297 # nothing to do for 476: We consider it a bug that older versions
2298 # did not load amsmath automatically for these commands, and do not
2299 # want to hardcode amsmath off.
2305 [481, [convert_dashes]],
2306 [482, [convert_phrases]],
2307 [483, [convert_specialchar]],
2312 [488, [convert_newgloss]],
2313 [489, [convert_BoxFeatures]],
2314 [490, [convert_origin]],
2316 [492, [convert_colorbox]],
2319 [495, [convert_subref]],
2320 [496, [convert_nounzip]],
2321 [497, [convert_external_bbox]],
2323 [499, [convert_moderncv_phone, convert_moderncv_name]],
2325 [501, [convert_fontsettings]],
2328 [504, [convert_save_props]],
2330 [506, [convert_info_tabular_feature]],
2331 [507, [convert_longtable_label]],
2332 [508, [convert_parbreak]]
2336 [507, [revert_parbreak]],
2337 [506, [revert_longtable_label]],
2338 [505, [revert_info_tabular_feature]],
2340 [503, [revert_save_props]],
2341 [502, [revert_verbatim_star]],
2342 [501, [revert_solution]],
2343 [500, [revert_fontsettings]],
2344 [499, [revert_achemso]],
2345 [498, [revert_moderncv_1, revert_moderncv_2]],
2346 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2347 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2348 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2349 [496, [revert_external_bbox]],
2350 [495, []], # nothing to do since the noUnzip parameter was optional
2351 [494, [revert_subref]],
2352 [493, [revert_jss]],
2353 [492, [revert_mathmulticol]],
2354 [491, [revert_colorbox]],
2355 [490, [revert_textcolor]],
2356 [489, [revert_origin]],
2357 [488, [revert_BoxFeatures]],
2358 [487, [revert_newgloss, revert_glossgroup]],
2359 [486, [revert_forest]],
2360 [485, [revert_ex_itemargs]],
2361 [484, [revert_sigplan_doi]],
2362 [483, [revert_georgian]],
2363 [482, [revert_specialchar]],
2364 [481, [revert_phrases]],
2365 [480, [revert_dashes]],
2366 [479, [revert_question_env]],
2367 [478, [revert_beamer_lemma]],
2368 [477, [revert_xarrow]],
2369 [476, [revert_swissgerman]],
2370 [475, [revert_smash]],
2371 [474, [revert_separator]]
2375 if __name__ == "__main__":