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"
622 and (words[1] in ["CommandInset", "ERT", "External", "Formula",
623 "FormulaMacro", "Graphics", "IPA", "listings"]
624 or ' '.join(words[1:]) == "Flex Code")):
625 # must not replace anything in insets that store LaTeX contents in .lyx files
626 # (math and command insets without overridden read() and write() methods
627 # filtering out IPA makes Text::readParToken() more simple
628 # skip ERT as well since it is not needed there
629 # Flex Code is logical markup, typically rendered as typewriter
630 j = find_end_of_inset(document.body, i)
632 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
637 if document.body[i] == "\\begin_layout LyX-Code":
638 j = find_end_of_layout(document.body, i)
640 document.warning("Malformed LyX document: "
641 "Can't find end of %s layout at line %d" % (words[1],i))
647 if len(words) > 0 and words[0] in ["\\leftindent", "\\paragraph_spacing", "\\align", "\\labelwidthstring"]:
648 # skip paragraph parameters (bug 10243)
652 j = document.body[i].find("--")
655 front = document.body[i][:j]
656 back = document.body[i][j+2:]
657 # We can have an arbitrary number of consecutive hyphens.
658 # These must be split into the corresponding number of two and three hyphens
659 # We must match what LaTeX does: First try emdash, then endash, then single hyphen
660 if back.find("-") == 0:
663 document.body.insert(i+1, back)
664 document.body[i] = front + "\\threehyphens"
667 document.body.insert(i+1, back)
668 document.body[i] = front + "\\twohyphens"
672 while i < len(document.body):
673 line = document.body[i]
674 while (line.endswith(r"-\SpecialChar \textcompwordmark{}") and
675 document.body[i+1].startswith("-")):
676 line = line.replace(r"\SpecialChar \textcompwordmark{}",
677 document.body.pop(i+1))
678 document.body[i] = line
681 # Return number of the next line to check for dashes.
682 def _dashes_next_line(document, i):
684 words = document.body[i].split()
685 # skip paragraph parameters (bug 10243):
686 if words and words[0] in ["\\leftindent", "\\paragraph_spacing",
687 "\\align", "\\labelwidthstring"]:
689 words = document.body[i].split()
690 # some insets should be skipped in revert_dashes (cf. convert_dashes)
691 if (len(words) > 1 and words[0] == "\\begin_inset" and
692 words[1] in ["CommandInset", "ERT", "External", "Formula",
693 "FormulaMacro", "Graphics", "IPA", "listings"]):
694 j = find_end_of_inset(document.body, i)
696 document.warning("Malformed LyX document: Can't find end of "
697 + words[1] + " inset at line " + str(i))
702 def revert_dashes(document):
704 Prevent ligatures of existing --- and --.
705 Convert \\twohyphens and \\threehyphens to -- and ---.
706 Remove preamble code from 2.3->2.2 conversion.
708 # Remove preamble code from 2.3->2.2 conversion:
709 for i, line in enumerate(document.preamble):
710 if (line == '% Added by lyx2lyx' and
711 document.preamble[i+1] == r'\renewcommand{\textendash}{--}' and
712 document.preamble[i+2] == r'\renewcommand{\textemdash}{---}'):
713 del document.preamble[i:i+3]
715 # Prevent ligation of hyphens:
717 while i < len(document.body)-1:
718 # increment i, skip some insets (cf. convert_dashes)
719 i = _dashes_next_line(document, i)
720 line = document.body[i]
722 line = line.replace("--", "-\\SpecialChar \\textcompwordmark{}\n-")
723 parts = line.split('\n')
725 document.body[i:i+1] = parts
727 # Convert \twohyphens and \threehyphens:
729 while i < len(document.body):
730 # skip some insets (see convert_dashes())
731 i = _dashes_next_line(document, i-1)
733 if document.body[i].find("\\twohyphens") >= 0:
734 document.body[i] = document.body[i].replace("\\twohyphens", "--")
736 if document.body[i].find("\\threehyphens") >= 0:
737 document.body[i] = document.body[i].replace("\\threehyphens", "---")
739 if replaced and i+1 < len(document.body) and \
740 (document.body[i+1].find("\\") != 0 or \
741 document.body[i+1].find("\\twohyphens") == 0 or
742 document.body[i+1].find("\\threehyphens") == 0) and \
743 len(document.body[i]) + len(document.body[i+1]) <= 80:
744 document.body[i] = document.body[i] + document.body[i+1]
745 document.body[i+1:i+2] = []
750 # order is important for the last three!
751 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
753 def is_part_of_converted_phrase(line, j, phrase):
754 "is phrase part of an already converted phrase?"
756 converted = "\\SpecialCharNoPassThru \\" + p
757 pos = j + len(phrase) - len(converted)
759 if line[pos:pos+len(converted)] == converted:
764 def convert_phrases(document):
765 "convert special phrases from plain text to \\SpecialCharNoPassThru"
767 if document.backend != "latex":
770 for phrase in phrases:
772 while i < len(document.body):
773 words = document.body[i].split()
774 if len(words) > 1 and words[0] == "\\begin_inset" and \
775 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
776 # must not replace anything in insets that store LaTeX contents in .lyx files
777 # (math and command insets withut overridden read() and write() methods
778 j = find_end_of_inset(document.body, i)
780 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
785 if document.body[i].find("\\") == 0:
788 j = document.body[i].find(phrase)
792 if not is_part_of_converted_phrase(document.body[i], j, phrase):
793 front = document.body[i][:j]
794 back = document.body[i][j+len(phrase):]
796 document.body.insert(i+1, back)
797 # We cannot use SpecialChar since we do not know whether we are outside passThru
798 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
802 def revert_phrases(document):
803 "convert special phrases to plain text"
806 while i < len(document.body):
807 words = document.body[i].split()
808 if len(words) > 1 and words[0] == "\\begin_inset" and \
809 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
810 # see convert_phrases
811 j = find_end_of_inset(document.body, i)
813 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
819 for phrase in phrases:
820 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
821 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
822 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
824 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
825 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
827 if replaced and i+1 < len(document.body) and \
828 (document.body[i+1].find("\\") != 0 or \
829 document.body[i+1].find("\\SpecialChar") == 0) and \
830 len(document.body[i]) + len(document.body[i+1]) <= 80:
831 document.body[i] = document.body[i] + document.body[i+1]
832 document.body[i+1:i+2] = []
837 def convert_specialchar_internal(document, forward):
838 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
839 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
840 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
841 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
842 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
843 "\\LaTeX":"LaTeX" # must be after LaTeX2e
847 while i < len(document.body):
848 words = document.body[i].split()
849 if len(words) > 1 and words[0] == "\\begin_inset" and \
850 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
851 # see convert_phrases
852 j = find_end_of_inset(document.body, i)
854 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
859 for key, value in specialchars.items():
861 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
862 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
864 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
865 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
869 def convert_specialchar(document):
870 "convert special characters to new syntax"
871 convert_specialchar_internal(document, True)
874 def revert_specialchar(document):
875 "convert special characters to old syntax"
876 convert_specialchar_internal(document, False)
879 def revert_georgian(document):
880 "Set the document language to English but assure Georgian output"
882 if document.language == "georgian":
883 document.language = "english"
884 i = find_token(document.header, "\\language georgian", 0)
886 document.header[i] = "\\language english"
887 j = find_token(document.header, "\\language_package default", 0)
889 document.header[j] = "\\language_package babel"
890 k = find_token(document.header, "\\options", 0)
892 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
894 l = find_token(document.header, "\\use_default_options", 0)
895 document.header.insert(l + 1, "\\options georgian")
898 def revert_sigplan_doi(document):
899 " Reverts sigplanconf DOI layout to ERT "
901 if document.textclass != "sigplanconf":
906 i = find_token(document.body, "\\begin_layout DOI", i)
909 j = find_end_of_layout(document.body, i)
911 document.warning("Malformed LyX document: Can't find end of DOI layout")
915 content = lyx2latex(document, document.body[i:j + 1])
916 add_to_preamble(document, ["\\doi{" + content + "}"])
917 del document.body[i:j + 1]
921 def revert_ex_itemargs(document):
922 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
924 if not "linguistics" in document.get_module_list():
928 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
930 i = find_token(document.body, "\\begin_inset Argument item:", i)
933 j = find_end_of_inset(document.body, i)
934 # Find containing paragraph layout
935 parent = get_containing_layout(document.body, i)
937 document.warning("Malformed LyX document: Can't find parent paragraph layout")
941 layoutname = parent[0]
942 if layoutname in example_layouts:
943 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
944 endPlain = find_end_of_layout(document.body, beginPlain)
945 content = document.body[beginPlain + 1 : endPlain]
946 del document.body[i:j+1]
947 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
948 document.body[parbeg : parbeg] = subst
952 def revert_forest(document):
953 " Reverts the forest environment (Linguistics module) to TeX-code "
955 if not "linguistics" in document.get_module_list():
960 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
963 j = find_end_of_inset(document.body, i)
965 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
969 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
970 endPlain = find_end_of_layout(document.body, beginPlain)
971 content = lyx2latex(document, document.body[beginPlain : endPlain])
973 add_to_preamble(document, ["\\usepackage{forest}"])
975 document.body[i:j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
979 def revert_glossgroup(document):
980 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
982 if not "linguistics" in document.get_module_list():
987 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
990 j = find_end_of_inset(document.body, i)
992 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
996 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
997 endPlain = find_end_of_layout(document.body, beginPlain)
998 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
1000 document.body[i:j + 1] = ["{", "", content, "", "}"]
1001 # no need to reset i
1004 def revert_newgloss(document):
1005 " Reverts the new Glosse insets (Linguistics module) to the old format "
1007 if not "linguistics" in document.get_module_list():
1010 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1011 for glosse in glosses:
1014 i = find_token(document.body, glosse, i)
1017 j = find_end_of_inset(document.body, i)
1019 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1023 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
1024 endarg = find_end_of_inset(document.body, arg)
1027 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
1028 if argbeginPlain == -1:
1029 document.warning("Malformed LyX document: Can't find arg plain Layout")
1032 argendPlain = find_end_of_inset(document.body, argbeginPlain)
1033 argcontent = lyx2verbatim(document, document.body[argbeginPlain : argendPlain - 2])
1035 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
1036 argcontent, "\\end_layout"]
1038 # remove Arg insets and paragraph, if it only contains this inset
1039 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
1040 del document.body[arg - 1 : endarg + 4]
1042 del document.body[arg : endarg + 1]
1044 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1045 endPlain = find_end_of_layout(document.body, beginPlain)
1046 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
1048 document.body[beginPlain + 1:endPlain] = [content]
1051 # Dissolve ERT insets
1052 for glosse in glosses:
1055 i = find_token(document.body, glosse, i)
1058 j = find_end_of_inset(document.body, i)
1060 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1064 ert = find_token(document.body, "\\begin_inset ERT", i, j)
1067 ertend = find_end_of_inset(document.body, ert)
1069 document.warning("Malformed LyX document: Can't find end of ERT inset")
1072 ertcontent = get_ert(document.body, ert, True)
1073 document.body[ert : ertend + 1] = [ertcontent]
1077 def convert_newgloss(document):
1078 " Converts Glosse insets (Linguistics module) to the new format "
1080 if not "linguistics" in document.get_module_list():
1083 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1084 for glosse in glosses:
1087 i = find_token(document.body, glosse, i)
1090 j = find_end_of_inset(document.body, i)
1092 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1099 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
1100 if beginPlain == -1:
1102 endPlain = find_end_of_layout(document.body, beginPlain)
1104 document.warning("Malformed LyX document: Can't find end of Glosse layout")
1108 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
1109 if glt != -1 and document.body[glt + 1].startswith("glt"):
1110 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1111 argcontent = document.body[glt + 1 : endPlain]
1112 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
1113 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
1114 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
1115 "\\end_layout", "", "\\end_inset"]
1117 content = document.body[beginPlain + 1 : endPlain]
1118 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
1119 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
1121 endPlain = find_end_of_layout(document.body, beginPlain)
1123 j = find_end_of_inset(document.body, i)
1128 def convert_BoxFeatures(document):
1129 " adds new box features "
1133 i = find_token(document.body, "height_special", i)
1136 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1140 def revert_BoxFeatures(document):
1141 " outputs new box features as TeX code "
1145 defaultThick = "0.4pt"
1146 defaultShadow = "4pt"
1148 i = find_token(document.body, "thickness", i)
1151 binset = find_token(document.body, "\\begin_inset Box", i - 11)
1152 if binset == -1 or binset != i - 11:
1154 continue # then "thickness" is is just a word in the text
1155 einset = find_end_of_inset(document.body, binset)
1157 document.warning("Malformed LyX document: Can't find end of box inset!")
1160 # read out the values
1161 beg = document.body[i].find('"');
1162 end = document.body[i].rfind('"');
1163 thickness = document.body[i][beg+1:end];
1164 beg = document.body[i+1].find('"');
1165 end = document.body[i+1].rfind('"');
1166 separation = document.body[i+1][beg+1:end];
1167 beg = document.body[i+2].find('"');
1168 end = document.body[i+2].rfind('"');
1169 shadowsize = document.body[i+2][beg+1:end];
1170 # delete the specification
1171 del document.body[i:i+3]
1173 # first output the closing brace
1174 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1175 document.body[einset -1 : einset - 1] = put_cmd_in_ert("}")
1176 # we have now the problem that if there is already \(f)colorbox in ERT around the inset
1177 # the ERT from this routine must be around it
1178 regexp = re.compile(r'^.*colorbox{.*$')
1179 pos = find_re(document.body, regexp, binset - 4)
1180 if pos != -1 and pos == binset - 4:
1184 # now output the lengths
1185 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1186 document.body[pos : pos] = put_cmd_in_ert("{")
1187 if thickness != defaultThick:
1188 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness]
1189 if separation != defaultSep and thickness == defaultThick:
1190 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation]
1191 if separation != defaultSep and thickness != defaultThick:
1192 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1193 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1194 document.body[pos + 5 : pos +6] = ["{\\backslash shadowsize " + shadowsize]
1195 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1196 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1197 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1198 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1199 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1200 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1203 def convert_origin(document):
1204 " Insert the origin tag "
1206 i = find_token(document.header, "\\textclass ", 0)
1208 document.warning("Malformed LyX document: No \\textclass!!")
1210 if document.dir == u'':
1214 if document.systemlyxdir and document.systemlyxdir != u'':
1216 if os.path.isabs(document.dir):
1217 absdir = os.path.normpath(document.dir)
1219 absdir = os.path.normpath(os.path.abspath(document.dir))
1220 if os.path.isabs(document.systemlyxdir):
1221 abssys = os.path.normpath(document.systemlyxdir)
1223 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1224 relpath = os.path.relpath(absdir, abssys)
1225 if relpath.find(u'..') == 0:
1230 origin = document.dir.replace(u'\\', u'/') + u'/'
1232 origin = os.path.join(u"/systemlyxdir", relpath).replace(u'\\', u'/') + u'/'
1233 document.header[i:i] = ["\\origin " + origin]
1236 def revert_origin(document):
1237 " Remove the origin tag "
1239 i = find_token(document.header, "\\origin ", 0)
1241 document.warning("Malformed LyX document: No \\origin!!")
1243 del document.header[i]
1246 color_names = ["brown", "darkgray", "gray", \
1247 "lightgray", "lime", "olive", "orange", \
1248 "pink", "purple", "teal", "violet"]
1250 def revert_textcolor(document):
1251 " revert new \\textcolor colors to TeX code "
1257 i = find_token(document.body, "\\color ", i)
1261 for color in list(color_names):
1262 if document.body[i] == "\\color " + color:
1263 # register that xcolor must be loaded in the preamble
1266 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1267 # find the next \\color and/or the next \\end_layout
1268 j = find_token(document.body, "\\color", i + 1)
1269 k = find_token(document.body, "\\end_layout", i + 1)
1270 if j == -1 and k != -1:
1273 # first output the closing brace
1275 document.body[k: k] = put_cmd_in_ert("}")
1277 document.body[j: j] = put_cmd_in_ert("}")
1278 # now output the \textcolor command
1279 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1283 def convert_colorbox(document):
1284 " adds color settings for boxes "
1288 i = find_token(document.body, "shadowsize", i)
1291 document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1295 def revert_colorbox(document):
1296 " outputs color settings for boxes as TeX code "
1299 defaultframecolor = "black"
1300 defaultbackcolor = "none"
1302 i = find_token(document.body, "framecolor", i)
1305 binset = find_token(document.body, "\\begin_inset Box", i - 14)
1308 einset = find_end_of_inset(document.body, binset)
1310 document.warning("Malformed LyX document: Can't find end of box inset!")
1312 # read out the values
1313 beg = document.body[i].find('"');
1314 end = document.body[i].rfind('"');
1315 framecolor = document.body[i][beg+1:end];
1316 beg = document.body[i + 1].find('"');
1317 end = document.body[i + 1].rfind('"');
1318 backcolor = document.body[i+1][beg+1:end];
1319 # delete the specification
1320 del document.body[i:i + 2]
1322 # first output the closing brace
1323 if framecolor != defaultframecolor or backcolor != defaultbackcolor:
1324 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1325 document.body[einset : einset] = put_cmd_in_ert("}")
1326 # determine the box type
1327 isBox = find_token(document.body, "\\begin_inset Box Boxed", binset)
1328 # now output the box commands
1329 if (framecolor != defaultframecolor and isBox == binset) or (backcolor != defaultbackcolor and isBox == binset):
1330 document.body[i - 14 : i - 14] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1331 # in the case we must also change the box type because the ERT code adds a frame
1332 document.body[i - 4] = "\\begin_inset Box Frameless"
1333 # if has_inner_box 0 we must set it and use_makebox to 1
1334 ibox = find_token(document.body, "has_inner_box", i - 4)
1335 if ibox == -1 or ibox != i - 1:
1336 document.warning("Malformed LyX document: Can't find has_inner_box statement!")
1338 # read out the value
1339 innerbox = document.body[ibox][-1:];
1341 document.body[ibox] = "has_inner_box 1"
1342 document.body[ibox + 3] = "use_makebox 1"
1343 if backcolor != defaultbackcolor and isBox != binset:
1344 document.body[i - 14 : i - 14] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
1347 def revert_mathmulticol(document):
1348 " Convert formulas to ERT if they contain multicolumns "
1352 i = find_token(document.body, '\\begin_inset Formula', i)
1355 j = find_end_of_inset(document.body, i)
1357 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1360 lines = document.body[i:j]
1361 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1362 code = "\n".join(lines)
1367 n = code.find("\\multicolumn", k)
1368 # no need to convert degenerated multicolumn cells,
1369 # they work in old LyX versions as "math ERT"
1370 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1371 ert = put_cmd_in_ert(code)
1372 document.body[i:j+1] = ert
1378 i = find_end_of_inset(document.body, i)
1383 def revert_jss(document):
1384 " Reverts JSS In_Preamble commands to ERT in preamble "
1386 if document.textclass != "jss":
1395 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1396 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1399 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1401 endh = find_end_of_inset(document.body, h)
1402 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1403 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1407 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1409 endm = find_end_of_inset(document.body, m)
1410 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1411 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1415 j = find_token(document.body, "\\begin_inset Flex Code", j)
1417 # assure that we are not in a Code Chunk inset
1418 if document.body[j][-1] == "e":
1419 endj = find_end_of_inset(document.body, j)
1420 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1421 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1427 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1429 endk = find_end_of_inset(document.body, k)
1430 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1431 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1435 n = find_token(document.body, "\\begin_inset Flex URL", n)
1437 endn = find_end_of_inset(document.body, n)
1438 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1439 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1441 # now revert the In_Preamble layouts
1443 i = find_token(document.body, "\\begin_layout Title", 0)
1446 j = find_end_of_layout(document.body, i)
1448 document.warning("Malformed LyX document: Can't find end of Title layout")
1451 content = lyx2latex(document, document.body[i:j + 1])
1452 add_to_preamble(document, ["\\title{" + content + "}"])
1453 del document.body[i:j + 1]
1455 i = find_token(document.body, "\\begin_layout Author", 0)
1458 j = find_end_of_layout(document.body, i)
1460 document.warning("Malformed LyX document: Can't find end of Author layout")
1463 content = lyx2latex(document, document.body[i:j + 1])
1464 add_to_preamble(document, ["\\author{" + content + "}"])
1465 del document.body[i:j + 1]
1467 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1470 j = find_end_of_layout(document.body, i)
1472 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1475 content = lyx2latex(document, document.body[i:j + 1])
1476 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1477 del document.body[i:j + 1]
1479 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1482 j = find_end_of_layout(document.body, i)
1484 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1487 content = lyx2latex(document, document.body[i:j + 1])
1488 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1489 del document.body[i:j + 1]
1491 i = find_token(document.body, "\\begin_layout Short Title", 0)
1494 j = find_end_of_layout(document.body, i)
1496 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1499 content = lyx2latex(document, document.body[i:j + 1])
1500 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1501 del document.body[i:j + 1]
1503 i = find_token(document.body, "\\begin_layout Abstract", 0)
1506 j = find_end_of_layout(document.body, i)
1508 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1511 content = lyx2latex(document, document.body[i:j + 1])
1512 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1513 del document.body[i:j + 1]
1515 i = find_token(document.body, "\\begin_layout Keywords", 0)
1518 j = find_end_of_layout(document.body, i)
1520 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1523 content = lyx2latex(document, document.body[i:j + 1])
1524 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1525 del document.body[i:j + 1]
1527 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1530 j = find_end_of_layout(document.body, i)
1532 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1535 content = lyx2latex(document, document.body[i:j + 1])
1536 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1537 del document.body[i:j + 1]
1539 i = find_token(document.body, "\\begin_layout Address", 0)
1542 j = find_end_of_layout(document.body, i)
1544 document.warning("Malformed LyX document: Can't find end of Address layout")
1547 content = lyx2latex(document, document.body[i:j + 1])
1548 add_to_preamble(document, ["\\Address{" + content + "}"])
1549 del document.body[i:j + 1]
1550 # finally handle the code layouts
1555 while m != -1 or j != -1 or h != -1 or k != -1:
1558 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1560 endh = find_end_of_inset(document.body, h)
1561 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1562 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1563 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1567 j = find_token(document.body, "\\begin_layout Code Input", j)
1569 endj = find_end_of_layout(document.body, j)
1570 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1571 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1572 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1573 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1574 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1578 k = find_token(document.body, "\\begin_layout Code Output", k)
1580 endk = find_end_of_layout(document.body, k)
1581 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1582 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1583 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1584 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1585 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1589 m = find_token(document.body, "\\begin_layout Code", m)
1591 endm = find_end_of_layout(document.body, m)
1592 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1593 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1594 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1595 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1596 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1600 def convert_subref(document):
1601 " converts sub: ref prefixes to subref: "
1604 rx = re.compile(r'^name \"sub:(.+)$')
1607 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1610 j = find_end_of_inset(document.body, i)
1612 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1616 for p in range(i, j):
1617 m = rx.match(document.body[p])
1620 document.body[p] = "name \"subsec:" + label
1624 rx = re.compile(r'^reference \"sub:(.+)$')
1627 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1630 j = find_end_of_inset(document.body, i)
1632 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1636 for p in range(i, j):
1637 m = rx.match(document.body[p])
1640 document.body[p] = "reference \"subsec:" + label
1646 def revert_subref(document):
1647 " reverts subref: ref prefixes to sub: "
1650 rx = re.compile(r'^name \"subsec:(.+)$')
1653 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1656 j = find_end_of_inset(document.body, i)
1658 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1662 for p in range(i, j):
1663 m = rx.match(document.body[p])
1666 document.body[p] = "name \"sub:" + label
1671 rx = re.compile(r'^reference \"subsec:(.+)$')
1674 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1677 j = find_end_of_inset(document.body, i)
1679 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1683 for p in range(i, j):
1684 m = rx.match(document.body[p])
1687 document.body[p] = "reference \"sub:" + label
1692 def convert_nounzip(document):
1693 " remove the noUnzip parameter of graphics insets "
1695 rx = re.compile(r'\s*noUnzip\s*$')
1698 i = find_token(document.body, "\\begin_inset Graphics", i)
1701 j = find_end_of_inset(document.body, i)
1703 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1707 k = find_re(document.body, rx, i, j)
1709 del document.body[k]
1714 def convert_revert_external_bbox(document, forward):
1715 " add units to bounding box of external insets "
1717 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1720 i = find_token(document.body, "\\begin_inset External", i)
1723 j = find_end_of_inset(document.body, i)
1725 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1728 k = find_re(document.body, rx, i, j)
1732 tokens = document.body[k].split()
1734 for t in range(1, 5):
1737 for t in range(1, 5):
1738 tokens[t] = length_in_bp(tokens[t])
1739 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1740 tokens[3] + " " + tokens[4]
1744 def convert_external_bbox(document):
1745 convert_revert_external_bbox(document, True)
1748 def revert_external_bbox(document):
1749 convert_revert_external_bbox(document, False)
1752 def revert_tcolorbox_1(document):
1753 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1756 i = find_token(document.header, "tcolorbox", i)
1762 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1765 flexEnd = find_end_of_inset(document.body, flex)
1766 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1767 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1768 flexEnd = find_end_of_inset(document.body, flex)
1770 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1772 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1773 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1777 def revert_tcolorbox_2(document):
1778 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1781 i = find_token(document.header, "tcolorbox", i)
1787 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1790 flexEnd = find_end_of_inset(document.body, flex)
1791 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1792 flexEnd = find_end_of_inset(document.body, flex)
1793 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1794 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1798 def revert_tcolorbox_3(document):
1799 " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1802 i = find_token(document.header, "tcolorbox", i)
1808 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1811 flexEnd = find_end_of_inset(document.body, flex)
1812 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1813 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1814 flexEnd = find_end_of_inset(document.body, flex)
1815 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1816 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1820 def revert_tcolorbox_4(document):
1821 " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1824 i = find_token(document.header, "tcolorbox", i)
1830 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1833 flexEnd = find_end_of_inset(document.body, flex)
1834 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1835 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1836 flexEnd = find_end_of_inset(document.body, flex)
1837 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1838 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1842 def revert_tcolorbox_5(document):
1843 " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1846 i = find_token(document.header, "tcolorbox", i)
1852 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1855 flexEnd = find_end_of_inset(document.body, flex)
1856 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1857 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1858 flexEnd = find_end_of_inset(document.body, flex)
1859 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1860 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1864 def revert_tcolorbox_6(document):
1865 " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1868 i = find_token(document.header, "tcolorbox", i)
1874 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1877 flexEnd = find_end_of_inset(document.body, flex)
1878 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1879 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1880 flexEnd = find_end_of_inset(document.body, flex)
1881 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1882 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1886 def revert_tcolorbox_7(document):
1887 " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1890 i = find_token(document.header, "tcolorbox", i)
1896 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1899 flexEnd = find_end_of_inset(document.body, flex)
1900 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1901 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1902 flexEnd = find_end_of_inset(document.body, flex)
1903 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1904 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1908 def revert_tcolorbox_8(document):
1909 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1915 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1917 j = find_end_of_layout(document.body, i)
1918 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1919 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1920 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1921 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1923 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1925 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1926 k = find_end_of_inset(document.body, j)
1927 k = find_token(document.body, "\\end_inset", k + 1)
1928 k = find_token(document.body, "\\end_inset", k + 1)
1930 k = find_token(document.body, "\\end_inset", k + 1)
1931 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1932 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1933 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1939 def revert_moderncv_1(document):
1940 " Reverts the new inset of moderncv to TeX-code in preamble "
1942 if document.textclass != "moderncv":
1948 # at first revert the new styles
1950 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1953 j = find_end_of_layout(document.body, i)
1955 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1958 content = lyx2latex(document, document.body[i:j + 1])
1959 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1960 del document.body[i:j + 1]
1962 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1965 j = find_end_of_layout(document.body, i)
1967 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1970 content = lyx2latex(document, document.body[i:j + 1])
1971 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1972 del document.body[i:j + 1]
1973 # now change the new styles to the obsolete ones
1975 i = find_token(document.body, "\\begin_layout Name", 0)
1978 j = find_end_of_layout(document.body, i)
1980 document.warning("Malformed LyX document: Can't find end of Name layout")
1983 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1984 if lineArg > j and j != 0:
1987 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1988 # we have to assure that no other inset is in the Argument
1989 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1990 endInset = find_token(document.body, "\\end_inset", beginPlain)
1993 while beginInset < endInset and beginInset != -1:
1994 beginInset = find_token(document.body, "\\begin_inset", k)
1995 endInset = find_token(document.body, "\\end_inset", l)
1998 Arg2 = document.body[l + 5 : l + 6]
2000 document.body[i : i + 1]= ["\\begin_layout FirstName"]
2001 # delete the Argument inset
2002 del( document.body[endInset - 2 : endInset + 3])
2003 del( document.body[lineArg : beginPlain + 1])
2004 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
2007 def revert_moderncv_2(document):
2008 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
2010 if document.textclass != "moderncv":
2017 i = find_token(document.body, "\\begin_layout Phone", i)
2020 j = find_end_of_layout(document.body, i)
2022 document.warning("Malformed LyX document: Can't find end of Phone layout")
2025 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
2026 if lineArg > j and j != 0:
2030 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
2031 # we have to assure that no other inset is in the Argument
2032 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
2033 endInset = find_token(document.body, "\\end_inset", beginPlain)
2036 while beginInset < endInset and beginInset != -1:
2037 beginInset = find_token(document.body, "\\begin_inset", k)
2038 endInset = find_token(document.body, "\\end_inset", l)
2041 Arg = document.body[beginPlain + 1 : beginPlain + 2]
2043 if Arg[0] == "mobile":
2044 document.body[i : i + 1]= ["\\begin_layout Mobile"]
2046 document.body[i : i + 1]= ["\\begin_layout Fax"]
2047 # delete the Argument inset
2048 del(document.body[endInset - 2 : endInset + 1])
2049 del(document.body[lineArg : beginPlain + 3])
2053 def convert_moderncv_phone(document):
2054 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
2056 if document.textclass != "moderncv":
2063 "Mobile" : "mobile",
2067 rx = re.compile(r'^\\begin_layout (\S+)$')
2069 # substitute \fax and \mobile by \phone[fax] and \phone[mobile], respectively
2070 i = find_token(document.body, "\\begin_layout", i)
2074 m = rx.match(document.body[i])
2078 if val not in list(phone_dict.keys()):
2081 j = find_end_of_layout(document.body, i)
2083 document.warning("Malformed LyX document: Can't find end of Mobile layout")
2087 document.body[i : i + 1] = ["\\begin_layout Phone", "\\begin_inset Argument 1", "status open", "",
2088 "\\begin_layout Plain Layout", phone_dict[val], "\\end_layout", "",
2092 def convert_moderncv_name(document):
2093 " Convert the FirstName and LastName layout of moderncv to the general Name layout "
2095 if document.textclass != "moderncv":
2098 fnb = 0 # Begin of FirstName inset
2099 fne = 0 # End of FirstName inset
2100 lnb = 0 # Begin of LastName (FamilyName) inset
2101 lne = 0 # End of LastName (FamilyName) inset
2102 nb = 0 # Begin of substituting Name inset
2103 ne = 0 # End of substituting Name inset
2104 FirstName = [] # FirstName content
2105 FamilyName = [] # LastName content
2109 fnb = find_token(document.body, "\\begin_layout FirstName", fnb)
2111 fne = find_end_of_layout(document.body, fnb)
2113 document.warning("Malformed LyX document: Can't find end of FirstName layout")
2115 FirstName = document.body[fnb + 1 : fne]
2117 lnb = find_token(document.body, "\\begin_layout FamilyName", lnb)
2119 lne = find_end_of_layout(document.body, lnb)
2121 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
2123 FamilyName = document.body[lnb + 1 : lne]
2124 # Determine the region for the substituting Name layout
2125 if fnb == -1 and lnb == -1: # Neither FirstName nor FamilyName exists -> Do nothing
2127 elif fnb == -1: # Only FamilyName exists -> New Name insets replaces that
2130 elif lnb == -1: # Only FirstName exists -> New Name insets replaces that
2133 elif fne > lne: # FirstName position before FamilyName -> New Name insets spans
2134 nb = lnb # from FamilyName begin
2135 ne = fne # to FirstName end
2136 else: # FirstName position before FamilyName -> New Name insets spans
2137 nb = fnb # from FirstName begin
2138 ne = lne # to FamilyName end
2140 # Insert the substituting layout now. If FirstName exists, use an otpional argument.
2142 document.body[nb : ne + 1] = ["\\begin_layout Name"] + FamilyName + ["\\end_layout", ""]
2144 document.body[nb : ne + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2145 "\\begin_layout Plain Layout"] + FirstName + ["\\end_layout", "",
2146 "\\end_inset", ""] + FamilyName + ["\\end_layout", ""]
2149 def revert_achemso(document):
2150 " Reverts the flex inset Latin to TeX code "
2152 if document.textclass != "achemso":
2157 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2159 j = find_end_of_inset(document.body, i)
2163 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2164 endPlain = find_end_of_layout(document.body, beginPlain)
2165 content = lyx2latex(document, document.body[beginPlain : endPlain])
2166 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2168 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2173 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2174 "\\font_sf_scale", "\\font_tt_scale"]
2175 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2176 fontquotes = [True, True, True, True, False, False]
2178 def convert_fontsettings(document):
2179 " Duplicate font settings "
2181 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2183 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2184 use_non_tex_fonts = "false"
2186 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2188 for f in fontsettings:
2189 i = find_token(document.header, f + " ", 0)
2191 document.warning("Malformed LyX document: No " + f + "!")
2193 # note that with i = -1, this will insert at the end
2195 value = fontdefaults[j]
2197 value = document.header[i][len(f):].strip()
2199 if use_non_tex_fonts == "true":
2200 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2202 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2204 if use_non_tex_fonts == "true":
2205 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2207 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2211 def revert_fontsettings(document):
2212 " Merge font settings "
2214 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2216 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2217 use_non_tex_fonts = "false"
2219 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2221 for f in fontsettings:
2222 i = find_token(document.header, f + " ", 0)
2224 document.warning("Malformed LyX document: No " + f + "!")
2227 line = get_value(document.header, f, i)
2230 q2 = line.find('"', q1+1)
2231 q3 = line.find('"', q2+1)
2232 q4 = line.find('"', q3+1)
2233 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2234 document.warning("Malformed LyX document: Missing quotes!")
2237 if use_non_tex_fonts == "true":
2238 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2240 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2242 if use_non_tex_fonts == "true":
2243 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2245 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2249 def revert_solution(document):
2250 " Reverts the solution environment of the theorem module to TeX code "
2252 # Do we use one of the modules that provides Solution?
2254 mods = document.get_module_list()
2256 if mod == "theorems-std" or mod == "theorems-bytype" \
2257 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2267 i = find_token(document.body, "\\begin_layout Solution", i)
2271 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2272 if is_starred == True:
2274 LyXName = "Solution*"
2275 theoremName = "newtheorem*"
2278 LyXName = "Solution"
2279 theoremName = "newtheorem"
2281 j = find_end_of_layout(document.body, i)
2283 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2287 # if this is not a consecutive env, add start command
2290 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2292 # has this a consecutive theorem of same type?
2293 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2295 # if this is not followed by a consecutive env, add end command
2297 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2299 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2301 add_to_preamble(document, "\\theoremstyle{definition}")
2302 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2303 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2304 (theoremName, LaTeXName))
2305 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2306 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2307 (theoremName, LaTeXName))
2309 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2313 def revert_verbatim_star(document):
2314 from lyx_2_1 import revert_verbatim
2315 revert_verbatim(document, True)
2318 def convert_save_props(document):
2319 " Add save_transient_properties parameter. "
2320 i = find_token(document.header, '\\begin_header', 0)
2322 document.warning("Malformed lyx document: Missing '\\begin_header'.")
2324 document.header.insert(i + 1, '\\save_transient_properties true')
2327 def revert_save_props(document):
2328 " Remove save_transient_properties parameter. "
2329 i = find_token(document.header, "\\save_transient_properties", 0)
2332 del document.header[i]
2335 def convert_info_tabular_feature(document):
2337 return arg.replace("inset-modify tabular", "tabular-feature")
2338 convert_info_insets(document, "shortcut(s)?|icon", f)
2341 def revert_info_tabular_feature(document):
2343 return arg.replace("tabular-feature", "inset-modify tabular")
2344 convert_info_insets(document, "shortcut(s)?|icon", f)
2351 supported_versions = ["2.2.0", "2.2"]
2353 [475, [convert_separator]],
2354 # nothing to do for 476: We consider it a bug that older versions
2355 # did not load amsmath automatically for these commands, and do not
2356 # want to hardcode amsmath off.
2362 [481, [convert_dashes]],
2363 [482, [convert_phrases]],
2364 [483, [convert_specialchar]],
2369 [488, [convert_newgloss]],
2370 [489, [convert_BoxFeatures]],
2371 [490, [convert_origin]],
2373 [492, [convert_colorbox]],
2376 [495, [convert_subref]],
2377 [496, [convert_nounzip]],
2378 [497, [convert_external_bbox]],
2380 [499, [convert_moderncv_phone, convert_moderncv_name]],
2382 [501, [convert_fontsettings]],
2385 [504, [convert_save_props]],
2387 [506, [convert_info_tabular_feature]],
2388 [507, [convert_longtable_label]],
2389 [508, [convert_parbreak]]
2393 [507, [revert_parbreak]],
2394 [506, [revert_longtable_label]],
2395 [505, [revert_info_tabular_feature]],
2397 [503, [revert_save_props]],
2398 [502, [revert_verbatim_star]],
2399 [501, [revert_solution]],
2400 [500, [revert_fontsettings]],
2401 [499, [revert_achemso]],
2402 [498, [revert_moderncv_1, revert_moderncv_2]],
2403 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2404 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2405 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2406 [496, [revert_external_bbox]],
2407 [495, []], # nothing to do since the noUnzip parameter was optional
2408 [494, [revert_subref]],
2409 [493, [revert_jss]],
2410 [492, [revert_mathmulticol]],
2411 [491, [revert_colorbox]],
2412 [490, [revert_textcolor]],
2413 [489, [revert_origin]],
2414 [488, [revert_BoxFeatures]],
2415 [487, [revert_newgloss, revert_glossgroup]],
2416 [486, [revert_forest]],
2417 [485, [revert_ex_itemargs]],
2418 [484, [revert_sigplan_doi]],
2419 [483, [revert_georgian]],
2420 [482, [revert_specialchar]],
2421 [481, [revert_phrases]],
2422 [480, [revert_dashes]],
2423 [479, [revert_question_env]],
2424 [478, [revert_beamer_lemma]],
2425 [477, [revert_xarrow]],
2426 [476, [revert_swissgerman]],
2427 [475, [revert_smash]],
2428 [474, [revert_separator]]
2432 if __name__ == "__main__":