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,
33 lyx2latex, 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_end_of_inset, find_end_of_layout,
38 find_nonempty_line, find_re, find_slice, find_token, find_token_backwards,
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 dash_renew_lines = find_slice(document.preamble,
710 ['% Added by lyx2lyx',
711 r'\renewcommand{\textendash}{--}',
712 r'\renewcommand{\textemdash}{---}'])
713 del(document.preamble[dash_renew_lines])
714 # Prevent ligation of hyphens:
716 while i < len(document.body)-1:
717 # increment i, skip some insets (cf. convert_dashes)
718 i = _dashes_next_line(document, i)
719 line = document.body[i]
721 line = line.replace("--", "-\\SpecialChar \\textcompwordmark{}\n-")
722 document.body[i:i+1] = line.split('\n')
723 # Convert \twohyphens and \threehyphens:
725 while i < len(document.body):
726 # skip some insets (see convert_dashes())
727 i = _dashes_next_line(document, i-1)
729 if document.body[i].find("\\twohyphens") >= 0:
730 document.body[i] = document.body[i].replace("\\twohyphens", "--")
732 if document.body[i].find("\\threehyphens") >= 0:
733 document.body[i] = document.body[i].replace("\\threehyphens", "---")
735 if replaced and i+1 < len(document.body) and \
736 (document.body[i+1].find("\\") != 0 or \
737 document.body[i+1].find("\\twohyphens") == 0 or
738 document.body[i+1].find("\\threehyphens") == 0) and \
739 len(document.body[i]) + len(document.body[i+1]) <= 80:
740 document.body[i] = document.body[i] + document.body[i+1]
741 document.body[i+1:i+2] = []
746 # order is important for the last three!
747 phrases = ["LyX", "LaTeX2e", "LaTeX", "TeX"]
749 def is_part_of_converted_phrase(line, j, phrase):
750 "is phrase part of an already converted phrase?"
752 converted = "\\SpecialCharNoPassThru \\" + p
753 pos = j + len(phrase) - len(converted)
755 if line[pos:pos+len(converted)] == converted:
760 def convert_phrases(document):
761 "convert special phrases from plain text to \\SpecialCharNoPassThru"
763 if document.backend != "latex":
766 for phrase in phrases:
768 while i < len(document.body):
769 words = document.body[i].split()
770 if len(words) > 1 and words[0] == "\\begin_inset" and \
771 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
772 # must not replace anything in insets that store LaTeX contents in .lyx files
773 # (math and command insets withut overridden read() and write() methods
774 j = find_end_of_inset(document.body, i)
776 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
781 if document.body[i].find("\\") == 0:
784 j = document.body[i].find(phrase)
788 if not is_part_of_converted_phrase(document.body[i], j, phrase):
789 front = document.body[i][:j]
790 back = document.body[i][j+len(phrase):]
792 document.body.insert(i+1, back)
793 # We cannot use SpecialChar since we do not know whether we are outside passThru
794 document.body[i] = front + "\\SpecialCharNoPassThru \\" + phrase
798 def revert_phrases(document):
799 "convert special phrases to plain text"
802 while i < len(document.body):
803 words = document.body[i].split()
804 if len(words) > 1 and words[0] == "\\begin_inset" and \
805 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
806 # see convert_phrases
807 j = find_end_of_inset(document.body, i)
809 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
815 for phrase in phrases:
816 # we can replace SpecialChar since LyX ensures that it cannot be inserted into passThru parts
817 if document.body[i].find("\\SpecialChar \\" + phrase) >= 0:
818 document.body[i] = document.body[i].replace("\\SpecialChar \\" + phrase, phrase)
820 if document.body[i].find("\\SpecialCharNoPassThru \\" + phrase) >= 0:
821 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru \\" + phrase, phrase)
823 if replaced and i+1 < len(document.body) and \
824 (document.body[i+1].find("\\") != 0 or \
825 document.body[i+1].find("\\SpecialChar") == 0) and \
826 len(document.body[i]) + len(document.body[i+1]) <= 80:
827 document.body[i] = document.body[i] + document.body[i+1]
828 document.body[i+1:i+2] = []
833 def convert_specialchar_internal(document, forward):
834 specialchars = {"\\-":"softhyphen", "\\textcompwordmark{}":"ligaturebreak", \
835 "\\@.":"endofsentence", "\\ldots{}":"ldots", \
836 "\\menuseparator":"menuseparator", "\\slash{}":"breakableslash", \
837 "\\nobreakdash-":"nobreakdash", "\\LyX":"LyX", \
838 "\\TeX":"TeX", "\\LaTeX2e":"LaTeX2e", \
839 "\\LaTeX":"LaTeX" # must be after LaTeX2e
843 while i < len(document.body):
844 words = document.body[i].split()
845 if len(words) > 1 and words[0] == "\\begin_inset" and \
846 words[1] in ["CommandInset", "External", "Formula", "Graphics", "listings"]:
847 # see convert_phrases
848 j = find_end_of_inset(document.body, i)
850 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
855 for key, value in specialchars.items():
857 document.body[i] = document.body[i].replace("\\SpecialChar " + key, "\\SpecialChar " + value)
858 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + key, "\\SpecialCharNoPassThru " + value)
860 document.body[i] = document.body[i].replace("\\SpecialChar " + value, "\\SpecialChar " + key)
861 document.body[i] = document.body[i].replace("\\SpecialCharNoPassThru " + value, "\\SpecialCharNoPassThru " + key)
865 def convert_specialchar(document):
866 "convert special characters to new syntax"
867 convert_specialchar_internal(document, True)
870 def revert_specialchar(document):
871 "convert special characters to old syntax"
872 convert_specialchar_internal(document, False)
875 def revert_georgian(document):
876 "Set the document language to English but assure Georgian output"
878 if document.language == "georgian":
879 document.language = "english"
880 i = find_token(document.header, "\\language georgian", 0)
882 document.header[i] = "\\language english"
883 j = find_token(document.header, "\\language_package default", 0)
885 document.header[j] = "\\language_package babel"
886 k = find_token(document.header, "\\options", 0)
888 document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
890 l = find_token(document.header, "\\use_default_options", 0)
891 document.header.insert(l + 1, "\\options georgian")
894 def revert_sigplan_doi(document):
895 " Reverts sigplanconf DOI layout to ERT "
897 if document.textclass != "sigplanconf":
902 i = find_token(document.body, "\\begin_layout DOI", i)
905 j = find_end_of_layout(document.body, i)
907 document.warning("Malformed LyX document: Can't find end of DOI layout")
911 content = lyx2latex(document, document.body[i:j + 1])
912 add_to_preamble(document, ["\\doi{" + content + "}"])
913 del document.body[i:j + 1]
917 def revert_ex_itemargs(document):
918 " Reverts \\item arguments of the example environments (Linguistics module) to TeX-code "
920 if not "linguistics" in document.get_module_list():
924 example_layouts = ["Numbered Examples (consecutive)", "Subexample"]
926 i = find_token(document.body, "\\begin_inset Argument item:", i)
929 j = find_end_of_inset(document.body, i)
930 # Find containing paragraph layout
931 parent = get_containing_layout(document.body, i)
933 document.warning("Malformed LyX document: Can't find parent paragraph layout")
937 layoutname = parent[0]
938 if layoutname in example_layouts:
939 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
940 endPlain = find_end_of_layout(document.body, beginPlain)
941 content = document.body[beginPlain + 1 : endPlain]
942 del document.body[i:j+1]
943 subst = put_cmd_in_ert("[") + content + put_cmd_in_ert("]")
944 document.body[parbeg : parbeg] = subst
948 def revert_forest(document):
949 " Reverts the forest environment (Linguistics module) to TeX-code "
951 if not "linguistics" in document.get_module_list():
956 i = find_token(document.body, "\\begin_inset Flex Structure Tree", i)
959 j = find_end_of_inset(document.body, i)
961 document.warning("Malformed LyX document: Can't find end of Structure Tree inset")
965 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
966 endPlain = find_end_of_layout(document.body, beginPlain)
967 content = lyx2latex(document, document.body[beginPlain : endPlain])
969 add_to_preamble(document, ["\\usepackage{forest}"])
971 document.body[i:j + 1] = put_cmd_in_ert("\\begin{forest}" + content + "\\end{forest}")
975 def revert_glossgroup(document):
976 " Reverts the GroupGlossedWords inset (Linguistics module) to TeX-code "
978 if not "linguistics" in document.get_module_list():
983 i = find_token(document.body, "\\begin_inset Flex GroupGlossedWords", i)
986 j = find_end_of_inset(document.body, i)
988 document.warning("Malformed LyX document: Can't find end of GroupGlossedWords inset")
992 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
993 endPlain = find_end_of_layout(document.body, beginPlain)
994 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
996 document.body[i:j + 1] = ["{", "", content, "", "}"]
1000 def revert_newgloss(document):
1001 " Reverts the new Glosse insets (Linguistics module) to the old format "
1003 if not "linguistics" in document.get_module_list():
1006 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1007 for glosse in glosses:
1010 i = find_token(document.body, glosse, i)
1013 j = find_end_of_inset(document.body, i)
1015 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1019 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
1020 endarg = find_end_of_inset(document.body, arg)
1023 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
1024 if argbeginPlain == -1:
1025 document.warning("Malformed LyX document: Can't find arg plain Layout")
1028 argendPlain = find_end_of_inset(document.body, argbeginPlain)
1029 argcontent = lyx2verbatim(document, document.body[argbeginPlain : argendPlain - 2])
1031 document.body[j:j] = ["", "\\begin_layout Plain Layout","\\backslash", "glt ",
1032 argcontent, "\\end_layout"]
1034 # remove Arg insets and paragraph, if it only contains this inset
1035 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
1036 del document.body[arg - 1 : endarg + 4]
1038 del document.body[arg : endarg + 1]
1040 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1041 endPlain = find_end_of_layout(document.body, beginPlain)
1042 content = lyx2verbatim(document, document.body[beginPlain : endPlain])
1044 document.body[beginPlain + 1:endPlain] = [content]
1047 # Dissolve ERT insets
1048 for glosse in glosses:
1051 i = find_token(document.body, glosse, i)
1054 j = find_end_of_inset(document.body, i)
1056 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1060 ert = find_token(document.body, "\\begin_inset ERT", i, j)
1063 ertend = find_end_of_inset(document.body, ert)
1065 document.warning("Malformed LyX document: Can't find end of ERT inset")
1068 ertcontent = get_ert(document.body, ert, True)
1069 document.body[ert : ertend + 1] = [ertcontent]
1073 def convert_newgloss(document):
1074 " Converts Glosse insets (Linguistics module) to the new format "
1076 if not "linguistics" in document.get_module_list():
1079 glosses = ("\\begin_inset Flex Glosse", "\\begin_inset Flex Tri-Glosse")
1080 for glosse in glosses:
1083 i = find_token(document.body, glosse, i)
1086 j = find_end_of_inset(document.body, i)
1088 document.warning("Malformed LyX document: Can't find end of Glosse inset")
1095 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", k, j)
1096 if beginPlain == -1:
1098 endPlain = find_end_of_layout(document.body, beginPlain)
1100 document.warning("Malformed LyX document: Can't find end of Glosse layout")
1104 glt = find_token(document.body, "\\backslash", beginPlain, endPlain)
1105 if glt != -1 and document.body[glt + 1].startswith("glt"):
1106 document.body[glt + 1] = document.body[glt + 1].lstrip("glt").lstrip()
1107 argcontent = document.body[glt + 1 : endPlain]
1108 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset Argument 1", "status open", "",
1109 "\\begin_layout Plain Layout", "\\begin_inset ERT", "status open", "",
1110 "\\begin_layout Plain Layout", ""] + argcontent + ["\\end_layout", "", "\\end_inset", "",
1111 "\\end_layout", "", "\\end_inset"]
1113 content = document.body[beginPlain + 1 : endPlain]
1114 document.body[beginPlain + 1 : endPlain] = ["\\begin_inset ERT", "status open", "",
1115 "\\begin_layout Plain Layout"] + content + ["\\end_layout", "", "\\end_inset"]
1117 endPlain = find_end_of_layout(document.body, beginPlain)
1119 j = find_end_of_inset(document.body, i)
1124 def convert_BoxFeatures(document):
1125 " adds new box features "
1129 i = find_token(document.body, "height_special", i)
1132 document.body[i+1:i+1] = ['thickness "0.4pt"', 'separation "3pt"', 'shadowsize "4pt"']
1136 def revert_BoxFeatures(document):
1137 " outputs new box features as TeX code "
1141 defaultThick = "0.4pt"
1142 defaultShadow = "4pt"
1144 i = find_token(document.body, "thickness", i)
1147 binset = find_token(document.body, "\\begin_inset Box", i - 11)
1148 if binset == -1 or binset != i - 11:
1150 continue # then "thickness" is is just a word in the text
1151 einset = find_end_of_inset(document.body, binset)
1153 document.warning("Malformed LyX document: Can't find end of box inset!")
1156 # read out the values
1157 beg = document.body[i].find('"');
1158 end = document.body[i].rfind('"');
1159 thickness = document.body[i][beg+1:end];
1160 beg = document.body[i+1].find('"');
1161 end = document.body[i+1].rfind('"');
1162 separation = document.body[i+1][beg+1:end];
1163 beg = document.body[i+2].find('"');
1164 end = document.body[i+2].rfind('"');
1165 shadowsize = document.body[i+2][beg+1:end];
1166 # delete the specification
1167 del document.body[i:i+3]
1169 # first output the closing brace
1170 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1171 document.body[einset -1 : einset - 1] = put_cmd_in_ert("}")
1172 # we have now the problem that if there is already \(f)colorbox in ERT around the inset
1173 # the ERT from this routine must be around it
1174 regexp = re.compile(r'^.*colorbox{.*$')
1175 pos = find_re(document.body, regexp, binset - 4)
1176 if pos != -1 and pos == binset - 4:
1180 # now output the lengths
1181 if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
1182 document.body[pos : pos] = put_cmd_in_ert("{")
1183 if thickness != defaultThick:
1184 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness]
1185 if separation != defaultSep and thickness == defaultThick:
1186 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation]
1187 if separation != defaultSep and thickness != defaultThick:
1188 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
1189 if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
1190 document.body[pos + 5 : pos +6] = ["{\\backslash shadowsize " + shadowsize]
1191 if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
1192 document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1193 if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
1194 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
1195 if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
1196 document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
1199 def convert_origin(document):
1200 " Insert the origin tag "
1202 i = find_token(document.header, "\\textclass ", 0)
1204 document.warning("Malformed LyX document: No \\textclass!!")
1206 if document.dir == u'':
1210 if document.systemlyxdir and document.systemlyxdir != u'':
1212 if os.path.isabs(document.dir):
1213 absdir = os.path.normpath(document.dir)
1215 absdir = os.path.normpath(os.path.abspath(document.dir))
1216 if os.path.isabs(document.systemlyxdir):
1217 abssys = os.path.normpath(document.systemlyxdir)
1219 abssys = os.path.normpath(os.path.abspath(document.systemlyxdir))
1220 relpath = os.path.relpath(absdir, abssys)
1221 if relpath.find(u'..') == 0:
1226 origin = document.dir.replace(u'\\', u'/') + u'/'
1228 origin = os.path.join(u"/systemlyxdir", relpath).replace(u'\\', u'/') + u'/'
1229 document.header[i:i] = ["\\origin " + origin]
1232 def revert_origin(document):
1233 " Remove the origin tag "
1235 i = find_token(document.header, "\\origin ", 0)
1237 document.warning("Malformed LyX document: No \\origin!!")
1239 del document.header[i]
1242 color_names = ["brown", "darkgray", "gray", \
1243 "lightgray", "lime", "olive", "orange", \
1244 "pink", "purple", "teal", "violet"]
1246 def revert_textcolor(document):
1247 " revert new \\textcolor colors to TeX code "
1253 i = find_token(document.body, "\\color ", i)
1257 for color in list(color_names):
1258 if document.body[i] == "\\color " + color:
1259 # register that xcolor must be loaded in the preamble
1262 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1263 # find the next \\color and/or the next \\end_layout
1264 j = find_token(document.body, "\\color", i + 1)
1265 k = find_token(document.body, "\\end_layout", i + 1)
1266 if j == -1 and k != -1:
1269 # first output the closing brace
1271 document.body[k: k] = put_cmd_in_ert("}")
1273 document.body[j: j] = put_cmd_in_ert("}")
1274 # now output the \textcolor command
1275 document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
1279 def convert_colorbox(document):
1280 " adds color settings for boxes "
1284 i = find_token(document.body, "shadowsize", i)
1287 document.body[i+1:i+1] = ['framecolor "black"', 'backgroundcolor "none"']
1291 def revert_colorbox(document):
1292 " outputs color settings for boxes as TeX code "
1295 defaultframecolor = "black"
1296 defaultbackcolor = "none"
1298 i = find_token(document.body, "framecolor", i)
1301 binset = find_token(document.body, "\\begin_inset Box", i - 14)
1304 einset = find_end_of_inset(document.body, binset)
1306 document.warning("Malformed LyX document: Can't find end of box inset!")
1308 # read out the values
1309 beg = document.body[i].find('"');
1310 end = document.body[i].rfind('"');
1311 framecolor = document.body[i][beg+1:end];
1312 beg = document.body[i + 1].find('"');
1313 end = document.body[i + 1].rfind('"');
1314 backcolor = document.body[i+1][beg+1:end];
1315 # delete the specification
1316 del document.body[i:i + 2]
1318 # first output the closing brace
1319 if framecolor != defaultframecolor or backcolor != defaultbackcolor:
1320 add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
1321 document.body[einset : einset] = put_cmd_in_ert("}")
1322 # determine the box type
1323 isBox = find_token(document.body, "\\begin_inset Box Boxed", binset)
1324 # now output the box commands
1325 if (framecolor != defaultframecolor and isBox == binset) or (backcolor != defaultbackcolor and isBox == binset):
1326 document.body[i - 14 : i - 14] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
1327 # in the case we must also change the box type because the ERT code adds a frame
1328 document.body[i - 4] = "\\begin_inset Box Frameless"
1329 # if has_inner_box 0 we must set it and use_makebox to 1
1330 ibox = find_token(document.body, "has_inner_box", i - 4)
1331 if ibox == -1 or ibox != i - 1:
1332 document.warning("Malformed LyX document: Can't find has_inner_box statement!")
1334 # read out the value
1335 innerbox = document.body[ibox][-1:];
1337 document.body[ibox] = "has_inner_box 1"
1338 document.body[ibox + 3] = "use_makebox 1"
1339 if backcolor != defaultbackcolor and isBox != binset:
1340 document.body[i - 14 : i - 14] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
1343 def revert_mathmulticol(document):
1344 " Convert formulas to ERT if they contain multicolumns "
1348 i = find_token(document.body, '\\begin_inset Formula', i)
1351 j = find_end_of_inset(document.body, i)
1353 document.warning("Malformed LyX document: Can't find end of Formula inset at line " + str(i))
1356 lines = document.body[i:j]
1357 lines[0] = lines[0].replace('\\begin_inset Formula', '').lstrip()
1358 code = "\n".join(lines)
1363 n = code.find("\\multicolumn", k)
1364 # no need to convert degenerated multicolumn cells,
1365 # they work in old LyX versions as "math ERT"
1366 if n != -1 and code.find("\\multicolumn{1}", k) != n:
1367 ert = put_cmd_in_ert(code)
1368 document.body[i:j+1] = ert
1374 i = find_end_of_inset(document.body, i)
1379 def revert_jss(document):
1380 " Reverts JSS In_Preamble commands to ERT in preamble "
1382 if document.textclass != "jss":
1391 # at first revert the inset layouts because they can be part of the In_Preamble layouts
1392 while m != -1 or j != -1 or h != -1 or k != -1 or n != -1:
1395 h = find_token(document.body, "\\begin_inset Flex Pkg", h)
1397 endh = find_end_of_inset(document.body, h)
1398 document.body[endh - 2 : endh + 1] = put_cmd_in_ert("}")
1399 document.body[h : h + 4] = put_cmd_in_ert("\\pkg{")
1403 m = find_token(document.body, "\\begin_inset Flex Proglang", m)
1405 endm = find_end_of_inset(document.body, m)
1406 document.body[endm - 2 : endm + 1] = put_cmd_in_ert("}")
1407 document.body[m : m + 4] = put_cmd_in_ert("\\proglang{")
1411 j = find_token(document.body, "\\begin_inset Flex Code", j)
1413 # assure that we are not in a Code Chunk inset
1414 if document.body[j][-1] == "e":
1415 endj = find_end_of_inset(document.body, j)
1416 document.body[endj - 2 : endj + 1] = put_cmd_in_ert("}")
1417 document.body[j : j + 4] = put_cmd_in_ert("\\code{")
1423 k = find_token(document.body, "\\begin_inset Flex E-mail", k)
1425 endk = find_end_of_inset(document.body, k)
1426 document.body[endk - 2 : endk + 1] = put_cmd_in_ert("}")
1427 document.body[k : k + 4] = put_cmd_in_ert("\\email{")
1431 n = find_token(document.body, "\\begin_inset Flex URL", n)
1433 endn = find_end_of_inset(document.body, n)
1434 document.body[endn - 2 : endn + 1] = put_cmd_in_ert("}")
1435 document.body[n : n + 4] = put_cmd_in_ert("\\url{")
1437 # now revert the In_Preamble layouts
1439 i = find_token(document.body, "\\begin_layout Title", 0)
1442 j = find_end_of_layout(document.body, i)
1444 document.warning("Malformed LyX document: Can't find end of Title layout")
1447 content = lyx2latex(document, document.body[i:j + 1])
1448 add_to_preamble(document, ["\\title{" + content + "}"])
1449 del document.body[i:j + 1]
1451 i = find_token(document.body, "\\begin_layout Author", 0)
1454 j = find_end_of_layout(document.body, i)
1456 document.warning("Malformed LyX document: Can't find end of Author layout")
1459 content = lyx2latex(document, document.body[i:j + 1])
1460 add_to_preamble(document, ["\\author{" + content + "}"])
1461 del document.body[i:j + 1]
1463 i = find_token(document.body, "\\begin_layout Plain Author", 0)
1466 j = find_end_of_layout(document.body, i)
1468 document.warning("Malformed LyX document: Can't find end of Plain Author layout")
1471 content = lyx2latex(document, document.body[i:j + 1])
1472 add_to_preamble(document, ["\\Plainauthor{" + content + "}"])
1473 del document.body[i:j + 1]
1475 i = find_token(document.body, "\\begin_layout Plain Title", 0)
1478 j = find_end_of_layout(document.body, i)
1480 document.warning("Malformed LyX document: Can't find end of Plain Title layout")
1483 content = lyx2latex(document, document.body[i:j + 1])
1484 add_to_preamble(document, ["\\Plaintitle{" + content + "}"])
1485 del document.body[i:j + 1]
1487 i = find_token(document.body, "\\begin_layout Short Title", 0)
1490 j = find_end_of_layout(document.body, i)
1492 document.warning("Malformed LyX document: Can't find end of Short Title layout")
1495 content = lyx2latex(document, document.body[i:j + 1])
1496 add_to_preamble(document, ["\\Shorttitle{" + content + "}"])
1497 del document.body[i:j + 1]
1499 i = find_token(document.body, "\\begin_layout Abstract", 0)
1502 j = find_end_of_layout(document.body, i)
1504 document.warning("Malformed LyX document: Can't find end of Abstract layout")
1507 content = lyx2latex(document, document.body[i:j + 1])
1508 add_to_preamble(document, ["\\Abstract{" + content + "}"])
1509 del document.body[i:j + 1]
1511 i = find_token(document.body, "\\begin_layout Keywords", 0)
1514 j = find_end_of_layout(document.body, i)
1516 document.warning("Malformed LyX document: Can't find end of Keywords layout")
1519 content = lyx2latex(document, document.body[i:j + 1])
1520 add_to_preamble(document, ["\\Keywords{" + content + "}"])
1521 del document.body[i:j + 1]
1523 i = find_token(document.body, "\\begin_layout Plain Keywords", 0)
1526 j = find_end_of_layout(document.body, i)
1528 document.warning("Malformed LyX document: Can't find end of Plain Keywords layout")
1531 content = lyx2latex(document, document.body[i:j + 1])
1532 add_to_preamble(document, ["\\Plainkeywords{" + content + "}"])
1533 del document.body[i:j + 1]
1535 i = find_token(document.body, "\\begin_layout Address", 0)
1538 j = find_end_of_layout(document.body, i)
1540 document.warning("Malformed LyX document: Can't find end of Address layout")
1543 content = lyx2latex(document, document.body[i:j + 1])
1544 add_to_preamble(document, ["\\Address{" + content + "}"])
1545 del document.body[i:j + 1]
1546 # finally handle the code layouts
1551 while m != -1 or j != -1 or h != -1 or k != -1:
1554 h = find_token(document.body, "\\begin_inset Flex Code Chunk", h)
1556 endh = find_end_of_inset(document.body, h)
1557 document.body[endh : endh + 1] = put_cmd_in_ert("\\end{CodeChunk}")
1558 document.body[h : h + 3] = put_cmd_in_ert("\\begin{CodeChunk}")
1559 document.body[h - 1 : h] = ["\\begin_layout Standard"]
1563 j = find_token(document.body, "\\begin_layout Code Input", j)
1565 endj = find_end_of_layout(document.body, j)
1566 document.body[endj : endj + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1567 document.body[endj + 3 : endj + 4] = put_cmd_in_ert("\\end{CodeInput}")
1568 document.body[endj + 13 : endj + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1569 document.body[j + 1 : j] = ["\\end_layout", "", "\\begin_layout Standard"]
1570 document.body[j : j + 1] = put_cmd_in_ert("\\begin{CodeInput}")
1574 k = find_token(document.body, "\\begin_layout Code Output", k)
1576 endk = find_end_of_layout(document.body, k)
1577 document.body[endk : endk + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1578 document.body[endk + 3 : endk + 4] = put_cmd_in_ert("\\end{CodeOutput}")
1579 document.body[endk + 13 : endk + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1580 document.body[k + 1 : k] = ["\\end_layout", "", "\\begin_layout Standard"]
1581 document.body[k : k + 1] = put_cmd_in_ert("\\begin{CodeOutput}")
1585 m = find_token(document.body, "\\begin_layout Code", m)
1587 endm = find_end_of_layout(document.body, m)
1588 document.body[endm : endm + 1] = ["\\end_layout", "", "\\begin_layout Standard"]
1589 document.body[endm + 3 : endm + 4] = put_cmd_in_ert("\\end{Code}")
1590 document.body[endm + 13 : endm + 13] = ["\\end_layout", "", "\\begin_layout Standard"]
1591 document.body[m + 1 : m] = ["\\end_layout", "", "\\begin_layout Standard"]
1592 document.body[m : m + 1] = put_cmd_in_ert("\\begin{Code}")
1596 def convert_subref(document):
1597 " converts sub: ref prefixes to subref: "
1600 rx = re.compile(r'^name \"sub:(.+)$')
1603 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1606 j = find_end_of_inset(document.body, i)
1608 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1612 for p in range(i, j):
1613 m = rx.match(document.body[p])
1616 document.body[p] = "name \"subsec:" + label
1620 rx = re.compile(r'^reference \"sub:(.+)$')
1623 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1626 j = find_end_of_inset(document.body, i)
1628 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1632 for p in range(i, j):
1633 m = rx.match(document.body[p])
1636 document.body[p] = "reference \"subsec:" + label
1642 def revert_subref(document):
1643 " reverts subref: ref prefixes to sub: "
1646 rx = re.compile(r'^name \"subsec:(.+)$')
1649 i = find_token(document.body, "\\begin_inset CommandInset label", i)
1652 j = find_end_of_inset(document.body, i)
1654 document.warning("Malformed LyX document: Can't find end of Label inset at line " + str(i))
1658 for p in range(i, j):
1659 m = rx.match(document.body[p])
1662 document.body[p] = "name \"sub:" + label
1667 rx = re.compile(r'^reference \"subsec:(.+)$')
1670 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1673 j = find_end_of_inset(document.body, i)
1675 document.warning("Malformed LyX document: Can't find end of Ref inset at line " + str(i))
1679 for p in range(i, j):
1680 m = rx.match(document.body[p])
1683 document.body[p] = "reference \"sub:" + label
1688 def convert_nounzip(document):
1689 " remove the noUnzip parameter of graphics insets "
1691 rx = re.compile(r'\s*noUnzip\s*$')
1694 i = find_token(document.body, "\\begin_inset Graphics", i)
1697 j = find_end_of_inset(document.body, i)
1699 document.warning("Malformed LyX document: Can't find end of graphics inset at line " + str(i))
1703 k = find_re(document.body, rx, i, j)
1705 del document.body[k]
1710 def convert_revert_external_bbox(document, forward):
1711 " add units to bounding box of external insets "
1713 rx = re.compile(r'^\s*boundingBox\s+\S+\s+\S+\s+\S+\s+\S+\s*$')
1716 i = find_token(document.body, "\\begin_inset External", i)
1719 j = find_end_of_inset(document.body, i)
1721 document.warning("Malformed LyX document: Can't find end of external inset at line " + str(i))
1724 k = find_re(document.body, rx, i, j)
1728 tokens = document.body[k].split()
1730 for t in range(1, 5):
1733 for t in range(1, 5):
1734 tokens[t] = length_in_bp(tokens[t])
1735 document.body[k] = "\tboundingBox " + tokens[1] + " " + tokens[2] + " " + \
1736 tokens[3] + " " + tokens[4]
1740 def convert_external_bbox(document):
1741 convert_revert_external_bbox(document, True)
1744 def revert_external_bbox(document):
1745 convert_revert_external_bbox(document, False)
1748 def revert_tcolorbox_1(document):
1749 " Reverts the Flex:Subtitle inset of tcolorbox to TeX-code "
1752 i = find_token(document.header, "tcolorbox", i)
1758 flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
1761 flexEnd = find_end_of_inset(document.body, flex)
1762 wasOpt = revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, False, True, False)
1763 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, False, False, False)
1764 flexEnd = find_end_of_inset(document.body, flex)
1766 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle")
1768 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\tcbsubtitle{")
1769 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("}")
1773 def revert_tcolorbox_2(document):
1774 " Reverts the Flex:Raster_Color_Box inset of tcolorbox to TeX-code "
1777 i = find_token(document.header, "tcolorbox", i)
1783 flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
1786 flexEnd = find_end_of_inset(document.body, flex)
1787 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1788 flexEnd = find_end_of_inset(document.body, flex)
1789 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{tcbraster}")
1790 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("\\end{tcbraster}")
1794 def revert_tcolorbox_3(document):
1795 " Reverts the Flex:Custom_Color_Box_1 inset of tcolorbox to TeX-code "
1798 i = find_token(document.header, "tcolorbox", i)
1804 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
1807 flexEnd = find_end_of_inset(document.body, flex)
1808 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1809 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1810 flexEnd = find_end_of_inset(document.body, flex)
1811 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxA}")
1812 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxA}")
1816 def revert_tcolorbox_4(document):
1817 " Reverts the Flex:Custom_Color_Box_2 inset of tcolorbox to TeX-code "
1820 i = find_token(document.header, "tcolorbox", i)
1826 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
1829 flexEnd = find_end_of_inset(document.body, flex)
1830 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1831 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1832 flexEnd = find_end_of_inset(document.body, flex)
1833 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxB}")
1834 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxB}")
1838 def revert_tcolorbox_5(document):
1839 " Reverts the Flex:Custom_Color_Box_3 inset of tcolorbox to TeX-code "
1842 i = find_token(document.header, "tcolorbox", i)
1848 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
1851 flexEnd = find_end_of_inset(document.body, flex)
1852 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1853 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1854 flexEnd = find_end_of_inset(document.body, flex)
1855 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxC}")
1856 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxC}")
1860 def revert_tcolorbox_6(document):
1861 " Reverts the Flex:Custom_Color_Box_4 inset of tcolorbox to TeX-code "
1864 i = find_token(document.header, "tcolorbox", i)
1870 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
1873 flexEnd = find_end_of_inset(document.body, flex)
1874 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1875 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1876 flexEnd = find_end_of_inset(document.body, flex)
1877 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxD}")
1878 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxD}")
1882 def revert_tcolorbox_7(document):
1883 " Reverts the Flex:Custom_Color_Box_5 inset of tcolorbox to TeX-code "
1886 i = find_token(document.header, "tcolorbox", i)
1892 flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
1895 flexEnd = find_end_of_inset(document.body, flex)
1896 revert_Argument_to_TeX_brace(document, flex, flexEnd, 1, 1, True, True, False)
1897 revert_Argument_to_TeX_brace(document, flex, 0, 2, 2, True, False, False)
1898 flexEnd = find_end_of_inset(document.body, flex)
1899 document.body[flex + 0 : flex + 4] = put_cmd_in_ert("\\begin{cBoxE}")
1900 document.body[flexEnd + 4 : flexEnd + 7] = put_cmd_in_ert("{}\\end{cBoxE}")
1904 def revert_tcolorbox_8(document):
1905 " Reverts the layout New Color Box Type of tcolorbox to TeX-code "
1911 i = find_token(document.body, "\\begin_layout New Color Box Type", i)
1913 j = find_end_of_layout(document.body, i)
1914 wasOpt = revert_Argument_to_TeX_brace(document, i, j, 1, 1, False, True, False)
1915 revert_Argument_to_TeX_brace(document, i, 0, 2, 2, False, False, True)
1916 revert_Argument_to_TeX_brace(document, i, 0, 3, 4, False, True, False)
1917 document.body[i] = document.body[i].replace("\\begin_layout New Color Box Type", "\\begin_layout Standard")
1919 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox")
1921 document.body[i + 1 : i + 1] = put_cmd_in_ert("\\newtcolorbox{")
1922 k = find_end_of_inset(document.body, j)
1923 k = find_token(document.body, "\\end_inset", k + 1)
1924 k = find_token(document.body, "\\end_inset", k + 1)
1926 k = find_token(document.body, "\\end_inset", k + 1)
1927 document.body[k + 2 : j + 2] = put_cmd_in_ert("{")
1928 j = find_token(document.body, "\\begin_layout Standard", j + 1)
1929 document.body[j - 2 : j - 2] = put_cmd_in_ert("}")
1935 def revert_moderncv_1(document):
1936 " Reverts the new inset of moderncv to TeX-code in preamble "
1938 if document.textclass != "moderncv":
1944 # at first revert the new styles
1946 i = find_token(document.body, "\\begin_layout CVIcons", 0)
1949 j = find_end_of_layout(document.body, i)
1951 document.warning("Malformed LyX document: Can't find end of CVIcons layout")
1954 content = lyx2latex(document, document.body[i:j + 1])
1955 add_to_preamble(document, ["\\moderncvicons{" + content + "}"])
1956 del document.body[i:j + 1]
1958 i = find_token(document.body, "\\begin_layout CVColumnWidth", 0)
1961 j = find_end_of_layout(document.body, i)
1963 document.warning("Malformed LyX document: Can't find end of CVColumnWidth layout")
1966 content = lyx2latex(document, document.body[i:j + 1])
1967 add_to_preamble(document, ["\\setlength{\hintscolumnwidth}{" + content + "}"])
1968 del document.body[i:j + 1]
1969 # now change the new styles to the obsolete ones
1971 i = find_token(document.body, "\\begin_layout Name", 0)
1974 j = find_end_of_layout(document.body, i)
1976 document.warning("Malformed LyX document: Can't find end of Name layout")
1979 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
1980 if lineArg > j and j != 0:
1983 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
1984 # we have to assure that no other inset is in the Argument
1985 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
1986 endInset = find_token(document.body, "\\end_inset", beginPlain)
1989 while beginInset < endInset and beginInset != -1:
1990 beginInset = find_token(document.body, "\\begin_inset", k)
1991 endInset = find_token(document.body, "\\end_inset", l)
1994 Arg2 = document.body[l + 5 : l + 6]
1996 document.body[i : i + 1]= ["\\begin_layout FirstName"]
1997 # delete the Argument inset
1998 del( document.body[endInset - 2 : endInset + 3])
1999 del( document.body[lineArg : beginPlain + 1])
2000 document.body[i + 4 : i + 4]= ["\\begin_layout FamilyName"] + Arg2 + ["\\end_layout"] + [""]
2003 def revert_moderncv_2(document):
2004 " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
2006 if document.textclass != "moderncv":
2013 i = find_token(document.body, "\\begin_layout Phone", i)
2016 j = find_end_of_layout(document.body, i)
2018 document.warning("Malformed LyX document: Can't find end of Phone layout")
2021 lineArg = find_token(document.body, "\\begin_inset Argument 1", i)
2022 if lineArg > j and j != 0:
2026 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", lineArg)
2027 # we have to assure that no other inset is in the Argument
2028 beginInset = find_token(document.body, "\\begin_inset", beginPlain)
2029 endInset = find_token(document.body, "\\end_inset", beginPlain)
2032 while beginInset < endInset and beginInset != -1:
2033 beginInset = find_token(document.body, "\\begin_inset", k)
2034 endInset = find_token(document.body, "\\end_inset", l)
2037 Arg = document.body[beginPlain + 1 : beginPlain + 2]
2039 if Arg[0] == "mobile":
2040 document.body[i : i + 1]= ["\\begin_layout Mobile"]
2042 document.body[i : i + 1]= ["\\begin_layout Fax"]
2043 # delete the Argument inset
2044 del(document.body[endInset - 2 : endInset + 1])
2045 del(document.body[lineArg : beginPlain + 3])
2049 def convert_moderncv_phone(document):
2050 " Convert the Fax and Mobile inset of moderncv to the new phone inset "
2052 if document.textclass != "moderncv":
2059 "Mobile" : "mobile",
2063 rx = re.compile(r'^\\begin_layout (\S+)$')
2065 # substitute \fax and \mobile by \phone[fax] and \phone[mobile], respectively
2066 i = find_token(document.body, "\\begin_layout", i)
2070 m = rx.match(document.body[i])
2074 if val not in list(phone_dict.keys()):
2077 j = find_end_of_layout(document.body, i)
2079 document.warning("Malformed LyX document: Can't find end of Mobile layout")
2083 document.body[i : i + 1] = ["\\begin_layout Phone", "\\begin_inset Argument 1", "status open", "",
2084 "\\begin_layout Plain Layout", phone_dict[val], "\\end_layout", "",
2088 def convert_moderncv_name(document):
2089 " Convert the FirstName and LastName layout of moderncv to the general Name layout "
2091 if document.textclass != "moderncv":
2094 fnb = 0 # Begin of FirstName inset
2095 fne = 0 # End of FirstName inset
2096 lnb = 0 # Begin of LastName (FamilyName) inset
2097 lne = 0 # End of LastName (FamilyName) inset
2098 nb = 0 # Begin of substituting Name inset
2099 ne = 0 # End of substituting Name inset
2100 FirstName = [] # FirstName content
2101 FamilyName = [] # LastName content
2105 fnb = find_token(document.body, "\\begin_layout FirstName", fnb)
2107 fne = find_end_of_layout(document.body, fnb)
2109 document.warning("Malformed LyX document: Can't find end of FirstName layout")
2111 FirstName = document.body[fnb + 1 : fne]
2113 lnb = find_token(document.body, "\\begin_layout FamilyName", lnb)
2115 lne = find_end_of_layout(document.body, lnb)
2117 document.warning("Malformed LyX document: Can't find end of FamilyName layout")
2119 FamilyName = document.body[lnb + 1 : lne]
2120 # Determine the region for the substituting Name layout
2121 if fnb == -1 and lnb == -1: # Neither FirstName nor FamilyName exists -> Do nothing
2123 elif fnb == -1: # Only FamilyName exists -> New Name insets replaces that
2126 elif lnb == -1: # Only FirstName exists -> New Name insets replaces that
2129 elif fne > lne: # FirstName position before FamilyName -> New Name insets spans
2130 nb = lnb # from FamilyName begin
2131 ne = fne # to FirstName end
2132 else: # FirstName position before FamilyName -> New Name insets spans
2133 nb = fnb # from FirstName begin
2134 ne = lne # to FamilyName end
2136 # Insert the substituting layout now. If FirstName exists, use an otpional argument.
2138 document.body[nb : ne + 1] = ["\\begin_layout Name"] + FamilyName + ["\\end_layout", ""]
2140 document.body[nb : ne + 1] = ["\\begin_layout Name", "\\begin_inset Argument 1", "status open", "",
2141 "\\begin_layout Plain Layout"] + FirstName + ["\\end_layout", "",
2142 "\\end_inset", ""] + FamilyName + ["\\end_layout", ""]
2145 def revert_achemso(document):
2146 " Reverts the flex inset Latin to TeX code "
2148 if document.textclass != "achemso":
2153 i = find_token(document.body, "\\begin_inset Flex Latin", i)
2155 j = find_end_of_inset(document.body, i)
2159 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2160 endPlain = find_end_of_layout(document.body, beginPlain)
2161 content = lyx2latex(document, document.body[beginPlain : endPlain])
2162 document.body[i:j + 1] = put_cmd_in_ert("\\latin{" + content + "}")
2164 document.warning("Malformed LyX document: Can't find end of flex inset Latin")
2169 fontsettings = ["\\font_roman", "\\font_sans", "\\font_typewriter", "\\font_math", \
2170 "\\font_sf_scale", "\\font_tt_scale"]
2171 fontdefaults = ["default", "default", "default", "auto", "100", "100"]
2172 fontquotes = [True, True, True, True, False, False]
2174 def convert_fontsettings(document):
2175 " Duplicate font settings "
2177 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2179 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2180 use_non_tex_fonts = "false"
2182 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2184 for f in fontsettings:
2185 i = find_token(document.header, f + " ", 0)
2187 document.warning("Malformed LyX document: No " + f + "!")
2189 # note that with i = -1, this will insert at the end
2191 value = fontdefaults[j]
2193 value = document.header[i][len(f):].strip()
2195 if use_non_tex_fonts == "true":
2196 document.header[i:i+1] = [f + ' "' + fontdefaults[j] + '" "' + value + '"']
2198 document.header[i:i+1] = [f + ' "' + value + '" "' + fontdefaults[j] + '"']
2200 if use_non_tex_fonts == "true":
2201 document.header[i:i+1] = [f + ' ' + fontdefaults[j] + ' ' + value]
2203 document.header[i:i+1] = [f + ' ' + value + ' ' + fontdefaults[j]]
2207 def revert_fontsettings(document):
2208 " Merge font settings "
2210 i = find_token(document.header, "\\use_non_tex_fonts ", 0)
2212 document.warning("Malformed LyX document: No \\use_non_tex_fonts!")
2213 use_non_tex_fonts = "false"
2215 use_non_tex_fonts = get_value(document.header, "\\use_non_tex_fonts", i)
2217 for f in fontsettings:
2218 i = find_token(document.header, f + " ", 0)
2220 document.warning("Malformed LyX document: No " + f + "!")
2223 line = get_value(document.header, f, i)
2226 q2 = line.find('"', q1+1)
2227 q3 = line.find('"', q2+1)
2228 q4 = line.find('"', q3+1)
2229 if q1 == -1 or q2 == -1 or q3 == -1 or q4 == -1:
2230 document.warning("Malformed LyX document: Missing quotes!")
2233 if use_non_tex_fonts == "true":
2234 document.header[i:i+1] = [f + ' ' + line[q3+1:q4]]
2236 document.header[i:i+1] = [f + ' ' + line[q1+1:q2]]
2238 if use_non_tex_fonts == "true":
2239 document.header[i:i+1] = [f + ' ' + line.split()[1]]
2241 document.header[i:i+1] = [f + ' ' + line.split()[0]]
2245 def revert_solution(document):
2246 " Reverts the solution environment of the theorem module to TeX code "
2248 # Do we use one of the modules that provides Solution?
2250 mods = document.get_module_list()
2252 if mod == "theorems-std" or mod == "theorems-bytype" \
2253 or mod == "theorems-ams" or mod == "theorems-ams-bytype":
2263 i = find_token(document.body, "\\begin_layout Solution", i)
2267 is_starred = document.body[i].startswith("\\begin_layout Solution*")
2268 if is_starred == True:
2270 LyXName = "Solution*"
2271 theoremName = "newtheorem*"
2274 LyXName = "Solution"
2275 theoremName = "newtheorem"
2277 j = find_end_of_layout(document.body, i)
2279 document.warning("Malformed LyX document: Can't find end of " + LyXName + " layout")
2283 # if this is not a consecutive env, add start command
2286 begcmd = put_cmd_in_ert("\\begin{%s}" % (LaTeXName))
2288 # has this a consecutive theorem of same type?
2289 consecutive = document.body[j + 2] == "\\begin_layout " + LyXName
2291 # if this is not followed by a consecutive env, add end command
2293 document.body[j : j + 1] = put_cmd_in_ert("\\end{%s}" % (LaTeXName)) + ["\\end_layout"]
2295 document.body[i : i + 1] = ["\\begin_layout Standard", ""] + begcmd
2297 add_to_preamble(document, "\\theoremstyle{definition}")
2298 if is_starred or mod == "theorems-bytype" or mod == "theorems-ams-bytype":
2299 add_to_preamble(document, "\\%s{%s}{\\protect\\solutionname}" % \
2300 (theoremName, LaTeXName))
2301 else: # mod == "theorems-std" or mod == "theorems-ams" and not is_starred
2302 add_to_preamble(document, "\\%s{%s}[thm]{\\protect\\solutionname}" % \
2303 (theoremName, LaTeXName))
2305 add_to_preamble(document, "\\providecommand{\solutionname}{Solution}")
2309 def revert_verbatim_star(document):
2310 from lyx_2_1 import revert_verbatim
2311 revert_verbatim(document, True)
2314 def convert_save_props(document):
2315 " Add save_transient_properties parameter. "
2316 i = find_token(document.header, '\\begin_header', 0)
2318 document.warning("Malformed lyx document: Missing '\\begin_header'.")
2320 document.header.insert(i + 1, '\\save_transient_properties true')
2323 def revert_save_props(document):
2324 " Remove save_transient_properties parameter. "
2325 i = find_token(document.header, "\\save_transient_properties", 0)
2328 del document.header[i]
2331 def convert_info_tabular_feature(document):
2333 return arg.replace("inset-modify tabular", "tabular-feature")
2334 convert_info_insets(document, "shortcut(s)?|icon", f)
2337 def revert_info_tabular_feature(document):
2339 return arg.replace("tabular-feature", "inset-modify tabular")
2340 convert_info_insets(document, "shortcut(s)?|icon", f)
2347 supported_versions = ["2.2.0", "2.2"]
2349 [475, [convert_separator]],
2350 # nothing to do for 476: We consider it a bug that older versions
2351 # did not load amsmath automatically for these commands, and do not
2352 # want to hardcode amsmath off.
2358 [481, [convert_dashes]],
2359 [482, [convert_phrases]],
2360 [483, [convert_specialchar]],
2365 [488, [convert_newgloss]],
2366 [489, [convert_BoxFeatures]],
2367 [490, [convert_origin]],
2369 [492, [convert_colorbox]],
2372 [495, [convert_subref]],
2373 [496, [convert_nounzip]],
2374 [497, [convert_external_bbox]],
2376 [499, [convert_moderncv_phone, convert_moderncv_name]],
2378 [501, [convert_fontsettings]],
2381 [504, [convert_save_props]],
2383 [506, [convert_info_tabular_feature]],
2384 [507, [convert_longtable_label]],
2385 [508, [convert_parbreak]]
2389 [507, [revert_parbreak]],
2390 [506, [revert_longtable_label]],
2391 [505, [revert_info_tabular_feature]],
2393 [503, [revert_save_props]],
2394 [502, [revert_verbatim_star]],
2395 [501, [revert_solution]],
2396 [500, [revert_fontsettings]],
2397 [499, [revert_achemso]],
2398 [498, [revert_moderncv_1, revert_moderncv_2]],
2399 [497, [revert_tcolorbox_1, revert_tcolorbox_2,
2400 revert_tcolorbox_3, revert_tcolorbox_4, revert_tcolorbox_5,
2401 revert_tcolorbox_6, revert_tcolorbox_7, revert_tcolorbox_8]],
2402 [496, [revert_external_bbox]],
2403 [495, []], # nothing to do since the noUnzip parameter was optional
2404 [494, [revert_subref]],
2405 [493, [revert_jss]],
2406 [492, [revert_mathmulticol]],
2407 [491, [revert_colorbox]],
2408 [490, [revert_textcolor]],
2409 [489, [revert_origin]],
2410 [488, [revert_BoxFeatures]],
2411 [487, [revert_newgloss, revert_glossgroup]],
2412 [486, [revert_forest]],
2413 [485, [revert_ex_itemargs]],
2414 [484, [revert_sigplan_doi]],
2415 [483, [revert_georgian]],
2416 [482, [revert_specialchar]],
2417 [481, [revert_phrases]],
2418 [480, [revert_dashes]],
2419 [479, [revert_question_env]],
2420 [478, [revert_beamer_lemma]],
2421 [477, [revert_xarrow]],
2422 [476, [revert_swissgerman]],
2423 [475, [revert_smash]],
2424 [474, [revert_separator]]
2428 if __name__ == "__main__":