1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # -*- coding: utf-8 -*-
4 # Copyright (C) 2015 The LyX team
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 """ Convert files to the file format generated by lyx 2.2"""
26 # Uncomment only what you need to import, please.
28 #from parser_tools import find_token, find_end_of, find_tokens, \
29 # find_token_exact, find_end_of_inset, find_end_of_layout, \
30 # find_token_backwards, is_in_inset, get_value, get_quoted_value, \
31 # del_token, check_token, get_option_value
33 from lyx2lyx_tools import add_to_preamble, put_cmd_in_ert, get_ert, lyx2latex, \
34 lyx2verbatim, length_in_bp, convert_info_insets
35 # insert_to_preamble, latex_length, revert_flex_inset, \
36 # revert_font_attrs, hex2ratio, str2bool
38 from parser_tools import find_token, find_token_backwards, find_re, \
39 find_end_of_inset, find_end_of_layout, find_nonempty_line, \
40 get_containing_layout, get_value, check_token
42 ####################################################################
43 # Private helper functions
45 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt, nolastopt):
47 Reverts an InsetArgument to TeX-code
49 revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt, notLastOpt)
50 LineOfBegin is the line of the \begin_layout or \begin_inset statement
51 LineOfEnd is the line of the \end_layout or \end_inset statement, if "0" is given, the end of the file is used instead
52 StartArgument is the number of the first argument that needs to be converted
53 EndArgument is the number of the last argument that needs to be converted or the last defined one
54 isEnvironment must be true, if the layout is for a LaTeX environment
55 isOpt must be true, if the argument is an optional one
56 notLastOpt must be true if the argument is mandatory and followed by optional ones
60 while lineArg != -1 and n < nmax + 1:
61 lineArg = find_token(document.body, "\\begin_inset Argument " + str(n), line)
62 if lineArg > endline and endline != 0:
65 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
66 # we have to assure that no other inset is in the Argument
67 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
68 endInset = find_token(document.body, "\\end_inset", beginPlain)
71 while beginInset < endInset and beginInset != -1:
72 beginInset = find_token(document.body, "\\begin_inset", k)
73 endInset = find_token(document.body, "\\end_inset", l)
76 if environment == False:
78 if nolastopt == False:
79 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
81 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
82 del(document.body[lineArg : beginPlain + 1])
85 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
86 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
90 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
91 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("{")
94 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("]")
95 document.body[lineArg : beginPlain + 1] = put_cmd_in_ert("[")
101 ###############################################################################
103 ### Conversion and reversion routines
105 ###############################################################################
107 def convert_longtable_label_internal(document, forward):
109 Convert reference to "LongTableNoNumber" into "Unnumbered" if forward is True
112 old_reference = "\\begin_inset Caption LongTableNoNumber"
113 new_reference = "\\begin_inset Caption Unnumbered"
115 # if the purpose is to revert swap the strings roles
117 old_reference, new_reference = new_reference, old_reference
121 i = find_token(document.body, old_reference, i)
126 document.body[i] = new_reference
129 def convert_longtable_label(document):
130 convert_longtable_label_internal(document, True)
133 def revert_longtable_label(document):
134 convert_longtable_label_internal(document, False)
137 def convert_separator(document):
139 Convert layout separators to separator insets and add (LaTeX) paragraph
140 breaks in order to mimic previous LaTeX export.
143 parins = ["\\begin_inset Separator parbreak", "\\end_inset", ""]
144 parlay = ["\\begin_layout Standard", "\\begin_inset Separator parbreak",
145 "\\end_inset", "", "\\end_layout", ""]
147 "family" : "default",
148 "series" : "default",
157 i = find_token(document.body, "\\begin_deeper", i)
161 j = find_token_backwards(document.body, "\\end_layout", i-1)
163 # reset any text style before inserting the inset
164 lay = get_containing_layout(document.body, j-1)
166 content = "\n".join(document.body[lay[1]:lay[2]])
167 for val in list(sty_dict.keys()):
168 if content.find("\\%s" % val) != -1:
169 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
172 document.body[j:j] = parins
173 i = i + len(parins) + 1
179 i = find_token(document.body, "\\align", i)
183 lay = get_containing_layout(document.body, i)
184 if lay != False and lay[0] == "Plain Layout":
188 j = find_token_backwards(document.body, "\\end_layout", i-1)
190 lay = get_containing_layout(document.body, j-1)
191 if lay != False and lay[0] == "Standard" \
192 and find_token(document.body, "\\align", lay[1], lay[2]) == -1 \
193 and find_token(document.body, "\\begin_inset VSpace", lay[1], lay[2]) == -1:
194 # reset any text style before inserting the inset
195 content = "\n".join(document.body[lay[1]:lay[2]])
196 for val in list(sty_dict.keys()):
197 if content.find("\\%s" % val) != -1:
198 document.body[j:j] = ["\\%s %s" % (val, sty_dict[val])]
201 document.body[j:j] = parins
202 i = i + len(parins) + 1
208 regexp = re.compile(r'^\\begin_layout (?:(-*)|(\s*))(Separator|EndOfSlide)(?:(-*)|(\s*))$', re.IGNORECASE)
212 i = find_re(document.body, regexp, i)
216 j = find_end_of_layout(document.body, i)
218 document.warning("Malformed LyX document: Missing `\\end_layout'.")
221 lay = get_containing_layout(document.body, j-1)
223 lines = document.body[lay[3]:lay[2]]
227 document.body[i:j+1] = parlay
229 document.body[i+1:i+1] = lines
231 i = i + len(parlay) + len(lines) + 1
234 def revert_separator(document):
235 " Revert separator insets to layout separators "
237 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
238 if document.textclass in beamer_classes:
239 beglaysep = "\\begin_layout Separator"
241 beglaysep = "\\begin_layout --Separator--"
243 parsep = [beglaysep, "", "\\end_layout", ""]
244 comert = ["\\begin_inset ERT", "status collapsed", "",
245 "\\begin_layout Plain Layout", "%", "\\end_layout",
246 "", "\\end_inset", ""]
247 empert = ["\\begin_inset ERT", "status collapsed", "",
248 "\\begin_layout Plain Layout", " ", "\\end_layout",
249 "", "\\end_inset", ""]
253 i = find_token(document.body, "\\begin_inset Separator", i)
257 lay = get_containing_layout(document.body, i)
259 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
266 kind = get_value(document.body, "\\begin_inset Separator", i, i+1, "plain").split()[1]
267 before = document.body[beg+1:i]
268 something_before = len(before) > 0 and len("".join(before)) > 0
269 j = find_end_of_inset(document.body, i)
270 after = document.body[j+1:end]
271 something_after = len(after) > 0 and len("".join(after)) > 0
273 beg = beg + len(before) + 1
274 elif something_before:
275 document.body[i:i] = ["\\end_layout", ""]
283 document.body[beg:j+1] = empert
286 document.body[beg:j+1] = comert
290 if layoutname == "Standard":
291 if not something_before:
292 document.body[beg:j+1] = parsep
294 document.body[i:i] = ["", "\\begin_layout Standard"]
297 document.body[beg:j+1] = ["\\begin_layout Standard"]
300 document.body[beg:j+1] = ["\\begin_deeper"]
302 end = end + 1 - (j + 1 - beg)
303 if not something_before:
304 document.body[i:i] = parsep
306 end = end + len(parsep)
307 document.body[i:i] = ["\\begin_layout Standard"]
308 document.body[end+2:end+2] = ["", "\\end_deeper", ""]
311 next_par_is_aligned = False
312 k = find_nonempty_line(document.body, end+1)
313 if k != -1 and check_token(document.body[k], "\\begin_layout"):
314 lay = get_containing_layout(document.body, k)
315 next_par_is_aligned = lay != False and \
316 find_token(document.body, "\\align", lay[1], lay[2]) != -1
317 if k != -1 and not next_par_is_aligned \
318 and not check_token(document.body[k], "\\end_deeper") \
319 and not check_token(document.body[k], "\\begin_deeper"):
320 if layoutname == "Standard":
321 document.body[beg:j+1] = [beglaysep]
324 document.body[beg:j+1] = ["\\begin_deeper", beglaysep]
325 end = end + 2 - (j + 1 - beg)
326 document.body[end+1:end+1] = ["", "\\end_deeper", ""]
330 del document.body[i:end+1]
332 del document.body[i:end-1]
337 def convert_parbreak(document):
339 Convert parbreak separators not specifically used to separate
340 environments to latexpar separators.
342 parbreakinset = "\\begin_inset Separator parbreak"
345 i = find_token(document.body, parbreakinset, i)
348 lay = get_containing_layout(document.body, i)
350 document.warning("Malformed LyX document: Can't convert separator inset at line " + str(i))
353 if lay[0] == "Standard":
354 # Convert only if not alone in the paragraph
355 k1 = find_nonempty_line(document.body, lay[1] + 1, i + 1)
356 k2 = find_nonempty_line(document.body, i + 1, lay[2])
357 if (k1 < i) or (k2 > i + 1) or not check_token(document.body[i], parbreakinset):
358 document.body[i] = document.body[i].replace("parbreak", "latexpar")
360 document.body[i] = document.body[i].replace("parbreak", "latexpar")
364 def revert_parbreak(document):
366 Revert latexpar separators to parbreak separators.
370 i = find_token(document.body, "\\begin_inset Separator latexpar", i)
373 document.body[i] = document.body[i].replace("latexpar", "parbreak")
377 def revert_smash(document):
378 " Set amsmath to on if smash commands are used "
380 commands = ["smash[t]", "smash[b]", "notag"]
381 i = find_token(document.header, "\\use_package amsmath", 0)
383 document.warning("Malformed LyX document: Can't find \\use_package amsmath.")
385 value = get_value(document.header, "\\use_package amsmath", i).split()[1]
387 # nothing to do if package is not auto but on or off
391 j = find_token(document.body, '\\begin_inset Formula', j)
394 k = find_end_of_inset(document.body, j)
396 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(j))
399 code = "\n".join(document.body[j:k])
401 if code.find("\\%s" % c) != -1:
402 # set amsmath to on, since it is loaded by the newer format
403 document.header[i] = "\\use_package amsmath 2"
408 def revert_swissgerman(document):
409 " Set language german-ch-old to german "
411 if document.language == "german-ch-old":
412 document.language = "german"
413 i = find_token(document.header, "\\language", 0)
415 document.header[i] = "\\language german"
418 j = find_token(document.body, "\\lang german-ch-old", j)
421 document.body[j] = document.body[j].replace("\\lang german-ch-old", "\\lang german")
425 def revert_use_package(document, pkg, commands, oldauto, supported):
426 # oldauto defines how the version we are reverting to behaves:
427 # if it is true, the old version uses the package automatically.
428 # if it is false, the old version never uses the package.
429 # If "supported" is true, the target version also supports this
431 regexp = re.compile(r'(\\use_package\s+%s)' % pkg)
432 p = find_re(document.header, regexp, 0)
433 value = "1" # default is auto
435 value = get_value(document.header, "\\use_package" , p).split()[1]
437 del document.header[p]
438 if value == "2" and not supported: # on
439 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
440 elif value == "1" and not oldauto: # auto
443 i = find_token(document.body, '\\begin_inset Formula', i)
446 j = find_end_of_inset(document.body, i)
448 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
451 code = "\n".join(document.body[i:j])
453 if code.find("\\%s" % c) != -1:
455 document.header[p] = "\\use_package " + pkg + " 2"
457 add_to_preamble(document, ["\\usepackage{" + pkg + "}"])
462 mathtools_commands = ["xhookrightarrow", "xhookleftarrow", "xRightarrow", \
463 "xrightharpoondown", "xrightharpoonup", "xrightleftharpoons", \
464 "xLeftarrow", "xleftharpoondown", "xleftharpoonup", \
465 "xleftrightarrow", "xLeftrightarrow", "xleftrightharpoons", \
468 def revert_xarrow(document):
469 "remove use_package mathtools"
470 revert_use_package(document, "mathtools", mathtools_commands, False, True)
473 def revert_beamer_lemma(document):
474 " Reverts beamer lemma layout to ERT "
476 beamer_classes = ["beamer", "article-beamer", "scrarticle-beamer"]
477 if document.textclass not in beamer_classes:
483 i = find_token(document.body, "\\begin_layout Lemma", i)
486 j = find_end_of_layout(document.body, i)
488 document.warning("Malformed LyX document: Can't find end of Lemma layout")
491 arg1 = find_token(document.body, "\\begin_inset Argument 1", i, j)
492 endarg1 = find_end_of_inset(document.body, arg1)
493 arg2 = find_token(document.body, "\\begin_inset Argument 2", i, j)
494 endarg2 = find_end_of_inset(document.body, arg2)
498 beginPlain1 = find_token(document.body, "\\begin_layout Plain Layout", arg1, endarg1)
499 if beginPlain1 == -1:
500 document.warning("Malformed LyX document: Can't find arg1 plain Layout")
503 endPlain1 = find_end_of_inset(document.body, beginPlain1)
504 content1 = document.body[beginPlain1 + 1 : endPlain1 - 2]
505 subst1 = put_cmd_in_ert("<") + content1 + put_cmd_in_ert(">")
507 beginPlain2 = find_token(document.body, "\\begin_layout Plain Layout", arg2, endarg2)
508 if beginPlain2 == -1:
509 document.warning("Malformed LyX document: Can't find arg2 plain Layout")
512 endPlain2 = find_end_of_inset(document.body, beginPlain2)
513 content2 = document.body[beginPlain2 + 1 : endPlain2 - 2]
514 subst2 = put_cmd_in_ert("[") + content2 + put_cmd_in_ert("]")
518 del document.body[arg2 : endarg2 + 1]
520 del document.body[arg1 : endarg1 + 1]
522 del document.body[arg1 : endarg1 + 1]
524 del document.body[arg2 : endarg2 + 1]
526 # index of end layout has probably changed
527 j = find_end_of_layout(document.body, i)
529 document.warning("Malformed LyX document: Can't find end of Lemma layout")
535 # if this is not a consecutive env, add start command
537 begcmd = put_cmd_in_ert("\\begin{lemma}")
539 # has this a consecutive lemma?
540 consecutive = document.body[j + 2] == "\\begin_layout Lemma"
542 # if this is not followed by a consecutive env, add end command
544 document.body[j : j + 1] = put_cmd_in_ert("\\end{lemma}") + ["\\end_layout"]
546 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd + subst1 + subst2
552 def revert_question_env(document):
554 Reverts question and question* environments of
555 theorems-ams-extended-bytype module to ERT
558 # Do we use theorems-ams-extended-bytype module?
559 if not "theorems-ams-extended-bytype" in document.get_module_list():
565 i = find_token(document.body, "\\begin_layout Question", i)
569 starred = document.body[i] == "\\begin_layout Question*"
571 j = find_end_of_layout(document.body, i)
573 document.warning("Malformed LyX document: Can't find end of Question layout")
577 # if this is not a consecutive env, add start command
581 begcmd = put_cmd_in_ert("\\begin{question*}")
583 begcmd = put_cmd_in_ert("\\begin{question}")
585 # has this a consecutive theorem of same type?
588 consecutive = document.body[j + 2] == "\\begin_layout Question*"
590 consecutive = document.body[j + 2] == "\\begin_layout Question"
592 # if this is not followed by a consecutive env, add end command
595 document.body[j : j + 1] = put_cmd_in_ert("\\end{question*}") + ["\\end_layout"]
597 document.body[j : j + 1] = put_cmd_in_ert("\\end{question}") + ["\\end_layout"]
599 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
601 add_to_preamble(document, "\\providecommand{\questionname}{Question}")
604 add_to_preamble(document, "\\theoremstyle{plain}\n" \
605 "\\newtheorem*{question*}{\\protect\\questionname}")
607 add_to_preamble(document, "\\theoremstyle{plain}\n" \
608 "\\newtheorem{question}{\\protect\\questionname}")
613 def convert_dashes(document):
614 "convert -- and --- to \\twohyphens and \\threehyphens"
616 if document.backend != "latex":
620 while i < len(document.body):
621 words = document.body[i].split()
622 if len(words) > 1 and words[0] == "\\begin_inset" and \
623 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
624 # must not replace anything in insets that store LaTeX contents in .lyx files
625 # (math and command insets withut overridden read() and write() methods
626 # filtering out IPA makes Text::readParToken() more simple
627 # skip ERT as well since it is not needed there
628 j = find_end_of_inset(document.body, i)
630 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
636 j = document.body[i].find("--")
639 front = document.body[i][:j]
640 back = document.body[i][j+2:]
641 # We can have an arbitrary number of consecutive hyphens.
642 # These must be split into the corresponding number of two and three hyphens
643 # We must match what LaTeX does: First try emdash, then endash, then single hyphen
644 if back.find("-") == 0:
647 document.body.insert(i+1, back)
648 document.body[i] = front + "\\threehyphens"
651 document.body.insert(i+1, back)
652 document.body[i] = front + "\\twohyphens"
656 def revert_dashes(document):
657 "convert \\twohyphens and \\threehyphens to -- and ---"
660 while i < len(document.body):
661 words = document.body[i].split()
662 if len(words) > 1 and words[0] == "\\begin_inset" and \
663 words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
665 j = find_end_of_inset(document.body, i)
667 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
673 if document.body[i].find("\\twohyphens") >= 0:
674 document.body[i] = document.body[i].replace("\\twohyphens", "--")
676 if document.body[i].find("\\threehyphens") >= 0:
677 document.body[i] = document.body[i].replace("\\threehyphens", "---")
679 if replaced and i+1 < len(document.body) and \
680 (document.body[i+1].find("\\") != 0 or \
681 document.body[i+1].find("\\twohyphens") == 0 or
682 document.body[i+1].find("\\threehyphens") == 0) and \
683 len(document.body[i]) + len(document.body[i+1]) <= 80:
684 document.body[i] = document.body[i] + document.body[i+1]
685 document.body[i+1:i+2] = []
690 # order is important for the last three!
691 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
693 def is_part_of_converted_phrase(line, j, phrase):
694 "is phrase part of an already converted phrase?"
696 converted = "\\SpecialCharNoPassThru \\" + p
697 pos = j + len(phrase) - len(converted)
699 if line[pos:pos+len(converted)] == converted:
704 def convert_phrases(document):
705 "convert special phrases from plain text to \\SpecialCharNoPassThru"
707 if document.backend != "latex":
710 for phrase in phrases:
712 while i < len(document.body):
713 words = document.body[i].split()
714 if len(words) > 1 and words[0] == "\\begin_inset" and \
715 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
716 # must not replace anything in insets that store LaTeX contents in .lyx files
717 # (math and command insets withut overridden read() and write() methods
718 j = find_end_of_inset(document.body, i)
720 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
725 if document.body[i].find("\\") == 0:
728 j = document.body[i].find(phrase)
732 if not is_part_of_converted_phrase(document.body[i], j, phrase):
733 front = document.body[i][:j]
734 back = document.body[i][j+len(phrase):]
736 document.body.insert(i+1, back)
737 # We cannot use SpecialChar since we do not know whether we are outside passThru
738 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
742 def revert_phrases(document):
743 "convert special phrases to plain text"
746 while i < len(document.body):
747 words = document.body[i].split()
748 if len(words) > 1 and words[0] == "\\begin_inset" and \
749 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
750 # see convert_phrases
751 j = find_end_of_inset(document.body, i)
753 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
759 for phrase in phrases:
760 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
761 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
762 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
764 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
765 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
767 if replaced and i+1 < len(document.body) and \
768 (document.body[i+1].find("\\") != 0 or \
769 document.body[i+1].find("\\SpecialChar") == 0) and \
770 len(document.body[i]) + len(document.body[i+1]) <= 80:
771 document.body[i] = document.body[i] + document.body[i+1]
772 document.body[i+1:i+2] = []
777 def convert_specialchar_internal(document, forward):
778 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
779 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
780 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
781 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
782 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
783 "\\LaTeX":"LaTeX" # must be after LaTeX2e
787 while i < len(document.body):
788 words = document.body[i].split()
789 if len(words) > 1 and words[0] == "\\begin_inset" and \
790 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
791 # see convert_phrases
792 j = find_end_of_inset(document.body, i)
794 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
799 for key, value in specialchars.iteritems():
801 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
802 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
804 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
805 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
809 def convert_specialchar(document):
810 "convert special characters to new syntax"
811 convert_specialchar_internal(document, True)
814 def revert_specialchar(document):
815 "convert special characters to old syntax"
816 convert_specialchar_internal(document, False)
819 def revert_georgian(document):
820 "Set the document language to English but assure Georgian output"
822 if document.language == "georgian":
823 document.language = "english"
824 i = find_token(document.header, "\\language georgian", 0)
826 document.header[i] = "\\language english"
827 j = find_token(document.header, "\\language_package default", 0)
829 document.header[j] = "\\language_package babel"
830 k = find_token(document.header, "\\options", 0)
832 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
834 l = find_token(document.header, "\\use_default_options", 0)
835 document.header.insert(l + 1, "\\options georgian")
838 def revert_sigplan_doi(document):
839 " Reverts sigplanconf DOI layout to ERT "
841 if document.textclass != "sigplanconf":
846 i = find_token(document.body, "\\begin_layout DOI", i)
849 j = find_end_of_layout(document.body, i)
851 document.warning("Malformed LyX document: Can't find end of DOI layout")
855 content = lyx2latex(document, document.body[i:j + 1])
856 add_to_preamble(document, ["\\doi{" + content + "}"])
857 del document.body[i:j + 1]
861 def revert_ex_itemargs(document):
862 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
864 if not "linguistics" in document.get_module_list():
868 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
870 i = find_token(document.body, "\\begin_inset Argument item:", i)
873 j = find_end_of_inset(document.body, i)
874 # Find containing paragraph layout
875 parent = get_containing_layout(document.body, i)
877 document.warning("Malformed LyX document: Can't find parent paragraph layout")
881 layoutname = parent[0]
882 if layoutname in example_layouts:
883 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
884 endPlain = find_end_of_layout(document.body, beginPlain)
885 content = document.body[beginPlain + 1 : endPlain]
886 del document.body[i:j+1]
887 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
888 document.body[parbeg : parbeg] = subst
892 def revert_forest(document):
893 " Reverts the forest environment (Linguistics module) to TeX-code "
895 if not "linguistics" in document.get_module_list():
900 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
903 j = find_end_of_inset(document.body, i)
905 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
909 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
910 endPlain = find_end_of_layout(document.body, beginPlain)
911 content = lyx2latex(document, document.body[beginPlain : endPlain])
913 add_to_preamble(document, ["\\usepackage{forest}"])
915 document.body[i:j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
919 def revert_glossgroup(document):
920 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
922 if not "linguistics" in document.get_module_list():
927 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
930 j = find_end_of_inset(document.body, i)
932 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
936 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
937 endPlain = find_end_of_layout(document.body, beginPlain)
938 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
940 document.body[i:j + 1] = ["{", "", content, "", "}"]
944 def revert_newgloss(document):
945 " Reverts the new Glosse insets (Linguistics module) to the old format "
947 if not "linguistics" in document.get_module_list():
950 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
951 for glosse in glosses:
954 i = find_token(document.body, glosse, i)
957 j = find_end_of_inset(document.body, i)
959 document.warning("Malformed LyX document: Can't find end of Glosse inset")
963 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
964 endarg = find_end_of_inset(document.body, arg)
967 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
968 if argbeginPlain == -1:
969 document.warning("Malformed LyX document: Can't find arg plain Layout")
972 argendPlain = find_end_of_inset(document.body, argbeginPlain)
973 argcontent = lyx2verbatim(document, document.body[argbeginPlain : argendPlain - 2])
975 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
976 argcontent, "\\end_layout"]
978 # remove Arg insets and paragraph, if it only contains this inset
979 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
980 del document.body[arg - 1 : endarg + 4]
982 del document.body[arg : endarg + 1]
984 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
985 endPlain = find_end_of_layout(document.body, beginPlain)
986 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
988 document.body[beginPlain + 1:endPlain] = [content]
991 # Dissolve ERT insets
992 for glosse in glosses:
995 i = find_token(document.body, glosse, i)
998 j = find_end_of_inset(document.body, i)
1000 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1004 ert = find_token(document.body, "\\begin_inset ERT", i, j)
1007 ertend = find_end_of_inset(document.body, ert)
1009 document.warning("Malformed LyX document: Can't find end of ERT inset")
1012 ertcontent = get_ert(document.body, ert, True)
1013 document.body[ert : ertend + 1] = [ertcontent]
1017 def convert_newgloss(document):
1018 " Converts Glosse insets (Linguistics module) to the new format "
1020 if not "linguistics" in document.get_module_list():
1023 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1024 for glosse in glosses:
1027 i = find_token(document.body, glosse, i)
1030 j = find_end_of_inset(document.body, i)
1032 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1039 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
1040 if beginPlain == -1:
1042 endPlain = find_end_of_layout(document.body, beginPlain)
1044 document.warning("Malformed LyX document: Can't find end of Glosse layout")
1048 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
1049 if glt != -1 and document.body[glt + 1].startswith("glt"):
1050 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1051 argcontent = document.body[glt + 1 : endPlain]
1052 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
1053 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
1054 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
1055 "\\end_layout", "", "\\end_inset"]
1057 content = document.body[beginPlain + 1 : endPlain]
1058 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
1059 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
1061 endPlain = find_end_of_layout(document.body, beginPlain)
1063 j = find_end_of_inset(document.body, i)
1068 def convert_BoxFeatures(document):
1069 " adds new box features "
1073 i = find_token(document.body, "height_special", i)
1076 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1080 def revert_BoxFeatures(document):
1081 " outputs new box features as TeX code "
1085 defaultThick = "0.4pt"
1086 defaultShadow = "4pt"
1088 i = find_token(document.body, "height_special", i)
1091 # read out the values
1092 beg = document.body[i+1].find('"');
1093 end = document.body[i+1].rfind('"');
1094 thickness = document.body[i+1][beg+1:end];
1095 beg = document.body[i+2].find('"');
1096 end = document.body[i+2].rfind('"');
1097 separation = document.body[i+2][beg+1:end];
1098 beg = document.body[i+3].find('"');
1099 end = document.body[i+3].rfind('"');
1100 shadowsize = document.body[i+3][beg+1:end];
1101 # delete the specification
1102 del document.body[i+1:i+4]
1104 # first output the closing brace
1105 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1106 document.body[i + 10 : i + 10] = put_cmd_in_ert("}")
1107 # now output the lengths
1108 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1109 document.body[i - 10 : i - 10] = put_cmd_in_ert("{")
1110 if thickness != defaultThick:
1111 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness]
1112 if separation != defaultSep and thickness == defaultThick:
1113 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation]
1114 if separation != defaultSep and thickness != defaultThick:
1115 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1116 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1117 document.body[i - 5 : i - 4] = ["{\\backslash shadowsize " + shadowsize]
1118 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1119 document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1120 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1121 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1122 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1123 document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1127 def convert_origin(document):
1128 " Insert the origin tag "
1130 i = find_token(document.header, "\\textclass ", 0)
1132 document.warning("Malformed LyX document: No \\textclass!!")
1134 if document.dir == "":
1138 if document.systemlyxdir and document.systemlyxdir != '':
1140 if os.path.isabs(document.dir):
1141 absdir = os.path.normpath(document.dir)
1143 absdir = os.path.normpath(os.path.abspath(document.dir))
1144 if os.path.isabs(document.systemlyxdir):
1145 abssys = os.path.normpath(document.systemlyxdir)
1147 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1148 relpath = os.path.relpath(absdir, abssys)
1149 if relpath.find('..') == 0:
1154 origin = document.dir.replace('\\', '/') + '/'
1156 origin = os.path.join("/systemlyxdir", relpath).replace('\\', '/') + '/'
1158 origin = unicode(origin, sys.getfilesystemencoding())
1159 document.header[i:i] = ["\\origin " + origin]
1162 def revert_origin(document):
1163 " Remove the origin tag "
1165 i = find_token(document.header, "\\origin ", 0)
1167 document.warning("Malformed LyX document: No \\origin!!")
1169 del document.header[i]
1172 color_names = ["brown", "darkgray", "gray", \
1173 "lightgray", "lime", "olive", "orange", \
1174 "pink", "purple", "teal", "violet"]
1176 def revert_textcolor(document):
1177 " revert new \\textcolor colors to TeX code "
1183 i = find_token(document.body, "\\color ", i)
1187 for color in list(color_names):
1188 if document.body[i] == "\\color " + color:
1189 # register that xcolor must be loaded in the preamble
1192 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\usepackage{xcolor}}{}"])
1193 # find the next \\color and/or the next \\end_layout
1194 j = find_token(document.body, "\\color", i + 1)
1195 k = find_token(document.body, "\\end_layout", i + 1)
1196 if j == -1 and k != -1:
1199 # first output the closing brace
1201 document.body[k: k] = put_cmd_in_ert("}")
1203 document.body[j: j] = put_cmd_in_ert("}")
1204 # now output the \textcolor command
1205 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1209 def convert_colorbox(document):
1210 " adds color settings for boxes "
1214 i = find_token(document.body, "shadowsize", i)
1217 document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1221 def revert_colorbox(document):
1222 " outputs color settings for boxes as TeX code "
1225 defaultframecolor = "black"
1226 defaultbackcolor = "none"
1228 binset = find_token(document.body, "\\begin_inset Box", binset)
1232 einset = find_end_of_inset(document.body, binset)
1234 document.warning("Malformed LyX document: Can't find end of box inset!")
1238 blay = find_token(document.body, "\\begin_layout", binset, einset)
1240 document.warning("Malformed LyX document: Can't find start of layout!")
1244 # doing it this way, we make sure only to find a framecolor option
1245 frame = find_token(document.body, "framecolor", binset, blay)
1250 beg = document.body[frame].find('"')
1251 end = document.body[frame].rfind('"')
1252 framecolor = document.body[frame][beg + 1 : end]
1254 # this should be on the next line
1256 beg = document.body[bgcolor].find('"')
1257 end = document.body[bgcolor].rfind('"')
1258 backcolor = document.body[bgcolor][beg + 1 : end]
1261 del document.body[frame : frame + 2]
1262 # adjust end of inset
1265 if document.body[binset] == "\\begin_inset Box Boxed" and \
1266 framecolor != defaultframecolor:
1267 document.body[binset] = "\\begin_inset Box Frameless"
1270 # first output the closing brace
1271 if framecolor == defaultframecolor and backcolor == defaultbackcolor:
1275 # we also neeed to load xcolor in the preamble but only once
1276 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\usepackage{xcolor}}{}"])
1277 document.body[einset + 1 : einset + 1] = put_cmd_in_ert("}")
1278 if framecolor != defaultframecolor:
1279 document.body[binset:binset] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1281 document.body[binset:binset] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
1286 def revert_mathmulticol(document):
1287 " Convert formulas to ERT if they contain multicolumns "
1291 i = find_token(document.body, '\\begin_inset Formula', i)
1294 j = find_end_of_inset(document.body, i)
1296 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1299 lines = document.body[i:j]
1300 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1301 code = "\n".join(lines)
1306 n = code.find("\\multicolumn", k)
1307 # no need to convert degenerated multicolumn cells,
1308 # they work in old LyX versions as "math ERT"
1309 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1310 ert = put_cmd_in_ert(code)
1311 document.body[i:j+1] = ert
1317 i = find_end_of_inset(document.body, i)
1322 def revert_jss(document):
1323 " Reverts JSS In_Preamble commands to ERT in preamble "
1325 if document.textclass != "jss":
1334 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1335 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1338 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1340 endh = find_end_of_inset(document.body, h)
1341 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1342 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1346 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1348 endm = find_end_of_inset(document.body, m)
1349 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1350 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1354 j = find_token(document.body, "\\begin_inset Flex Code", j)
1356 # assure that we are not in a Code Chunk inset
1357 if document.body[j][-1] == "e":
1358 endj = find_end_of_inset(document.body, j)
1359 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1360 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1366 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1368 endk = find_end_of_inset(document.body, k)
1369 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1370 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1374 n = find_token(document.body, "\\begin_inset Flex URL", n)
1376 endn = find_end_of_inset(document.body, n)
1377 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1378 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1380 # now revert the In_Preamble layouts
1382 i = find_token(document.body, "\\begin_layout Title", 0)
1385 j = find_end_of_layout(document.body, i)
1387 document.warning("Malformed LyX document: Can't find end of Title layout")
1390 content = lyx2latex(document, document.body[i:j + 1])
1391 add_to_preamble(document, ["\\title{" + content + "}"])
1392 del document.body[i:j + 1]
1394 i = find_token(document.body, "\\begin_layout Author", 0)
1397 j = find_end_of_layout(document.body, i)
1399 document.warning("Malformed LyX document: Can't find end of Author layout")
1402 content = lyx2latex(document, document.body[i:j + 1])
1403 add_to_preamble(document, ["\\author{" + content + "}"])
1404 del document.body[i:j + 1]
1406 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1409 j = find_end_of_layout(document.body, i)
1411 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1414 content = lyx2latex(document, document.body[i:j + 1])
1415 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1416 del document.body[i:j + 1]
1418 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1421 j = find_end_of_layout(document.body, i)
1423 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1426 content = lyx2latex(document, document.body[i:j + 1])
1427 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1428 del document.body[i:j + 1]
1430 i = find_token(document.body, "\\begin_layout Short Title", 0)
1433 j = find_end_of_layout(document.body, i)
1435 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1438 content = lyx2latex(document, document.body[i:j + 1])
1439 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1440 del document.body[i:j + 1]
1442 i = find_token(document.body, "\\begin_layout Abstract", 0)
1445 j = find_end_of_layout(document.body, i)
1447 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1450 content = lyx2latex(document, document.body[i:j + 1])
1451 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1452 del document.body[i:j + 1]
1454 i = find_token(document.body, "\\begin_layout Keywords", 0)
1457 j = find_end_of_layout(document.body, i)
1459 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1462 content = lyx2latex(document, document.body[i:j + 1])
1463 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1464 del document.body[i:j + 1]
1466 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1469 j = find_end_of_layout(document.body, i)
1471 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1474 content = lyx2latex(document, document.body[i:j + 1])
1475 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1476 del document.body[i:j + 1]
1478 i = find_token(document.body, "\\begin_layout Address", 0)
1481 j = find_end_of_layout(document.body, i)
1483 document.warning("Malformed LyX document: Can't find end of Address layout")
1486 content = lyx2latex(document, document.body[i:j + 1])
1487 add_to_preamble(document, ["\\Address{" + content + "}"])
1488 del document.body[i:j + 1]
1489 # finally handle the code layouts
1494 while m != -1 or j != -1 or h != -1 or k != -1:
1497 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1499 endh = find_end_of_inset(document.body, h)
1500 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1501 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1502 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1506 j = find_token(document.body, "\\begin_layout Code Input", j)
1508 endj = find_end_of_layout(document.body, j)
1509 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1510 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1511 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1512 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1513 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1517 k = find_token(document.body, "\\begin_layout Code Output", k)
1519 endk = find_end_of_layout(document.body, k)
1520 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1521 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1522 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1523 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1524 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1528 m = find_token(document.body, "\\begin_layout Code", m)
1530 endm = find_end_of_layout(document.body, m)
1531 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1532 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1533 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1534 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1535 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1539 def convert_subref(document):
1540 " converts sub: ref prefixes to subref: "
1543 rx = re.compile(r'^name \"sub:(.+)$')
1546 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1549 j = find_end_of_inset(document.body, i)
1551 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1555 for p in range(i, j):
1556 m = rx.match(document.body[p])
1559 document.body[p] = "name \"subsec:" + label
1563 rx = re.compile(r'^reference \"sub:(.+)$')
1566 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1569 j = find_end_of_inset(document.body, i)
1571 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1575 for p in range(i, j):
1576 m = rx.match(document.body[p])
1579 document.body[p] = "reference \"subsec:" + label
1585 def revert_subref(document):
1586 " reverts subref: ref prefixes to sub: "
1589 rx = re.compile(r'^name \"subsec:(.+)$')
1592 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1595 j = find_end_of_inset(document.body, i)
1597 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1601 for p in range(i, j):
1602 m = rx.match(document.body[p])
1605 document.body[p] = "name \"sub:" + label
1610 rx = re.compile(r'^reference \"subsec:(.+)$')
1613 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1616 j = find_end_of_inset(document.body, i)
1618 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1622 for p in range(i, j):
1623 m = rx.match(document.body[p])
1626 document.body[p] = "reference \"sub:" + label
1631 def convert_nounzip(document):
1632 " remove the noUnzip parameter of graphics insets "
1634 rx = re.compile(r'\s*noUnzip\s*$')
1637 i = find_token(document.body, "\\begin_inset Graphics", i)
1640 j = find_end_of_inset(document.body, i)
1642 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1646 k = find_re(document.body, rx, i, j)
1648 del document.body[k]
1653 def convert_revert_external_bbox(document, forward):
1654 " add units to bounding box of external insets "
1656 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1659 i = find_token(document.body, "\\begin_inset External", i)
1662 j = find_end_of_inset(document.body, i)
1664 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1667 k = find_re(document.body, rx, i, j)
1671 tokens = document.body[k].split()
1673 for t in range(1, 5):
1676 for t in range(1, 5):
1677 tokens[t] = length_in_bp(tokens[t])
1678 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1679 tokens[3] + " " + tokens[4]
1683 def convert_external_bbox(document):
1684 convert_revert_external_bbox(document, True)
1687 def revert_external_bbox(document):
1688 convert_revert_external_bbox(document, False)
1691 def revert_tcolorbox_1(document):
1692 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1695 i = find_token(document.header, "tcolorbox", i)
1701 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1704 flexEnd = find_end_of_inset(document.body, flex)
1705 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1706 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1707 flexEnd = find_end_of_inset(document.body, flex)
1709 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1711 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1712 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1716 def revert_tcolorbox_2(document):
1717 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1720 i = find_token(document.header, "tcolorbox", i)
1726 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1729 flexEnd = find_end_of_inset(document.body, flex)
1730 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1731 flexEnd = find_end_of_inset(document.body, flex)
1732 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1733 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1737 def revert_tcolorbox_3(document):
1738 " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1741 i = find_token(document.header, "tcolorbox", i)
1747 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1750 flexEnd = find_end_of_inset(document.body, flex)
1751 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1752 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1753 flexEnd = find_end_of_inset(document.body, flex)
1754 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1755 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1759 def revert_tcolorbox_4(document):
1760 " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1763 i = find_token(document.header, "tcolorbox", i)
1769 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1772 flexEnd = find_end_of_inset(document.body, flex)
1773 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1774 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1775 flexEnd = find_end_of_inset(document.body, flex)
1776 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1777 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1781 def revert_tcolorbox_5(document):
1782 " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1785 i = find_token(document.header, "tcolorbox", i)
1791 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1794 flexEnd = find_end_of_inset(document.body, flex)
1795 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1796 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1797 flexEnd = find_end_of_inset(document.body, flex)
1798 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1799 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1803 def revert_tcolorbox_6(document):
1804 " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1807 i = find_token(document.header, "tcolorbox", i)
1813 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1816 flexEnd = find_end_of_inset(document.body, flex)
1817 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1818 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1819 flexEnd = find_end_of_inset(document.body, flex)
1820 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1821 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1825 def revert_tcolorbox_7(document):
1826 " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1829 i = find_token(document.header, "tcolorbox", i)
1835 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1838 flexEnd = find_end_of_inset(document.body, flex)
1839 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1840 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1841 flexEnd = find_end_of_inset(document.body, flex)
1842 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1843 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1847 def revert_tcolorbox_8(document):
1848 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1854 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1856 j = find_end_of_layout(document.body, i)
1857 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1858 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1859 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1860 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1862 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1864 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1865 k = find_end_of_inset(document.body, j)
1866 k = find_token(document.body, "\\end_inset", k + 1)
1867 k = find_token(document.body, "\\end_inset", k + 1)
1869 k = find_token(document.body, "\\end_inset", k + 1)
1870 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1871 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1872 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1878 def revert_moderncv_1(document):
1879 " Reverts the new inset of moderncv to TeX-code in preamble "
1881 if document.textclass != "moderncv":
1887 # at first revert the new styles
1889 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1892 j = find_end_of_layout(document.body, i)
1894 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1897 content = lyx2latex(document, document.body[i:j + 1])
1898 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1899 del document.body[i:j + 1]
1901 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1904 j = find_end_of_layout(document.body, i)
1906 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1909 content = lyx2latex(document, document.body[i:j + 1])
1910 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1911 del document.body[i:j + 1]
1912 # now change the new styles to the obsolete ones
1914 i = find_token(document.body, "\\begin_layout Name", 0)
1917 j = find_end_of_layout(document.body, i)
1919 document.warning("Malformed LyX document: Can't find end of Name layout")
1922 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1923 if lineArg > j and j != 0:
1926 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1927 # we have to assure that no other inset is in the Argument
1928 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1929 endInset = find_token(document.body, "\\end_inset", beginPlain)
1932 while beginInset < endInset and beginInset != -1:
1933 beginInset = find_token(document.body, "\\begin_inset", k)
1934 endInset = find_token(document.body, "\\end_inset", l)
1937 Arg2 = document.body[l + 5 : l + 6]
1939 document.body[i : i + 1]= ["\\begin_layout FirstName"]
1940 # delete the Argument inset
1941 del( document.body[endInset - 2 : endInset + 3])
1942 del( document.body[lineArg : beginPlain + 1])
1943 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
1946 def revert_moderncv_2(document):
1947 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
1949 if document.textclass != "moderncv":
1956 i = find_token(document.body, "\\begin_layout Phone", i)
1959 j = find_end_of_layout(document.body, i)
1961 document.warning("Malformed LyX document: Can't find end of Phone layout")
1964 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1965 if lineArg > j and j != 0:
1969 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1970 # we have to assure that no other inset is in the Argument
1971 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1972 endInset = find_token(document.body, "\\end_inset", beginPlain)
1975 while beginInset < endInset and beginInset != -1:
1976 beginInset = find_token(document.body, "\\begin_inset", k)
1977 endInset = find_token(document.body, "\\end_inset", l)
1980 Arg = document.body[beginPlain + 1 : beginPlain + 2]
1982 if Arg[0] == "mobile":
1983 document.body[i : i + 1]= ["\\begin_layout Mobile"]
1985 document.body[i : i + 1]= ["\\begin_layout Fax"]
1986 # delete the Argument inset
1987 del(document.body[endInset - 2 : endInset + 1])
1988 del(document.body[lineArg : beginPlain + 3])
1992 def convert_moderncv_phone(document):
1993 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
1995 if document.textclass != "moderncv":
2002 "Mobile" : "mobile",
2006 rx = re.compile(r'^\\begin_layout (\S+)$')
2008 # substitute \fax and \mobile by \phone[fax] and \phone[mobile], respectively
2009 i = find_token(document.body, "\\begin_layout", i)
2013 m = rx.match(document.body[i])
2017 if val not in list(phone_dict.keys()):
2020 j = find_end_of_layout(document.body, i)
2022 document.warning("Malformed LyX document: Can't find end of Mobile layout")
2026 document.body[i : i + 1] = ["\\begin_layout Phone", "\\begin_inset Argument 1", "status open", "",
2027 "\\begin_layout Plain Layout", phone_dict[val], "\\end_layout", "",
2031 def convert_moderncv_name(document):
2032 " Convert the FirstName and LastName layout of moderncv to the general Name layout "
2034 if document.textclass != "moderncv":
2037 fnb = 0 # Begin of FirstName inset
2038 fne = 0 # End of FirstName inset
2039 lnb = 0 # Begin of LastName (FamilyName) inset
2040 lne = 0 # End of LastName (FamilyName) inset
2041 nb = 0 # Begin of substituting Name inset
2042 ne = 0 # End of substituting Name inset
2043 FirstName = [] # FirstName content
2044 FamilyName = [] # LastName content
2048 fnb = find_token(document.body, "\\begin_layout FirstName", fnb)
2050 fne = find_end_of_layout(document.body, fnb)
2052 document.warning("Malformed LyX document: Can't find end of FirstName layout")
2054 FirstName = document.body[fnb + 1 : fne]
2056 lnb = find_token(document.body, "\\begin_layout FamilyName", lnb)
2058 lne = find_end_of_layout(document.body, lnb)
2060 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
2062 FamilyName = document.body[lnb + 1 : lne]
2063 # Determine the region for the substituting Name layout
2064 if fnb == -1 and lnb == -1: # Neither FirstName nor FamilyName exists -> Do nothing
2066 elif fnb == -1: # Only FamilyName exists -> New Name insets replaces that
2069 elif lnb == -1: # Only FirstName exists -> New Name insets replaces that
2072 elif fne > lne: # FirstName position before FamilyName -> New Name insets spans
2073 nb = lnb # from FamilyName begin
2074 ne = fne # to FirstName end
2075 else: # FirstName position before FamilyName -> New Name insets spans
2076 nb = fnb # from FirstName begin
2077 ne = lne # to FamilyName end
2079 # Insert the substituting layout now. If FirstName exists, use an otpional argument.
2081 document.body[nb : ne + 1] = ["\\begin_layout Name"] + FamilyName + ["\\end_layout", ""]
2083 document.body[nb : ne + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2084 "\\begin_layout Plain Layout"] + FirstName + ["\\end_layout", "",
2085 "\\end_inset", ""] + FamilyName + ["\\end_layout", ""]
2088 def revert_achemso(document):
2089 " Reverts the flex inset Latin to TeX code "
2091 if document.textclass != "achemso":
2096 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2098 j = find_end_of_inset(document.body, i)
2102 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2103 endPlain = find_end_of_layout(document.body, beginPlain)
2104 content = lyx2latex(document, document.body[beginPlain : endPlain])
2105 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2107 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2112 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2113 "\\font_sf_scale", "\\font_tt_scale"]
2114 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2115 fontquotes = [True, True, True, True, False, False]
2117 def convert_fontsettings(document):
2118 " Duplicate font settings "
2120 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2122 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2123 use_non_tex_fonts = "false"
2125 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2127 for f in fontsettings:
2128 i = find_token(document.header, f + " ", 0)
2130 document.warning("Malformed LyX document: No " + f + "!")
2132 # note that with i = -1, this will insert at the end
2134 value = fontdefaults[j]
2136 value = document.header[i][len(f):].strip()
2138 if use_non_tex_fonts == "true":
2139 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2141 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2143 if use_non_tex_fonts == "true":
2144 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2146 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2150 def revert_fontsettings(document):
2151 " Merge font settings "
2153 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2155 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2156 use_non_tex_fonts = "false"
2158 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2160 for f in fontsettings:
2161 i = find_token(document.header, f + " ", 0)
2163 document.warning("Malformed LyX document: No " + f + "!")
2166 line = get_value(document.header, f, i)
2169 q2 = line.find('"', q1+1)
2170 q3 = line.find('"', q2+1)
2171 q4 = line.find('"', q3+1)
2172 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2173 document.warning("Malformed LyX document: Missing quotes!")
2176 if use_non_tex_fonts == "true":
2177 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2179 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2181 if use_non_tex_fonts == "true":
2182 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2184 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2188 def revert_solution(document):
2189 " Reverts the solution environment of the theorem module to TeX code "
2191 # Do we use one of the modules that provides Solution?
2193 mods = document.get_module_list()
2195 if mod == "theorems-std" or mod == "theorems-bytype" \
2196 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2206 i = find_token(document.body, "\\begin_layout Solution", i)
2210 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2211 if is_starred == True:
2213 LyXName = "Solution*"
2214 theoremName = "newtheorem*"
2217 LyXName = "Solution"
2218 theoremName = "newtheorem"
2220 j = find_end_of_layout(document.body, i)
2222 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2226 # if this is not a consecutive env, add start command
2229 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2231 # has this a consecutive theorem of same type?
2232 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2234 # if this is not followed by a consecutive env, add end command
2236 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2238 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2240 add_to_preamble(document, "\\theoremstyle{definition}")
2241 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2242 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2243 (theoremName, LaTeXName))
2244 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2245 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2246 (theoremName, LaTeXName))
2248 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2252 def revert_verbatim_star(document):
2253 from lyx_2_1 import revert_verbatim
2254 revert_verbatim(document, True)
2257 def convert_save_props(document):
2258 " Add save_transient_properties parameter. "
2259 i = find_token(document.header, '\\begin_header', 0)
2261 document.warning("Malformed lyx document: Missing '\\begin_header'.")
2263 document.header.insert(i + 1, '\\save_transient_properties true')
2266 def revert_save_props(document):
2267 " Remove save_transient_properties parameter. "
2268 i = find_token(document.header, "\\save_transient_properties", 0)
2271 del document.header[i]
2274 def convert_info_tabular_feature(document):
2276 return arg.replace("inset-modify tabular", "tabular-feature")
2277 convert_info_insets(document, "shortcut(s)?|icon", f)
2280 def revert_info_tabular_feature(document):
2282 return arg.replace("tabular-feature", "inset-modify tabular")
2283 convert_info_insets(document, "shortcut(s)?|icon", f)
2290 supported_versions = ["2.2.0", "2.2"]
2292 [475, [convert_separator]],
2293 # nothing to do for 476: We consider it a bug that older versions
2294 # did not load amsmath automatically for these commands, and do not
2295 # want to hardcode amsmath off.
2301 [481, [convert_dashes]],
2302 [482, [convert_phrases]],
2303 [483, [convert_specialchar]],
2308 [488, [convert_newgloss]],
2309 [489, [convert_BoxFeatures]],
2310 [490, [convert_origin]],
2312 [492, [convert_colorbox]],
2315 [495, [convert_subref]],
2316 [496, [convert_nounzip]],
2317 [497, [convert_external_bbox]],
2319 [499, [convert_moderncv_phone, convert_moderncv_name]],
2321 [501, [convert_fontsettings]],
2324 [504, [convert_save_props]],
2326 [506, [convert_info_tabular_feature]],
2327 [507, [convert_longtable_label]],
2328 [508, [convert_parbreak]]
2332 [507, [revert_parbreak]],
2333 [506, [revert_longtable_label]],
2334 [505, [revert_info_tabular_feature]],
2336 [503, [revert_save_props]],
2337 [502, [revert_verbatim_star]],
2338 [501, [revert_solution]],
2339 [500, [revert_fontsettings]],
2340 [499, [revert_achemso]],
2341 [498, [revert_moderncv_1, revert_moderncv_2]],
2342 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2343 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2344 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2345 [496, [revert_external_bbox]],
2346 [495, []], # nothing to do since the noUnzip parameter was optional
2347 [494, [revert_subref]],
2348 [493, [revert_jss]],
2349 [492, [revert_mathmulticol]],
2350 [491, [revert_colorbox]],
2351 [490, [revert_textcolor]],
2352 [489, [revert_origin]],
2353 [488, [revert_BoxFeatures]],
2354 [487, [revert_newgloss, revert_glossgroup]],
2355 [486, [revert_forest]],
2356 [485, [revert_ex_itemargs]],
2357 [484, [revert_sigplan_doi]],
2358 [483, [revert_georgian]],
2359 [482, [revert_specialchar]],
2360 [481, [revert_phrases]],
2361 [480, [revert_dashes]],
2362 [479, [revert_question_env]],
2363 [478, [revert_beamer_lemma]],
2364 [477, [revert_xarrow]],
2365 [476, [revert_swissgerman]],
2366 [475, [revert_smash]],
2367 [474, [revert_separator]]
2371 if __name__ == "__main__":