1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # Copyright (C) 2015 The LyX team
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 """ Convert files to the file format generated by lyx 2.2"""
25 # Uncomment only what you need to import, please.
27 #from parser_tools import find_token, find_end_of, find_tokens, \
28 # find_token_exact, find_end_of_inset, find_end_of_layout, \
29 # find_token_backwards, is_in_inset, get_value, get_quoted_value, \
30 # del_token, check_token, get_option_value
32 from lyx2lyx_tools import add_to_preamble, put_cmd_in_ert, get_ert, lyx2latex, \
33 lyx2verbatim, length_in_bp, convert_info_insets
34 # insert_to_preamble, latex_length, revert_flex_inset, \
35 # revert_font_attrs, hex2ratio, str2bool
37 from parser_tools import find_token, find_token_backwards, find_re, \
38 find_end_of_inset, find_end_of_layout, find_nonempty_line, \
39 get_containing_layout, get_value, check_token
41 ####################################################################
42 # Private helper functions
44 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt, nolastopt):
46 Reverts an InsetArgument to TeX-code
48 revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt, notLastOpt)
49 LineOfBegin is the line of the \begin_layout or \begin_inset statement
50 LineOfEnd is the line of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
51 StartArgument is the number of the first argument that needs to be converted
52 EndArgument is the number of the last argument that needs to be converted or the last defined one
53 isEnvironment must be true, if the layout is for a LaTeX environment
54 isOpt must be true, if the argument is an optional one
55 notLastOpt must be true if the argument is mandatory and followed by optional ones
59 while lineArg != -1 and n < nmax + 1:
60 lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
61 if lineArg > endline and endline != 0:
64 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
65 # we have to assure that no other inset is in the Argument
66 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
67 endInset = find_token(document.body, "\\end_inset", beginPlain)
70 while beginInset < endInset and beginInset != -1:
71 beginInset = find_token(document.body, "\\begin_inset", k)
72 endInset = find_token(document.body, "\\end_inset", l)
75 if environment == False:
77 if nolastopt == False:
78 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
80 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
81 del(document.body[lineArg : beginPlain + 1])
84 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
85 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
89 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
90 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
93 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
94 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
100 ###############################################################################
102 ### Conversion and reversion routines
104 ###############################################################################
106 def convert_longtable_label_internal(document, forward):
108 Convert reference to "LongTableNoNumber" into "Unnumbered" if forward is True
111 old_reference = "\\begin_inset Caption LongTableNoNumber"
112 new_reference = "\\begin_inset Caption Unnumbered"
114 # if the purpose is to revert swap the strings roles
116 old_reference, new_reference = new_reference, old_reference
120 i = find_token(document.body, old_reference, i)
125 document.body[i] = new_reference
128 def convert_longtable_label(document):
129 convert_longtable_label_internal(document, True)
132 def revert_longtable_label(document):
133 convert_longtable_label_internal(document, False)
136 def convert_separator(document):
138 Convert layout separators to separator insets and add (LaTeX) paragraph
139 breaks in order to mimic previous LaTeX export.
142 parins = ["\\begin_inset Separator parbreak", "\\end_inset", ""]
143 parlay = ["\\begin_layout Standard", "\\begin_inset Separator parbreak",
144 "\\end_inset", "", "\\end_layout", ""]
146 "family" : "default",
147 "series" : "default",
156 i = find_token(document.body, "\\begin_deeper", i)
160 j = find_token_backwards(document.body, "\\end_layout", i-1)
162 # reset any text style before inserting the inset
163 lay = get_containing_layout(document.body, j-1)
165 content = "\n".join(document.body[lay[1]:lay[2]])
166 for val in list(sty_dict.keys()):
167 if content.find("\\%s" % val) != -1:
168 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
171 document.body[j:j] = parins
172 i = i + len(parins) + 1
178 i = find_token(document.body, "\\align", i)
182 lay = get_containing_layout(document.body, i)
183 if lay != False and lay[0] == "Plain Layout":
187 j = find_token_backwards(document.body, "\\end_layout", i-1)
189 lay = get_containing_layout(document.body, j-1)
190 if lay != False and lay[0] == "Standard" \
191 and find_token(document.body, "\\align", lay[1], lay[2]) == -1 \
192 and find_token(document.body, "\\begin_inset VSpace", lay[1], lay[2]) == -1:
193 # reset any text style before inserting the inset
194 content = "\n".join(document.body[lay[1]:lay[2]])
195 for val in list(sty_dict.keys()):
196 if content.find("\\%s" % val) != -1:
197 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
200 document.body[j:j] = parins
201 i = i + len(parins) + 1
207 regexp = re.compile(r'^\\begin_layout (?:(-*)|(\s*))(Separator|EndOfSlide)(?:(-*)|(\s*))$', re.IGNORECASE)
211 i = find_re(document.body, regexp, i)
215 j = find_end_of_layout(document.body, i)
217 document.warning("Malformed LyX document: Missing `\\end_layout'.")
220 lay = get_containing_layout(document.body, j-1)
222 lines = document.body[lay[3]:lay[2]]
226 document.body[i:j+1] = parlay
228 document.body[i+1:i+1] = lines
230 i = i + len(parlay) + len(lines) + 1
233 def revert_separator(document):
234 " Revert separator insets to layout separators "
236 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
237 if document.textclass in beamer_classes:
238 beglaysep = "\\begin_layout Separator"
240 beglaysep = "\\begin_layout --Separator--"
242 parsep = [beglaysep, "", "\\end_layout", ""]
243 comert = ["\\begin_inset ERT", "status collapsed", "",
244 "\\begin_layout Plain Layout", "%", "\\end_layout",
245 "", "\\end_inset", ""]
246 empert = ["\\begin_inset ERT", "status collapsed", "",
247 "\\begin_layout Plain Layout", " ", "\\end_layout",
248 "", "\\end_inset", ""]
252 i = find_token(document.body, "\\begin_inset Separator", i)
256 lay = get_containing_layout(document.body, i)
258 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
265 kind = get_value(document.body, "\\begin_inset Separator", i, i+1, "plain").split()[1]
266 before = document.body[beg+1:i]
267 something_before = len(before) > 0 and len("".join(before)) > 0
268 j = find_end_of_inset(document.body, i)
269 after = document.body[j+1:end]
270 something_after = len(after) > 0 and len("".join(after)) > 0
272 beg = beg + len(before) + 1
273 elif something_before:
274 document.body[i:i] = ["\\end_layout", ""]
282 document.body[beg:j+1] = empert
285 document.body[beg:j+1] = comert
289 if layoutname == "Standard":
290 if not something_before:
291 document.body[beg:j+1] = parsep
293 document.body[i:i] = ["", "\\begin_layout Standard"]
296 document.body[beg:j+1] = ["\\begin_layout Standard"]
299 document.body[beg:j+1] = ["\\begin_deeper"]
301 end = end + 1 - (j + 1 - beg)
302 if not something_before:
303 document.body[i:i] = parsep
305 end = end + len(parsep)
306 document.body[i:i] = ["\\begin_layout Standard"]
307 document.body[end+2:end+2] = ["", "\\end_deeper", ""]
310 next_par_is_aligned = False
311 k = find_nonempty_line(document.body, end+1)
312 if k != -1 and check_token(document.body[k], "\\begin_layout"):
313 lay = get_containing_layout(document.body, k)
314 next_par_is_aligned = lay != False and \
315 find_token(document.body, "\\align", lay[1], lay[2]) != -1
316 if k != -1 and not next_par_is_aligned \
317 and not check_token(document.body[k], "\\end_deeper") \
318 and not check_token(document.body[k], "\\begin_deeper"):
319 if layoutname == "Standard":
320 document.body[beg:j+1] = [beglaysep]
323 document.body[beg:j+1] = ["\\begin_deeper", beglaysep]
324 end = end + 2 - (j + 1 - beg)
325 document.body[end+1:end+1] = ["", "\\end_deeper", ""]
329 del document.body[i:end+1]
331 del document.body[i:end-1]
336 def convert_parbreak(document):
338 Convert parbreak separators not specifically used to separate
339 environments to latexpar separators.
341 parbreakinset = "\\begin_inset Separator parbreak"
344 i = find_token(document.body, parbreakinset, i)
347 lay = get_containing_layout(document.body, i)
349 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
352 if lay[0] == "Standard":
353 # Convert only if not alone in the paragraph
354 k1 = find_nonempty_line(document.body, lay[1] + 1, i + 1)
355 k2 = find_nonempty_line(document.body, i + 1, lay[2])
356 if (k1 < i) or (k2 > i + 1) or not check_token(document.body[i], parbreakinset):
357 document.body[i] = document.body[i].replace("parbreak", "latexpar")
359 document.body[i] = document.body[i].replace("parbreak", "latexpar")
363 def revert_parbreak(document):
365 Revert latexpar separators to parbreak separators.
369 i = find_token(document.body, "\\begin_inset Separator latexpar", i)
372 document.body[i] = document.body[i].replace("latexpar", "parbreak")
376 def revert_smash(document):
377 " Set amsmath to on if smash commands are used "
379 commands = ["smash[t]", "smash[b]", "notag"]
380 i = find_token(document.header, "\\use_package amsmath", 0)
382 document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
384 value = get_value(document.header, "\\use_package amsmath", i).split()[1]
386 # nothing to do if package is not auto but on or off
390 j = find_token(document.body, '\\begin_inset Formula', j)
393 k = find_end_of_inset(document.body, j)
395 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
398 code = "\n".join(document.body[j:k])
400 if code.find("\\%s" % c) != -1:
401 # set amsmath to on, since it is loaded by the newer format
402 document.header[i] = "\\use_package amsmath 2"
407 def revert_swissgerman(document):
408 " Set language german-ch-old to german "
410 if document.language == "german-ch-old":
411 document.language = "german"
412 i = find_token(document.header, "\\language", 0)
414 document.header[i] = "\\language german"
417 j = find_token(document.body, "\\lang german-ch-old", j)
420 document.body[j] = document.body[j].replace("\\lang german-ch-old", "\\lang german")
424 def revert_use_package(document, pkg, commands, oldauto, supported):
425 # oldauto defines how the version we are reverting to behaves:
426 # if it is true, the old version uses the package automatically.
427 # if it is false, the old version never uses the package.
428 # If "supported" is true, the target version also supports this
430 regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
431 p = find_re(document.header, regexp, 0)
432 value = "1" # default is auto
434 value = get_value(document.header, "\\use_package" , p).split()[1]
436 del document.header[p]
437 if value == "2" and not supported: # on
438 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
439 elif value == "1" and not oldauto: # auto
442 i = find_token(document.body, '\\begin_inset Formula', i)
445 j = find_end_of_inset(document.body, i)
447 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
450 code = "\n".join(document.body[i:j])
452 if code.find("\\%s" % c) != -1:
454 document.header[p] = "\\use_package " + pkg + " 2"
456 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
461 mathtools_commands = ["xhookrightarrow", "xhookleftarrow", "xRightarrow", \
462 "xrightharpoondown", "xrightharpoonup", "xrightleftharpoons", \
463 "xLeftarrow", "xleftharpoondown", "xleftharpoonup", \
464 "xleftrightarrow", "xLeftrightarrow", "xleftrightharpoons", \
467 def revert_xarrow(document):
468 "remove use_package mathtools"
469 revert_use_package(document, "mathtools", mathtools_commands, False, True)
472 def revert_beamer_lemma(document):
473 " Reverts beamer lemma layout to ERT "
475 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
476 if document.textclass not in beamer_classes:
482 i = find_token(document.body, "\\begin_layout Lemma", i)
485 j = find_end_of_layout(document.body, i)
487 document.warning("Malformed LyX document: Can't find end of Lemma layout")
490 arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j)
491 endarg1 = find_end_of_inset(document.body, arg1)
492 arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j)
493 endarg2 = find_end_of_inset(document.body, arg2)
497 beginPlain1 = find_token(document.body, "\\begin_layout Plain Layout", arg1, endarg1)
498 if beginPlain1 == -1:
499 document.warning("Malformed LyX document: Can't find arg1 plain Layout")
502 endPlain1 = find_end_of_inset(document.body, beginPlain1)
503 content1 = document.body[beginPlain1 + 1 : endPlain1 - 2]
504 subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">")
506 beginPlain2 = find_token(document.body, "\\begin_layout Plain Layout", arg2, endarg2)
507 if beginPlain2 == -1:
508 document.warning("Malformed LyX document: Can't find arg2 plain Layout")
511 endPlain2 = find_end_of_inset(document.body, beginPlain2)
512 content2 = document.body[beginPlain2 + 1 : endPlain2 - 2]
513 subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]")
517 del document.body[arg2 : endarg2 + 1]
519 del document.body[arg1 : endarg1 + 1]
521 del document.body[arg1 : endarg1 + 1]
523 del document.body[arg2 : endarg2 + 1]
525 # index of end layout has probably changed
526 j = find_end_of_layout(document.body, i)
528 document.warning("Malformed LyX document: Can't find end of Lemma layout")
534 # if this is not a consecutive env, add start command
536 begcmd = put_cmd_in_ert("\\begin{lemma}")
538 # has this a consecutive lemma?
539 consecutive = document.body[j + 2] == "\\begin_layout Lemma"
541 # if this is not followed by a consecutive env, add end command
543 document.body[j : j + 1] = put_cmd_in_ert("\\end{lemma}") + ["\\end_layout"]
545 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd + subst1 + subst2
551 def revert_question_env(document):
553 Reverts question and question* environments of
554 theorems-ams-extended-bytype module to ERT
557 # Do we use theorems-ams-extended-bytype module?
558 if not "theorems-ams-extended-bytype" in document.get_module_list():
564 i = find_token(document.body, "\\begin_layout Question", i)
568 starred = document.body[i] == "\\begin_layout Question*"
570 j = find_end_of_layout(document.body, i)
572 document.warning("Malformed LyX document: Can't find end of Question layout")
576 # if this is not a consecutive env, add start command
580 begcmd = put_cmd_in_ert("\\begin{question*}")
582 begcmd = put_cmd_in_ert("\\begin{question}")
584 # has this a consecutive theorem of same type?
587 consecutive = document.body[j + 2] == "\\begin_layout Question*"
589 consecutive = document.body[j + 2] == "\\begin_layout Question"
591 # if this is not followed by a consecutive env, add end command
594 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
596 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
598 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
600 add_to_preamble(document, "\\providecommand{\questionname}{Question}")
603 add_to_preamble(document, "\\theoremstyle{plain}\n" \
604 "\\newtheorem*{question*}{\\protect\\questionname}")
606 add_to_preamble(document, "\\theoremstyle{plain}\n" \
607 "\\newtheorem{question}{\\protect\\questionname}")
612 def convert_dashes(document):
613 "convert -- and --- to \\twohyphens and \\threehyphens"
615 if document.backend != "latex":
619 while i < len(document.body):
620 words = document.body[i].split()
621 if len(words) > 1 and words[0] == "\\begin_inset" and \
622 words[1] in ["CommandInset", "ERT", "External", "Formula", "FormulaMacro", "Graphics", "IPA", "listings"]:
623 # must not replace anything in insets that store LaTeX contents in .lyx files
624 # (math and command insets withut overridden read() and write() methods
625 # filtering out IPA makes Text::readParToken() more simple
626 # skip ERT as well since it is not needed there
627 j = find_end_of_inset(document.body, i)
629 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
634 if len(words) > 0 and words[0] in ["\\leftindent", "\\paragraph_spacing", "\\align", "\\labelwidthstring"]:
635 # skip paragraph parameters (bug 10243)
639 j = document.body[i].find("--")
642 front = document.body[i][:j]
643 back = document.body[i][j+2:]
644 # We can have an arbitrary number of consecutive hyphens.
645 # These must be split into the corresponding number of two and three hyphens
646 # We must match what LaTeX does: First try emdash, then endash, then single hyphen
647 if back.find("-") == 0:
650 document.body.insert(i+1, back)
651 document.body[i] = front + "\\threehyphens"
654 document.body.insert(i+1, back)
655 document.body[i] = front + "\\twohyphens"
659 def revert_dashes(document):
660 "convert \\twohyphens and \\threehyphens to -- and ---"
663 while i < len(document.body):
664 words = document.body[i].split()
665 if len(words) > 1 and words[0] == "\\begin_inset" and \
666 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
668 j = find_end_of_inset(document.body, i)
670 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
676 if document.body[i].find("\\twohyphens") >= 0:
677 document.body[i] = document.body[i].replace("\\twohyphens", "--")
679 if document.body[i].find("\\threehyphens") >= 0:
680 document.body[i] = document.body[i].replace("\\threehyphens", "---")
682 if replaced and i+1 < len(document.body) and \
683 (document.body[i+1].find("\\") != 0 or \
684 document.body[i+1].find("\\twohyphens") == 0 or
685 document.body[i+1].find("\\threehyphens") == 0) and \
686 len(document.body[i]) + len(document.body[i+1]) <= 80:
687 document.body[i] = document.body[i] + document.body[i+1]
688 document.body[i+1:i+2] = []
693 # order is important for the last three!
694 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
696 def is_part_of_converted_phrase(line, j, phrase):
697 "is phrase part of an already converted phrase?"
699 converted = "\\SpecialCharNoPassThru \\" + p
700 pos = j + len(phrase) - len(converted)
702 if line[pos:pos+len(converted)] == converted:
707 def convert_phrases(document):
708 "convert special phrases from plain text to \\SpecialCharNoPassThru"
710 if document.backend != "latex":
713 for phrase in phrases:
715 while i < len(document.body):
716 words = document.body[i].split()
717 if len(words) > 1 and words[0] == "\\begin_inset" and \
718 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
719 # must not replace anything in insets that store LaTeX contents in .lyx files
720 # (math and command insets withut overridden read() and write() methods
721 j = find_end_of_inset(document.body, i)
723 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
728 if document.body[i].find("\\") == 0:
731 j = document.body[i].find(phrase)
735 if not is_part_of_converted_phrase(document.body[i], j, phrase):
736 front = document.body[i][:j]
737 back = document.body[i][j+len(phrase):]
739 document.body.insert(i+1, back)
740 # We cannot use SpecialChar since we do not know whether we are outside passThru
741 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
745 def revert_phrases(document):
746 "convert special phrases to plain text"
749 while i < len(document.body):
750 words = document.body[i].split()
751 if len(words) > 1 and words[0] == "\\begin_inset" and \
752 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
753 # see convert_phrases
754 j = find_end_of_inset(document.body, i)
756 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
762 for phrase in phrases:
763 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
764 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
765 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
767 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
768 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
770 if replaced and i+1 < len(document.body) and \
771 (document.body[i+1].find("\\") != 0 or \
772 document.body[i+1].find("\\SpecialChar") == 0) and \
773 len(document.body[i]) + len(document.body[i+1]) <= 80:
774 document.body[i] = document.body[i] + document.body[i+1]
775 document.body[i+1:i+2] = []
780 def convert_specialchar_internal(document, forward):
781 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
782 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
783 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
784 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
785 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
786 "\\LaTeX":"LaTeX" # must be after LaTeX2e
790 while i < len(document.body):
791 words = document.body[i].split()
792 if len(words) > 1 and words[0] == "\\begin_inset" and \
793 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
794 # see convert_phrases
795 j = find_end_of_inset(document.body, i)
797 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
802 for key, value in specialchars.items():
804 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
805 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
807 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
808 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
812 def convert_specialchar(document):
813 "convert special characters to new syntax"
814 convert_specialchar_internal(document, True)
817 def revert_specialchar(document):
818 "convert special characters to old syntax"
819 convert_specialchar_internal(document, False)
822 def revert_georgian(document):
823 "Set the document language to English but assure Georgian output"
825 if document.language == "georgian":
826 document.language = "english"
827 i = find_token(document.header, "\\language georgian", 0)
829 document.header[i] = "\\language english"
830 j = find_token(document.header, "\\language_package default", 0)
832 document.header[j] = "\\language_package babel"
833 k = find_token(document.header, "\\options", 0)
835 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
837 l = find_token(document.header, "\\use_default_options", 0)
838 document.header.insert(l + 1, "\\options georgian")
841 def revert_sigplan_doi(document):
842 " Reverts sigplanconf DOI layout to ERT "
844 if document.textclass != "sigplanconf":
849 i = find_token(document.body, "\\begin_layout DOI", i)
852 j = find_end_of_layout(document.body, i)
854 document.warning("Malformed LyX document: Can't find end of DOI layout")
858 content = lyx2latex(document, document.body[i:j + 1])
859 add_to_preamble(document, ["\\doi{" + content + "}"])
860 del document.body[i:j + 1]
864 def revert_ex_itemargs(document):
865 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
867 if not "linguistics" in document.get_module_list():
871 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
873 i = find_token(document.body, "\\begin_inset Argument item:", i)
876 j = find_end_of_inset(document.body, i)
877 # Find containing paragraph layout
878 parent = get_containing_layout(document.body, i)
880 document.warning("Malformed LyX document: Can't find parent paragraph layout")
884 layoutname = parent[0]
885 if layoutname in example_layouts:
886 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
887 endPlain = find_end_of_layout(document.body, beginPlain)
888 content = document.body[beginPlain + 1 : endPlain]
889 del document.body[i:j+1]
890 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
891 document.body[parbeg : parbeg] = subst
895 def revert_forest(document):
896 " Reverts the forest environment (Linguistics module) to TeX-code "
898 if not "linguistics" in document.get_module_list():
903 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
906 j = find_end_of_inset(document.body, i)
908 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
912 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
913 endPlain = find_end_of_layout(document.body, beginPlain)
914 content = lyx2latex(document, document.body[beginPlain : endPlain])
916 add_to_preamble(document, ["\\usepackage{forest}"])
918 document.body[i:j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
922 def revert_glossgroup(document):
923 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
925 if not "linguistics" in document.get_module_list():
930 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
933 j = find_end_of_inset(document.body, i)
935 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
939 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
940 endPlain = find_end_of_layout(document.body, beginPlain)
941 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
943 document.body[i:j + 1] = ["{", "", content, "", "}"]
947 def revert_newgloss(document):
948 " Reverts the new Glosse insets (Linguistics module) to the old format "
950 if not "linguistics" in document.get_module_list():
953 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
954 for glosse in glosses:
957 i = find_token(document.body, glosse, i)
960 j = find_end_of_inset(document.body, i)
962 document.warning("Malformed LyX document: Can't find end of Glosse inset")
966 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
967 endarg = find_end_of_inset(document.body, arg)
970 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
971 if argbeginPlain == -1:
972 document.warning("Malformed LyX document: Can't find arg plain Layout")
975 argendPlain = find_end_of_inset(document.body, argbeginPlain)
976 argcontent = lyx2verbatim(document, document.body[argbeginPlain : argendPlain - 2])
978 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
979 argcontent, "\\end_layout"]
981 # remove Arg insets and paragraph, if it only contains this inset
982 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
983 del document.body[arg - 1 : endarg + 4]
985 del document.body[arg : endarg + 1]
987 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
988 endPlain = find_end_of_layout(document.body, beginPlain)
989 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
991 document.body[beginPlain + 1:endPlain] = [content]
994 # Dissolve ERT insets
995 for glosse in glosses:
998 i = find_token(document.body, glosse, i)
1001 j = find_end_of_inset(document.body, i)
1003 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1007 ert = find_token(document.body, "\\begin_inset ERT", i, j)
1010 ertend = find_end_of_inset(document.body, ert)
1012 document.warning("Malformed LyX document: Can't find end of ERT inset")
1015 ertcontent = get_ert(document.body, ert, True)
1016 document.body[ert : ertend + 1] = [ertcontent]
1020 def convert_newgloss(document):
1021 " Converts Glosse insets (Linguistics module) to the new format "
1023 if not "linguistics" in document.get_module_list():
1026 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1027 for glosse in glosses:
1030 i = find_token(document.body, glosse, i)
1033 j = find_end_of_inset(document.body, i)
1035 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1042 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
1043 if beginPlain == -1:
1045 endPlain = find_end_of_layout(document.body, beginPlain)
1047 document.warning("Malformed LyX document: Can't find end of Glosse layout")
1051 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
1052 if glt != -1 and document.body[glt + 1].startswith("glt"):
1053 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1054 argcontent = document.body[glt + 1 : endPlain]
1055 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
1056 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
1057 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
1058 "\\end_layout", "", "\\end_inset"]
1060 content = document.body[beginPlain + 1 : endPlain]
1061 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
1062 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
1064 endPlain = find_end_of_layout(document.body, beginPlain)
1066 j = find_end_of_inset(document.body, i)
1071 def convert_BoxFeatures(document):
1072 " adds new box features "
1076 i = find_token(document.body, "height_special", i)
1079 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1083 def revert_BoxFeatures(document):
1084 " outputs new box features as TeX code "
1088 defaultThick = "0.4pt"
1089 defaultShadow = "4pt"
1091 i = find_token(document.body, "height_special", i)
1094 # read out the values
1095 beg = document.body[i+1].find('"');
1096 end = document.body[i+1].rfind('"');
1097 thickness = document.body[i+1][beg+1:end];
1098 beg = document.body[i+2].find('"');
1099 end = document.body[i+2].rfind('"');
1100 separation = document.body[i+2][beg+1:end];
1101 beg = document.body[i+3].find('"');
1102 end = document.body[i+3].rfind('"');
1103 shadowsize = document.body[i+3][beg+1:end];
1104 # delete the specification
1105 del document.body[i+1:i+4]
1107 # first output the closing brace
1108 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1109 document.body[i + 10 : i + 10] = put_cmd_in_ert("}")
1110 # now output the lengths
1111 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1112 document.body[i - 10 : i - 10] = put_cmd_in_ert("{")
1113 if thickness != defaultThick:
1114 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness]
1115 if separation != defaultSep and thickness == defaultThick:
1116 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation]
1117 if separation != defaultSep and thickness != defaultThick:
1118 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1119 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1120 document.body[i - 5 : i - 4] = ["{\\backslash shadowsize " + shadowsize]
1121 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1122 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1123 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1124 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1125 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1126 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1130 def convert_origin(document):
1131 " Insert the origin tag "
1133 i = find_token(document.header, "\\textclass ", 0)
1135 document.warning("Malformed LyX document: No \\textclass!!")
1137 if document.dir == u'':
1141 if document.systemlyxdir and document.systemlyxdir != u'':
1143 if os.path.isabs(document.dir):
1144 absdir = os.path.normpath(document.dir)
1146 absdir = os.path.normpath(os.path.abspath(document.dir))
1147 if os.path.isabs(document.systemlyxdir):
1148 abssys = os.path.normpath(document.systemlyxdir)
1150 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1151 relpath = os.path.relpath(absdir, abssys)
1152 if relpath.find(u'..') == 0:
1157 origin = document.dir.replace(u'\\', u'/') + u'/'
1159 origin = os.path.join(u"/systemlyxdir", relpath).replace(u'\\', u'/') + u'/'
1160 document.header[i:i] = ["\\origin " + origin]
1163 def revert_origin(document):
1164 " Remove the origin tag "
1166 i = find_token(document.header, "\\origin ", 0)
1168 document.warning("Malformed LyX document: No \\origin!!")
1170 del document.header[i]
1173 color_names = ["brown", "darkgray", "gray", \
1174 "lightgray", "lime", "olive", "orange", \
1175 "pink", "purple", "teal", "violet"]
1177 def revert_textcolor(document):
1178 " revert new \\textcolor colors to TeX code "
1184 i = find_token(document.body, "\\color ", i)
1188 for color in list(color_names):
1189 if document.body[i] == "\\color " + color:
1190 # register that xcolor must be loaded in the preamble
1193 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1194 # find the next \\color and/or the next \\end_layout
1195 j = find_token(document.body, "\\color", i + 1)
1196 k = find_token(document.body, "\\end_layout", i + 1)
1197 if j == -1 and k != -1:
1200 # first output the closing brace
1202 document.body[k: k] = put_cmd_in_ert("}")
1204 document.body[j: j] = put_cmd_in_ert("}")
1205 # now output the \textcolor command
1206 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1210 def convert_colorbox(document):
1211 " adds color settings for boxes "
1215 i = find_token(document.body, "shadowsize", i)
1218 document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1222 def revert_colorbox(document):
1223 " outputs color settings for boxes as TeX code "
1226 defaultframecolor = "black"
1227 defaultbackcolor = "none"
1229 binset = find_token(document.body, "\\begin_inset Box", binset)
1233 einset = find_end_of_inset(document.body, binset)
1235 document.warning("Malformed LyX document: Can't find end of box inset!")
1239 blay = find_token(document.body, "\\begin_layout", binset, einset)
1241 document.warning("Malformed LyX document: Can't find start of layout!")
1245 # doing it this way, we make sure only to find a framecolor option
1246 frame = find_token(document.body, "framecolor", binset, blay)
1251 beg = document.body[frame].find('"')
1252 end = document.body[frame].rfind('"')
1253 framecolor = document.body[frame][beg + 1 : end]
1255 # this should be on the next line
1257 beg = document.body[bgcolor].find('"')
1258 end = document.body[bgcolor].rfind('"')
1259 backcolor = document.body[bgcolor][beg + 1 : end]
1262 del document.body[frame : frame + 2]
1263 # adjust end of inset
1266 if document.body[binset] == "\\begin_inset Box Boxed" and \
1267 framecolor != defaultframecolor:
1268 document.body[binset] = "\\begin_inset Box Frameless"
1271 # first output the closing brace
1272 if framecolor == defaultframecolor and backcolor == defaultbackcolor:
1276 # we also neeed to load xcolor in the preamble but only once
1277 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1278 document.body[einset + 2 : einset + 2] = put_cmd_in_ert("}")
1279 if framecolor != defaultframecolor:
1280 document.body[binset:binset] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1282 document.body[binset:binset] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
1287 def revert_mathmulticol(document):
1288 " Convert formulas to ERT if they contain multicolumns "
1292 i = find_token(document.body, '\\begin_inset Formula', i)
1295 j = find_end_of_inset(document.body, i)
1297 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1300 lines = document.body[i:j]
1301 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1302 code = "\n".join(lines)
1307 n = code.find("\\multicolumn", k)
1308 # no need to convert degenerated multicolumn cells,
1309 # they work in old LyX versions as "math ERT"
1310 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1311 ert = put_cmd_in_ert(code)
1312 document.body[i:j+1] = ert
1318 i = find_end_of_inset(document.body, i)
1323 def revert_jss(document):
1324 " Reverts JSS In_Preamble commands to ERT in preamble "
1326 if document.textclass != "jss":
1335 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1336 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1339 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1341 endh = find_end_of_inset(document.body, h)
1342 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1343 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1347 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1349 endm = find_end_of_inset(document.body, m)
1350 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1351 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1355 j = find_token(document.body, "\\begin_inset Flex Code", j)
1357 # assure that we are not in a Code Chunk inset
1358 if document.body[j][-1] == "e":
1359 endj = find_end_of_inset(document.body, j)
1360 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1361 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1367 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1369 endk = find_end_of_inset(document.body, k)
1370 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1371 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1375 n = find_token(document.body, "\\begin_inset Flex URL", n)
1377 endn = find_end_of_inset(document.body, n)
1378 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1379 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1381 # now revert the In_Preamble layouts
1383 i = find_token(document.body, "\\begin_layout Title", 0)
1386 j = find_end_of_layout(document.body, i)
1388 document.warning("Malformed LyX document: Can't find end of Title layout")
1391 content = lyx2latex(document, document.body[i:j + 1])
1392 add_to_preamble(document, ["\\title{" + content + "}"])
1393 del document.body[i:j + 1]
1395 i = find_token(document.body, "\\begin_layout Author", 0)
1398 j = find_end_of_layout(document.body, i)
1400 document.warning("Malformed LyX document: Can't find end of Author layout")
1403 content = lyx2latex(document, document.body[i:j + 1])
1404 add_to_preamble(document, ["\\author{" + content + "}"])
1405 del document.body[i:j + 1]
1407 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1410 j = find_end_of_layout(document.body, i)
1412 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1415 content = lyx2latex(document, document.body[i:j + 1])
1416 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1417 del document.body[i:j + 1]
1419 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1422 j = find_end_of_layout(document.body, i)
1424 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1427 content = lyx2latex(document, document.body[i:j + 1])
1428 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1429 del document.body[i:j + 1]
1431 i = find_token(document.body, "\\begin_layout Short Title", 0)
1434 j = find_end_of_layout(document.body, i)
1436 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1439 content = lyx2latex(document, document.body[i:j + 1])
1440 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1441 del document.body[i:j + 1]
1443 i = find_token(document.body, "\\begin_layout Abstract", 0)
1446 j = find_end_of_layout(document.body, i)
1448 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1451 content = lyx2latex(document, document.body[i:j + 1])
1452 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1453 del document.body[i:j + 1]
1455 i = find_token(document.body, "\\begin_layout Keywords", 0)
1458 j = find_end_of_layout(document.body, i)
1460 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1463 content = lyx2latex(document, document.body[i:j + 1])
1464 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1465 del document.body[i:j + 1]
1467 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1470 j = find_end_of_layout(document.body, i)
1472 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1475 content = lyx2latex(document, document.body[i:j + 1])
1476 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1477 del document.body[i:j + 1]
1479 i = find_token(document.body, "\\begin_layout Address", 0)
1482 j = find_end_of_layout(document.body, i)
1484 document.warning("Malformed LyX document: Can't find end of Address layout")
1487 content = lyx2latex(document, document.body[i:j + 1])
1488 add_to_preamble(document, ["\\Address{" + content + "}"])
1489 del document.body[i:j + 1]
1490 # finally handle the code layouts
1495 while m != -1 or j != -1 or h != -1 or k != -1:
1498 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1500 endh = find_end_of_inset(document.body, h)
1501 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1502 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1503 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1507 j = find_token(document.body, "\\begin_layout Code Input", j)
1509 endj = find_end_of_layout(document.body, j)
1510 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1511 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1512 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1513 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1514 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1518 k = find_token(document.body, "\\begin_layout Code Output", k)
1520 endk = find_end_of_layout(document.body, k)
1521 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1522 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1523 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1524 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1525 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1529 m = find_token(document.body, "\\begin_layout Code", m)
1531 endm = find_end_of_layout(document.body, m)
1532 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1533 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1534 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1535 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1536 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1540 def convert_subref(document):
1541 " converts sub: ref prefixes to subref: "
1544 rx = re.compile(r'^name \"sub:(.+)$')
1547 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1550 j = find_end_of_inset(document.body, i)
1552 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1556 for p in range(i, j):
1557 m = rx.match(document.body[p])
1560 document.body[p] = "name \"subsec:" + label
1564 rx = re.compile(r'^reference \"sub:(.+)$')
1567 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1570 j = find_end_of_inset(document.body, i)
1572 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1576 for p in range(i, j):
1577 m = rx.match(document.body[p])
1580 document.body[p] = "reference \"subsec:" + label
1586 def revert_subref(document):
1587 " reverts subref: ref prefixes to sub: "
1590 rx = re.compile(r'^name \"subsec:(.+)$')
1593 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1596 j = find_end_of_inset(document.body, i)
1598 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1602 for p in range(i, j):
1603 m = rx.match(document.body[p])
1606 document.body[p] = "name \"sub:" + label
1611 rx = re.compile(r'^reference \"subsec:(.+)$')
1614 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1617 j = find_end_of_inset(document.body, i)
1619 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1623 for p in range(i, j):
1624 m = rx.match(document.body[p])
1627 document.body[p] = "reference \"sub:" + label
1632 def convert_nounzip(document):
1633 " remove the noUnzip parameter of graphics insets "
1635 rx = re.compile(r'\s*noUnzip\s*$')
1638 i = find_token(document.body, "\\begin_inset Graphics", i)
1641 j = find_end_of_inset(document.body, i)
1643 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1647 k = find_re(document.body, rx, i, j)
1649 del document.body[k]
1654 def convert_revert_external_bbox(document, forward):
1655 " add units to bounding box of external insets "
1657 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1660 i = find_token(document.body, "\\begin_inset External", i)
1663 j = find_end_of_inset(document.body, i)
1665 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1668 k = find_re(document.body, rx, i, j)
1672 tokens = document.body[k].split()
1674 for t in range(1, 5):
1677 for t in range(1, 5):
1678 tokens[t] = length_in_bp(tokens[t])
1679 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1680 tokens[3] + " " + tokens[4]
1684 def convert_external_bbox(document):
1685 convert_revert_external_bbox(document, True)
1688 def revert_external_bbox(document):
1689 convert_revert_external_bbox(document, False)
1692 def revert_tcolorbox_1(document):
1693 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1696 i = find_token(document.header, "tcolorbox", i)
1702 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1705 flexEnd = find_end_of_inset(document.body, flex)
1706 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1707 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1708 flexEnd = find_end_of_inset(document.body, flex)
1710 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1712 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1713 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1717 def revert_tcolorbox_2(document):
1718 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1721 i = find_token(document.header, "tcolorbox", i)
1727 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1730 flexEnd = find_end_of_inset(document.body, flex)
1731 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1732 flexEnd = find_end_of_inset(document.body, flex)
1733 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1734 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1738 def revert_tcolorbox_3(document):
1739 " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1742 i = find_token(document.header, "tcolorbox", i)
1748 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1751 flexEnd = find_end_of_inset(document.body, flex)
1752 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1753 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1754 flexEnd = find_end_of_inset(document.body, flex)
1755 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1756 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1760 def revert_tcolorbox_4(document):
1761 " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1764 i = find_token(document.header, "tcolorbox", i)
1770 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1773 flexEnd = find_end_of_inset(document.body, flex)
1774 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1775 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1776 flexEnd = find_end_of_inset(document.body, flex)
1777 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1778 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1782 def revert_tcolorbox_5(document):
1783 " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1786 i = find_token(document.header, "tcolorbox", i)
1792 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1795 flexEnd = find_end_of_inset(document.body, flex)
1796 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1797 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1798 flexEnd = find_end_of_inset(document.body, flex)
1799 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1800 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1804 def revert_tcolorbox_6(document):
1805 " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1808 i = find_token(document.header, "tcolorbox", i)
1814 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1817 flexEnd = find_end_of_inset(document.body, flex)
1818 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1819 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1820 flexEnd = find_end_of_inset(document.body, flex)
1821 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1822 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1826 def revert_tcolorbox_7(document):
1827 " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1830 i = find_token(document.header, "tcolorbox", i)
1836 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1839 flexEnd = find_end_of_inset(document.body, flex)
1840 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1841 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1842 flexEnd = find_end_of_inset(document.body, flex)
1843 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1844 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1848 def revert_tcolorbox_8(document):
1849 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1855 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1857 j = find_end_of_layout(document.body, i)
1858 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1859 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1860 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1861 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1863 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1865 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1866 k = find_end_of_inset(document.body, j)
1867 k = find_token(document.body, "\\end_inset", k + 1)
1868 k = find_token(document.body, "\\end_inset", k + 1)
1870 k = find_token(document.body, "\\end_inset", k + 1)
1871 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1872 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1873 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1879 def revert_moderncv_1(document):
1880 " Reverts the new inset of moderncv to TeX-code in preamble "
1882 if document.textclass != "moderncv":
1888 # at first revert the new styles
1890 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1893 j = find_end_of_layout(document.body, i)
1895 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1898 content = lyx2latex(document, document.body[i:j + 1])
1899 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1900 del document.body[i:j + 1]
1902 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1905 j = find_end_of_layout(document.body, i)
1907 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1910 content = lyx2latex(document, document.body[i:j + 1])
1911 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1912 del document.body[i:j + 1]
1913 # now change the new styles to the obsolete ones
1915 i = find_token(document.body, "\\begin_layout Name", 0)
1918 j = find_end_of_layout(document.body, i)
1920 document.warning("Malformed LyX document: Can't find end of Name layout")
1923 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1924 if lineArg > j and j != 0:
1927 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1928 # we have to assure that no other inset is in the Argument
1929 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1930 endInset = find_token(document.body, "\\end_inset", beginPlain)
1933 while beginInset < endInset and beginInset != -1:
1934 beginInset = find_token(document.body, "\\begin_inset", k)
1935 endInset = find_token(document.body, "\\end_inset", l)
1938 Arg2 = document.body[l + 5 : l + 6]
1940 document.body[i : i + 1]= ["\\begin_layout FirstName"]
1941 # delete the Argument inset
1942 del( document.body[endInset - 2 : endInset + 3])
1943 del( document.body[lineArg : beginPlain + 1])
1944 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1947 def revert_moderncv_2(document):
1948 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1950 if document.textclass != "moderncv":
1957 i = find_token(document.body, "\\begin_layout Phone", i)
1960 j = find_end_of_layout(document.body, i)
1962 document.warning("Malformed LyX document: Can't find end of Phone layout")
1965 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1966 if lineArg > j and j != 0:
1970 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1971 # we have to assure that no other inset is in the Argument
1972 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1973 endInset = find_token(document.body, "\\end_inset", beginPlain)
1976 while beginInset < endInset and beginInset != -1:
1977 beginInset = find_token(document.body, "\\begin_inset", k)
1978 endInset = find_token(document.body, "\\end_inset", l)
1981 Arg = document.body[beginPlain + 1 : beginPlain + 2]
1983 if Arg[0] == "mobile":
1984 document.body[i : i + 1]= ["\\begin_layout Mobile"]
1986 document.body[i : i + 1]= ["\\begin_layout Fax"]
1987 # delete the Argument inset
1988 del(document.body[endInset - 2 : endInset + 1])
1989 del(document.body[lineArg : beginPlain + 3])
1993 def convert_moderncv_phone(document):
1994 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
1996 if document.textclass != "moderncv":
2003 "Mobile" : "mobile",
2007 rx = re.compile(r'^\\begin_layout (\S+)$')
2009 # substitute \fax and \mobile by \phone[fax] and \phone[mobile], respectively
2010 i = find_token(document.body, "\\begin_layout", i)
2014 m = rx.match(document.body[i])
2018 if val not in list(phone_dict.keys()):
2021 j = find_end_of_layout(document.body, i)
2023 document.warning("Malformed LyX document: Can't find end of Mobile layout")
2027 document.body[i : i + 1] = ["\\begin_layout Phone", "\\begin_inset Argument 1", "status open", "",
2028 "\\begin_layout Plain Layout", phone_dict[val], "\\end_layout", "",
2032 def convert_moderncv_name(document):
2033 " Convert the FirstName and LastName layout of moderncv to the general Name layout "
2035 if document.textclass != "moderncv":
2038 fnb = 0 # Begin of FirstName inset
2039 fne = 0 # End of FirstName inset
2040 lnb = 0 # Begin of LastName (FamilyName) inset
2041 lne = 0 # End of LastName (FamilyName) inset
2042 nb = 0 # Begin of substituting Name inset
2043 ne = 0 # End of substituting Name inset
2044 FirstName = [] # FirstName content
2045 FamilyName = [] # LastName content
2049 fnb = find_token(document.body, "\\begin_layout FirstName", fnb)
2051 fne = find_end_of_layout(document.body, fnb)
2053 document.warning("Malformed LyX document: Can't find end of FirstName layout")
2055 FirstName = document.body[fnb + 1 : fne]
2057 lnb = find_token(document.body, "\\begin_layout FamilyName", lnb)
2059 lne = find_end_of_layout(document.body, lnb)
2061 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
2063 FamilyName = document.body[lnb + 1 : lne]
2064 # Determine the region for the substituting Name layout
2065 if fnb == -1 and lnb == -1: # Neither FirstName nor FamilyName exists -> Do nothing
2067 elif fnb == -1: # Only FamilyName exists -> New Name insets replaces that
2070 elif lnb == -1: # Only FirstName exists -> New Name insets replaces that
2073 elif fne > lne: # FirstName position before FamilyName -> New Name insets spans
2074 nb = lnb # from FamilyName begin
2075 ne = fne # to FirstName end
2076 else: # FirstName position before FamilyName -> New Name insets spans
2077 nb = fnb # from FirstName begin
2078 ne = lne # to FamilyName end
2080 # Insert the substituting layout now. If FirstName exists, use an otpional argument.
2082 document.body[nb : ne + 1] = ["\\begin_layout Name"] + FamilyName + ["\\end_layout", ""]
2084 document.body[nb : ne + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2085 "\\begin_layout Plain Layout"] + FirstName + ["\\end_layout", "",
2086 "\\end_inset", ""] + FamilyName + ["\\end_layout", ""]
2089 def revert_achemso(document):
2090 " Reverts the flex inset Latin to TeX code "
2092 if document.textclass != "achemso":
2097 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2099 j = find_end_of_inset(document.body, i)
2103 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2104 endPlain = find_end_of_layout(document.body, beginPlain)
2105 content = lyx2latex(document, document.body[beginPlain : endPlain])
2106 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2108 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2113 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2114 "\\font_sf_scale", "\\font_tt_scale"]
2115 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2116 fontquotes = [True, True, True, True, False, False]
2118 def convert_fontsettings(document):
2119 " Duplicate font settings "
2121 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2123 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2124 use_non_tex_fonts = "false"
2126 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2128 for f in fontsettings:
2129 i = find_token(document.header, f + " ", 0)
2131 document.warning("Malformed LyX document: No " + f + "!")
2133 # note that with i = -1, this will insert at the end
2135 value = fontdefaults[j]
2137 value = document.header[i][len(f):].strip()
2139 if use_non_tex_fonts == "true":
2140 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2142 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2144 if use_non_tex_fonts == "true":
2145 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2147 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2151 def revert_fontsettings(document):
2152 " Merge font settings "
2154 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2156 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2157 use_non_tex_fonts = "false"
2159 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2161 for f in fontsettings:
2162 i = find_token(document.header, f + " ", 0)
2164 document.warning("Malformed LyX document: No " + f + "!")
2167 line = get_value(document.header, f, i)
2170 q2 = line.find('"', q1+1)
2171 q3 = line.find('"', q2+1)
2172 q4 = line.find('"', q3+1)
2173 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2174 document.warning("Malformed LyX document: Missing quotes!")
2177 if use_non_tex_fonts == "true":
2178 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2180 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2182 if use_non_tex_fonts == "true":
2183 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2185 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2189 def revert_solution(document):
2190 " Reverts the solution environment of the theorem module to TeX code "
2192 # Do we use one of the modules that provides Solution?
2194 mods = document.get_module_list()
2196 if mod == "theorems-std" or mod == "theorems-bytype" \
2197 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2207 i = find_token(document.body, "\\begin_layout Solution", i)
2211 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2212 if is_starred == True:
2214 LyXName = "Solution*"
2215 theoremName = "newtheorem*"
2218 LyXName = "Solution"
2219 theoremName = "newtheorem"
2221 j = find_end_of_layout(document.body, i)
2223 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2227 # if this is not a consecutive env, add start command
2230 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2232 # has this a consecutive theorem of same type?
2233 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2235 # if this is not followed by a consecutive env, add end command
2237 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2239 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2241 add_to_preamble(document, "\\theoremstyle{definition}")
2242 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2243 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2244 (theoremName, LaTeXName))
2245 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2246 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2247 (theoremName, LaTeXName))
2249 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2253 def revert_verbatim_star(document):
2254 from lyx_2_1 import revert_verbatim
2255 revert_verbatim(document, True)
2258 def convert_save_props(document):
2259 " Add save_transient_properties parameter. "
2260 i = find_token(document.header, '\\begin_header', 0)
2262 document.warning("Malformed lyx document: Missing '\\begin_header'.")
2264 document.header.insert(i + 1, '\\save_transient_properties true')
2267 def revert_save_props(document):
2268 " Remove save_transient_properties parameter. "
2269 i = find_token(document.header, "\\save_transient_properties", 0)
2272 del document.header[i]
2275 def convert_info_tabular_feature(document):
2277 return arg.replace("inset-modify tabular", "tabular-feature")
2278 convert_info_insets(document, "shortcut(s)?|icon", f)
2281 def revert_info_tabular_feature(document):
2283 return arg.replace("tabular-feature", "inset-modify tabular")
2284 convert_info_insets(document, "shortcut(s)?|icon", f)
2291 supported_versions = ["2.2.0", "2.2"]
2293 [475, [convert_separator]],
2294 # nothing to do for 476: We consider it a bug that older versions
2295 # did not load amsmath automatically for these commands, and do not
2296 # want to hardcode amsmath off.
2302 [481, [convert_dashes]],
2303 [482, [convert_phrases]],
2304 [483, [convert_specialchar]],
2309 [488, [convert_newgloss]],
2310 [489, [convert_BoxFeatures]],
2311 [490, [convert_origin]],
2313 [492, [convert_colorbox]],
2316 [495, [convert_subref]],
2317 [496, [convert_nounzip]],
2318 [497, [convert_external_bbox]],
2320 [499, [convert_moderncv_phone, convert_moderncv_name]],
2322 [501, [convert_fontsettings]],
2325 [504, [convert_save_props]],
2327 [506, [convert_info_tabular_feature]],
2328 [507, [convert_longtable_label]],
2329 [508, [convert_parbreak]]
2333 [507, [revert_parbreak]],
2334 [506, [revert_longtable_label]],
2335 [505, [revert_info_tabular_feature]],
2337 [503, [revert_save_props]],
2338 [502, [revert_verbatim_star]],
2339 [501, [revert_solution]],
2340 [500, [revert_fontsettings]],
2341 [499, [revert_achemso]],
2342 [498, [revert_moderncv_1, revert_moderncv_2]],
2343 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2344 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2345 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2346 [496, [revert_external_bbox]],
2347 [495, []], # nothing to do since the noUnzip parameter was optional
2348 [494, [revert_subref]],
2349 [493, [revert_jss]],
2350 [492, [revert_mathmulticol]],
2351 [491, [revert_colorbox]],
2352 [490, [revert_textcolor]],
2353 [489, [revert_origin]],
2354 [488, [revert_BoxFeatures]],
2355 [487, [revert_newgloss, revert_glossgroup]],
2356 [486, [revert_forest]],
2357 [485, [revert_ex_itemargs]],
2358 [484, [revert_sigplan_doi]],
2359 [483, [revert_georgian]],
2360 [482, [revert_specialchar]],
2361 [481, [revert_phrases]],
2362 [480, [revert_dashes]],
2363 [479, [revert_question_env]],
2364 [478, [revert_beamer_lemma]],
2365 [477, [revert_xarrow]],
2366 [476, [revert_swissgerman]],
2367 [475, [revert_smash]],
2368 [474, [revert_separator]]
2372 if __name__ == "__main__":