1 # This file is part of lyx2lyx
2 # Copyright (C) 2016 The LyX team
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 """Convert files to the file format generated by lyx 2.3"""
22 # Uncomment only what you need to import, please.
24 from parser_tools import (
38 get_containing_layout,
46 from lyx2lyx_tools import (
53 insert_document_option,
54 remove_document_option,
58 ####################################################################
59 # Private helper functions
62 ###############################################################################
64 ### Conversion and reversion routines
66 ###############################################################################
69 def convert_microtype(document):
70 "Add microtype settings."
71 i = find_token(document.header, "\\font_tt_scale")
72 j = find_token(document.preamble, "\\usepackage{microtype}")
74 document.header.insert(i + 1, "\\use_microtype false")
76 document.header.insert(i + 1, "\\use_microtype true")
77 del document.preamble[j]
78 if j and document.preamble[j - 1] == "% Added by lyx2lyx":
79 del document.preamble[j - 1]
82 def revert_microtype(document):
83 "Remove microtype settings."
84 use_microtype = get_bool_value(document.header, "\\use_microtype", delete=True)
86 add_to_preamble(document, ["\\usepackage{microtype}"])
89 def convert_dateinset(document):
90 "Convert date external inset to ERT"
93 i = find_token(document.body, "\\begin_inset External", i + 1)
96 j = find_end_of_inset(document.body, i)
99 "Malformed lyx document: Missing '\\end_inset' in convert_dateinset."
102 if get_value(document.body, "template", i, j) == "Date":
103 document.body[i : j + 1] = put_cmd_in_ert("\\today ")
107 def convert_inputenc(document):
108 """Replace no longer supported input encoding setting."""
109 i = find_token(document.header, "\\inputencoding pt254")
111 document.header[i] = "\\inputencoding pt154"
114 def convert_ibranches(document):
115 'Add "inverted 0" to branch insets'
118 i = find_token(document.body, "\\begin_inset Branch", i + 1)
121 document.body.insert(i + 1, "inverted 0")
124 def revert_ibranches(document):
125 "Convert inverted branches to explicit anti-branches"
126 # Get list of branches
130 i = find_token(document.header, "\\branch", i + 1)
133 branch = document.header[i][8:].strip()
134 selected = get_bool_value(document.header, "\\selected", i + 1, i + 2)
137 "Malformed LyX document: No selection indicator " "for branch %s." % branch
140 # the value tells us whether the branch is selected
141 ourbranches[branch] = selected
143 # Find branch insets, remove "inverted" tag and
144 # convert inverted insets to "Anti-OldBranch" insets
148 i = find_token(document.body, "\\begin_inset Branch", i + 1)
151 inverted = get_bool_value(document.body, "inverted", i + 1, i + 2, delete=True)
153 document.warning("Malformed LyX document: Missing 'inverted' tag in branch inset.")
156 branch = document.body[i][20:].strip()
157 if not branch in antibranches:
158 antibranch = "Anti-" + branch
159 while antibranch in antibranches:
160 antibranch = "x" + antibranch
161 antibranches[branch] = antibranch
163 antibranch = antibranches[branch]
164 document.body[i] = "\\begin_inset Branch " + antibranch
166 # now we need to add the new branches to the header
167 for old, new in antibranches.items():
168 i = find_token(document.header, "\\branch " + old, 0)
170 document.warning("Can't find branch %s even though we found it before!" % (old))
172 j = find_token(document.header, "\\end_branch", i)
174 document.warning("Malformed LyX document! Can't find end of branch " + old)
176 lines = ["\\branch " + new, "\\selected %d" % (not ourbranches[old])]
177 # these are the old lines telling us color, etc.
178 lines += document.header[i + 2 : j + 1]
179 document.header[i:i] = lines
182 beamer_article_styles = [
183 "### Inserted by lyx2lyx (more [scr]article styles) ###",
184 "Input article.layout",
185 "Input beamer.layout",
186 "Provides geometry 0",
187 "Provides hyperref 0",
196 " \\usepackage{beamerarticle,pgf}",
197 " % this default might be overridden by plain title style",
198 " \\newcommand\\makebeamertitle{\\frame{\\maketitle}}%",
199 " \\AtBeginDocument{",
200 " \\let\\origtableofcontents=\\tableofcontents",
201 " \\def\\tableofcontents{\\@ifnextchar[{\\origtableofcontents}{\\gobbletableofcontents}}",
202 " \\def\\gobbletableofcontents#1{\\origtableofcontents}",
205 "### End of insertion by lyx2lyx (more [scr]article styles) ###",
209 def revert_beamer_article_styles(document):
210 "Include (scr)article styles in beamer article"
212 beamer_articles = ["article-beamer", "scrarticle-beamer"]
213 if document.textclass not in beamer_articles:
216 if document.textclass == "scrarticle-beamer":
217 beamer_article_styles[1] = "Input scrartcl.layout"
218 document.append_local_layout(beamer_article_styles)
221 def convert_beamer_article_styles(document):
222 "Remove included (scr)article styles in beamer article"
224 beamer_articles = ["article-beamer", "scrarticle-beamer"]
225 if document.textclass not in beamer_articles:
228 if document.textclass == "scrarticle-beamer":
229 beamer_article_styles[1] = "Input scrartcl.layout"
230 document.del_local_layout(beamer_article_styles)
233 def revert_new_babel_languages(document):
234 """Revert "bosnian", "friulan", "macedonian", "piedmontese", "romansh".
236 Set the document language to English but use correct babel setting.
239 nblanguages = ["bosnian", "friulan", "macedonian", "piedmontese", "romansh"]
241 for lang in nblanguages:
242 if lang == "bosnian" or lang == "macedonian":
243 # These are only supported by babel
244 revert_language(document, lang, lang, "")
246 # These are supported by babel and polyglossia
247 revert_language(document, lang, lang, lang)
251 # def convert_new_babel_languages(document)
252 # set to native support if get_value(document.header, "\\options") in
253 # ["bosnian", "friulan", "macedonian", "piedmontese", "romansh"]
257 def revert_amharic(document):
258 "Set the document language to English but assure Amharic output"
260 revert_language(document, "amharic", "", "amharic")
263 def revert_asturian(document):
264 "Set the document language to English but assure Asturian output"
266 revert_language(document, "asturian", "", "asturian")
269 def revert_kannada(document):
270 "Set the document language to English but assure Kannada output"
272 revert_language(document, "kannada", "", "kannada")
275 def revert_khmer(document):
276 "Set the document language to English but assure Khmer output"
278 revert_language(document, "khmer", "", "khmer")
281 def revert_urdu(document):
282 "Set the document language to English but assure Urdu output"
284 revert_language(document, "urdu", "", "urdu")
287 def revert_syriac(document):
288 "Set the document language to English but assure Syriac output"
290 revert_language(document, "syriac", "", "syriac")
293 def revert_quotes(document):
294 "Revert Quote Insets in verbatim or Hebrew context to plain quotes"
296 # First handle verbatim insets
299 while i < len(document.body):
300 words = document.body[i].split()
303 and words[0] == "\\begin_inset"
305 words[1] in ["ERT", "listings"]
306 or (len(words) > 2 and words[2] in ["URL", "Chunk", "Sweave", "S/R"])
309 j = find_end_of_inset(document.body, i)
313 "Malformed LyX document: Can't find end of "
321 k = find_token(document.body, "\\begin_inset Quotes", i, j)
325 l = find_end_of_inset(document.body, k)
328 "Malformed LyX document: Can't find end of Quote inset at line "
334 if document.body[k].endswith("s"):
336 document.body[k : l + 2] = [replace]
341 # Now verbatim layouts
344 while i < len(document.body):
345 words = document.body[i].split()
348 and words[0] == "\\begin_layout"
349 and words[1] in ["Verbatim", "Verbatim*", "Code", "Author_Email", "Author_URL"]
351 j = find_end_of_layout(document.body, i)
354 "Malformed LyX document: Can't find end of "
362 k = find_token(document.body, "\\begin_inset Quotes", i, j)
366 l = find_end_of_inset(document.body, k)
369 "Malformed LyX document: Can't find end of Quote inset at line "
375 if document.body[k].endswith("s"):
377 document.body[k : l + 2] = [replace]
384 not document.language == "hebrew"
385 and find_token(document.body, "\\lang hebrew", 0) == -1
392 k = find_token(document.body, "\\begin_inset Quotes", i)
395 l = find_end_of_inset(document.body, k)
398 "Malformed LyX document: Can't find end of Quote inset at line " + str(k)
403 parent = get_containing_layout(document.body, k)
404 ql = find_token_backwards(document.body, "\\lang", k)
405 if ql == -1 or ql < parent[1]:
406 hebrew = document.language == "hebrew"
407 elif document.body[ql] == "\\lang hebrew":
411 if document.body[k].endswith("s"):
413 document.body[k : l + 2] = [replace]
417 iopart_local_layout = [
418 "### Inserted by lyx2lyx (stdlayouts) ###",
419 "Input stdlayouts.inc",
420 "### End of insertion by lyx2lyx (stdlayouts) ###" "",
424 def revert_iopart(document):
425 "Input new styles via local layout"
426 if document.textclass != "iopart":
428 document.append_local_layout(iopart_local_layout)
431 def convert_iopart(document):
432 "Remove local layout we added, if it is there"
433 if document.textclass != "iopart":
435 document.del_local_layout(iopart_local_layout)
438 def convert_quotestyle(document):
439 "Convert \\quotes_language to \\quotes_style"
440 i = find_token(document.header, "\\quotes_language", 0)
442 document.warning("Malformed LyX document! Can't find \\quotes_language!")
444 val = get_value(document.header, "\\quotes_language", i)
445 document.header[i] = "\\quotes_style " + val
448 def revert_quotestyle(document):
449 "Revert \\quotes_style to \\quotes_language"
450 i = find_token(document.header, "\\quotes_style", 0)
452 document.warning("Malformed LyX document! Can't find \\quotes_style!")
454 val = get_value(document.header, "\\quotes_style", i)
455 document.header[i] = "\\quotes_language " + val
458 def revert_plainquote(document):
459 "Revert plain quote insets"
461 # First, revert style setting
462 i = find_token(document.header, "\\quotes_style plain", 0)
464 document.header[i] = "\\quotes_style english"
470 k = find_token(document.body, "\\begin_inset Quotes q", i)
473 l = find_end_of_inset(document.body, k)
476 "Malformed LyX document: Can't find end of Quote inset at line " + str(k)
481 if document.body[k].endswith("s"):
483 document.body[k : l + 2] = [replace]
487 def convert_frenchquotes(document):
488 "Convert french quote insets to swiss"
490 # First, revert style setting
491 i = find_token(document.header, "\\quotes_style french", 0)
493 document.header[i] = "\\quotes_style swiss"
498 i = find_token(document.body, "\\begin_inset Quotes f", i)
501 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
502 newval = val.replace("f", "c", 1)
503 document.body[i] = document.body[i].replace(val, newval)
507 def revert_swissquotes(document):
508 "Revert swiss quote insets to french"
510 # First, revert style setting
511 i = find_token(document.header, "\\quotes_style swiss", 0)
513 document.header[i] = "\\quotes_style french"
518 i = find_token(document.body, "\\begin_inset Quotes c", i)
521 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
522 newval = val.replace("c", "f", 1)
523 document.body[i] = document.body[i].replace(val, newval)
527 def revert_britishquotes(document):
528 "Revert british quote insets to english"
530 # First, revert style setting
531 i = find_token(document.header, "\\quotes_style british", 0)
533 document.header[i] = "\\quotes_style english"
538 i = find_token(document.body, "\\begin_inset Quotes b", i)
541 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
542 newval = val.replace("b", "e", 1)
545 newval = newval.replace("d", "s")
548 newval = newval.replace("s", "d")
549 document.body[i] = document.body[i].replace(val, newval)
553 def revert_swedishgquotes(document):
554 "Revert swedish quote insets"
556 # First, revert style setting
557 i = find_token(document.header, "\\quotes_style swedishg", 0)
559 document.header[i] = "\\quotes_style danish"
564 i = find_token(document.body, "\\begin_inset Quotes w", i)
567 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
570 newval = val.replace("w", "a", 1).replace("r", "l")
573 newval = val.replace("w", "s", 1)
574 document.body[i] = document.body[i].replace(val, newval)
578 def revert_frenchquotes(document):
579 "Revert french inner quote insets"
583 i = find_token(document.body, "\\begin_inset Quotes f", i)
586 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
589 newval = val.replace("f", "e", 1).replace("s", "d")
590 document.body[i] = document.body[i].replace(val, newval)
594 def revert_frenchinquotes(document):
595 "Revert inner frenchin quote insets"
597 # First, revert style setting
598 i = find_token(document.header, "\\quotes_style frenchin", 0)
600 document.header[i] = "\\quotes_style french"
605 i = find_token(document.body, "\\begin_inset Quotes i", i)
608 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
609 newval = val.replace("i", "f", 1)
612 newval = newval.replace("s", "d")
613 document.body[i] = document.body[i].replace(val, newval)
617 def revert_russianquotes(document):
618 "Revert russian quote insets"
620 # First, revert style setting
621 i = find_token(document.header, "\\quotes_style russian", 0)
623 document.header[i] = "\\quotes_style french"
628 i = find_token(document.body, "\\begin_inset Quotes r", i)
631 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
635 newval = val.replace("r", "g", 1).replace("s", "d")
638 newval = val.replace("r", "f", 1)
639 document.body[i] = document.body[i].replace(val, newval)
643 def revert_dynamicquotes(document):
644 "Revert dynamic quote insets"
646 # First, revert header
647 i = find_token(document.header, "\\dynamic_quotes", 0)
649 del document.header[i]
653 i = find_token(document.header, "\\quotes_style", 0)
655 document.warning("Malformed document! Missing \\quotes_style")
657 style = get_value(document.header, "\\quotes_style", i)
660 if style == "english":
662 elif style == "swedish":
664 elif style == "german":
666 elif style == "polish":
668 elif style == "swiss":
670 elif style == "danish":
672 elif style == "plain":
674 elif style == "british":
676 elif style == "swedishg":
678 elif style == "french":
680 elif style == "frenchin":
682 elif style == "russian":
685 # now transform the insets
688 i = find_token(document.body, "\\begin_inset Quotes x", i)
691 document.body[i] = document.body[i].replace("x", s)
695 def revert_cjkquotes(document):
696 "Revert cjk quote insets"
700 i = find_token(document.header, "\\quotes_style", 0)
702 document.warning("Malformed document! Missing \\quotes_style")
704 style = get_value(document.header, "\\quotes_style", i)
706 global_cjk = style.find("cjk") != -1
709 document.header[i] = "\\quotes_style english"
710 # transform dynamic insets
712 if style == "cjkangle":
716 i = find_token(document.body, "\\begin_inset Quotes x", i)
719 document.body[i] = document.body[i].replace("x", s)
723 "chinese-simplified",
724 "chinese-traditional",
733 k = find_token(document.body, "\\begin_inset Quotes j", i)
736 l = find_end_of_inset(document.body, k)
739 "Malformed LyX document: Can't find end of Quote inset at line " + str(k)
744 parent = get_containing_layout(document.body, k)
745 ql = find_token_backwards(document.body, "\\lang", k)
746 if ql == -1 or ql < parent[1]:
747 cjk = document.language in cjk_langs
748 elif document.body[ql].split()[1] in cjk_langs:
750 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
759 replace = ["\\begin_inset Formula $\\llceil$", "\\end_inset"]
765 replace = ["\\begin_inset Formula $\\rrfloor$", "\\end_inset"]
773 replace = ["\\begin_inset Formula $\\lceil$", "\\end_inset"]
779 replace = ["\\begin_inset Formula $\\rfloor$", "\\end_inset"]
781 document.body[k : l + 1] = replace
787 k = find_token(document.body, "\\begin_inset Quotes k", i)
790 l = find_end_of_inset(document.body, k)
793 "Malformed LyX document: Can't find end of Quote inset at line " + str(k)
798 parent = get_containing_layout(document.body, k)
799 ql = find_token_backwards(document.body, "\\lang", k)
800 if ql == -1 or ql < parent[1]:
801 cjk = document.language in cjk_langs
802 elif document.body[ql].split()[1] in cjk_langs:
804 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
813 replace = ["\\begin_inset Formula $\\langle$", "\\end_inset"]
819 replace = ["\\begin_inset Formula $\\rangle$", "\\end_inset"]
828 "\\begin_inset Formula $\\langle\\kern -2.5pt\\langle$",
837 "\\begin_inset Formula $\\rangle\\kern -2.5pt\\rangle$",
841 document.body[k : l + 1] = replace
845 def convert_crimson(document):
846 """Transform preamble code to native font setting."""
848 i = find_substring(document.preamble, "{cochineal}")
851 # Find and delete user-preamble code:
852 if document.preamble[i] == "\\usepackage[proportional,osf]{cochineal}":
854 elif document.preamble[i] == "\\usepackage{cochineal}":
858 del document.preamble[i]
859 if i and document.preamble[i - 1] == "% Added by lyx2lyx":
860 del document.preamble[i - 1]
862 # Convert to native font setting:
863 j = find_token(document.header, "\\font_roman")
865 romanfont = ["\font_roman", '"cochineal"', '"default"']
867 romanfont = document.header[j].split()
868 romanfont[1] = '"cochineal"'
869 document.header[j] = " ".join(romanfont)
871 set_bool_value(document.header, "\\font_osf", osf)
872 except ValueError: # no \\font_osf setting in document.header
874 document.header.insert(-1, "\\font_osf true")
877 def revert_crimson(document):
878 "Revert native Cochineal/Crimson font definition to LaTeX"
880 i = find_token(document.header, '\\font_roman "cochineal"')
883 # replace unsupported font setting
884 document.header[i] = document.header[i].replace("cochineal", "default")
885 # no need for preamble code with system fonts
886 if get_bool_value(document.header, "\\use_non_tex_fonts"):
888 # transfer old style figures setting to package options
889 j = find_token(document.header, "\\font_osf true")
891 options = "[proportional,osf]"
892 document.header[j] = "\\font_osf false"
895 add_to_preamble(document, ["\\usepackage%s{cochineal}" % options])
898 def revert_cochinealmath(document):
899 "Revert cochineal newtxmath definitions to LaTeX"
901 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
902 i = find_token(document.header, '\\font_math "cochineal-ntxm"', 0)
904 add_to_preamble(document, "\\usepackage[cochineal]{newtxmath}")
905 document.header[i] = document.header[i].replace("cochineal-ntxm", "auto")
908 def revert_labelonly(document):
909 "Revert labelonly tag for InsetRef"
912 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
915 j = find_end_of_inset(document.body, i)
917 document.warning("Can't find end of reference inset at line %d!!" % (i))
920 k = find_token(document.body, "LatexCommand labelonly", i, j)
924 label = get_quoted_value(document.body, "reference", i, j)
926 document.warning("Can't find label for reference at line %d!" % (i))
929 document.body[i : j + 1] = put_cmd_in_ert([label])
933 def revert_plural_refs(document):
934 "Revert plural and capitalized references"
935 i = find_token(document.header, "\\use_refstyle 1", 0)
936 use_refstyle = i != 0
940 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
943 j = find_end_of_inset(document.body, i)
945 document.warning("Can't find end of reference inset at line %d!!" % (i))
949 plural = caps = suffix = False
950 k = find_token(document.body, "LaTeXCommand formatted", i, j)
951 if k != -1 and use_refstyle:
952 plural = get_bool_value(document.body, "plural", i, j, False)
953 caps = get_bool_value(document.body, "caps", i, j, False)
954 label = get_quoted_value(document.body, "reference", i, j)
957 (prefix, suffix) = label.split(":", 1)
960 "No `:' separator in formatted reference at line %d!" % (i)
963 document.warning("Can't find label for reference at line %d!" % (i))
965 # this effectively tests also for use_refstyle and a formatted reference
966 # we do this complicated test because we would otherwise do this erasure
967 # over and over and over
968 if not ((plural or caps) and suffix):
969 del_token(document.body, "plural", i, j)
970 del_token(document.body, "caps", i, j - 1) # since we deleted a line
975 prefix = prefix[0].title() + prefix[1:]
976 cmd = "\\" + prefix + "ref"
979 cmd += "{" + suffix + "}"
980 document.body[i : j + 1] = put_cmd_in_ert([cmd])
984 def revert_noprefix(document):
985 "Revert labelonly tags with 'noprefix' set"
988 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
991 j = find_end_of_inset(document.body, i)
993 document.warning("Can't find end of reference inset at line %d!!" % (i))
996 k = find_token(document.body, "LatexCommand labelonly", i, j)
999 noprefix = get_bool_value(document.body, "noprefix", i, j)
1001 # either it was not a labelonly command, or else noprefix was not set.
1002 # in that case, we just delete the option.
1003 del_token(document.body, "noprefix", i, j)
1006 label = get_quoted_value(document.body, "reference", i, j)
1008 document.warning("Can't find label for reference at line %d!" % (i))
1012 (prefix, suffix) = label.split(":", 1)
1014 document.warning("No `:' separator in formatted reference at line %d!" % (i))
1015 # we'll leave this as an ordinary labelonly reference
1016 del_token(document.body, "noprefix", i, j)
1019 document.body[i : j + 1] = put_cmd_in_ert([suffix])
1023 def revert_biblatex(document):
1024 "Revert biblatex support"
1030 # 1. Get cite engine
1032 i = find_token(document.header, "\\cite_engine", 0)
1034 document.warning("Malformed document! Missing \\cite_engine")
1036 engine = get_value(document.header, "\\cite_engine", i)
1038 # 2. Store biblatex state and revert to natbib
1040 if engine in ["biblatex", "biblatex-natbib"]:
1042 document.header[i] = "\\cite_engine natbib"
1044 # 3. Store and remove new document headers
1046 i = find_token(document.header, "\\biblatex_bibstyle", 0)
1048 bibstyle = get_value(document.header, "\\biblatex_bibstyle", i)
1049 del document.header[i]
1052 i = find_token(document.header, "\\biblatex_citestyle", 0)
1054 citestyle = get_value(document.header, "\\biblatex_citestyle", i)
1055 del document.header[i]
1058 i = find_token(document.header, "\\biblio_options", 0)
1060 biblio_options = get_value(document.header, "\\biblio_options", i)
1061 del document.header[i]
1064 bbxopts = "[natbib=true"
1066 bbxopts += ",bibstyle=" + bibstyle
1068 bbxopts += ",citestyle=" + citestyle
1069 if biblio_options != "":
1070 bbxopts += "," + biblio_options
1072 add_to_preamble(document, "\\usepackage" + bbxopts + "{biblatex}")
1082 i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
1085 j = find_end_of_inset(document.body, i)
1087 document.warning("Can't find end of bibtex inset at line %d!!" % (i))
1090 bibs = get_quoted_value(document.body, "bibfiles", i, j)
1091 opts = get_quoted_value(document.body, "biblatexopts", i, j)
1094 bibresources += bibs.split(",")
1096 document.warning("Can't find bibfiles for bibtex inset at line %d!" % (i))
1097 # remove biblatexopts line
1098 k = find_token(document.body, "biblatexopts", i, j)
1100 del document.body[k]
1101 # Re-find inset end line
1102 j = find_end_of_inset(document.body, i)
1103 # Insert ERT \\printbibliography and wrap bibtex inset to a Note
1105 pcmd = "printbibliography"
1107 pcmd += "[" + opts + "]"
1109 "\\begin_inset ERT",
1112 "\\begin_layout Plain Layout",
1124 "\\begin_layout Standard",
1125 "\\begin_inset Note Note",
1128 "\\begin_layout Plain Layout",
1130 repl += document.body[i : j + 1]
1131 repl += ["", "\\end_layout", "", "\\end_inset", "", ""]
1132 document.body[i : j + 1] = repl
1138 for b in bibresources:
1139 add_to_preamble(document, "\\addbibresource{" + b + ".bib}")
1141 # 2. Citation insets
1143 # Specific citation insets used in biblatex that need to be reverted to ERT
1146 "citebyear": "citeyear",
1147 "citeyear": "cite*",
1148 "Footcite": "Smartcite",
1149 "footcite": "smartcite",
1150 "Autocite": "Autocite",
1151 "autocite": "autocite",
1152 "citetitle": "citetitle",
1153 "citetitle*": "citetitle*",
1154 "fullcite": "fullcite",
1155 "footfullcite": "footfullcite",
1156 "supercite": "supercite",
1157 "citeauthor": "citeauthor",
1158 "citeauthor*": "citeauthor*",
1159 "Citeauthor": "Citeauthor",
1160 "Citeauthor*": "Citeauthor*",
1163 # All commands accepted by LyX < 2.3. Everything else throws an error.
1205 i = find_token(document.body, "\\begin_inset CommandInset citation", i)
1208 j = find_end_of_inset(document.body, i)
1210 document.warning("Can't find end of citation inset at line %d!!" % (i))
1213 k = find_token(document.body, "LatexCommand", i, j)
1215 document.warning("Can't find LatexCommand for citation inset at line %d!" % (i))
1218 cmd = get_value(document.body, "LatexCommand", k)
1219 if biblatex and cmd in list(new_citations.keys()):
1220 pre = get_quoted_value(document.body, "before", i, j)
1221 post = get_quoted_value(document.body, "after", i, j)
1222 key = get_quoted_value(document.body, "key", i, j)
1224 document.warning("Citation inset at line %d does not have a key!" % (i))
1226 # Replace known new commands with ERT
1227 res = "\\" + new_citations[cmd]
1229 res += "[" + pre + "]"
1231 res += "[" + post + "]"
1234 res += "{" + key + "}"
1235 document.body[i : j + 1] = put_cmd_in_ert([res])
1236 elif cmd not in old_citations:
1237 # Reset unknown commands to cite. This is what LyX does as well
1238 # (but LyX 2.2 would break on unknown commands)
1239 document.body[k] = "LatexCommand cite"
1240 document.warning("Reset unknown cite command '%s' with cite" % cmd)
1243 # Emulate the old biblatex-workaround (pretend natbib in order to use the styles)
1245 biblatex_emulation = [
1246 "### Inserted by lyx2lyx (biblatex emulation) ###",
1247 "Provides natbib 1",
1248 "### End of insertion by lyx2lyx (biblatex emulation) ###",
1250 document.append_local_layout(biblatex_emulation)
1253 def revert_citekeyonly(document):
1254 "Revert keyonly cite command to ERT"
1258 i = find_token(document.body, "\\begin_inset CommandInset citation", i)
1261 j = find_end_of_inset(document.body, i)
1263 document.warning("Can't find end of citation inset at line %d!!" % (i))
1266 k = find_token(document.body, "LatexCommand", i, j)
1268 document.warning("Can't find LatexCommand for citation inset at line %d!" % (i))
1271 cmd = get_value(document.body, "LatexCommand", k)
1272 if cmd != "keyonly":
1276 key = get_quoted_value(document.body, "key", i, j)
1278 document.warning("Citation inset at line %d does not have a key!" % (i))
1279 # Replace known new commands with ERT
1280 document.body[i : j + 1] = put_cmd_in_ert([key])
1284 def revert_bibpackopts(document):
1285 "Revert support for natbib/jurabib package options"
1288 i = find_token(document.header, "\\cite_engine", 0)
1290 document.warning("Malformed document! Missing \\cite_engine")
1292 engine = get_value(document.header, "\\cite_engine", i)
1295 if engine not in ["natbib", "jurabib"]:
1298 i = find_token(document.header, "\\biblio_options", 0)
1300 # Nothing to do if we have no options
1303 biblio_options = get_value(document.header, "\\biblio_options", i)
1304 del document.header[i]
1306 if not biblio_options:
1307 # Nothing to do for empty options
1310 bibliography_package_options = [
1311 "### Inserted by lyx2lyx (bibliography package options) ###",
1312 "PackageOptions " + engine + " " + biblio_options,
1313 "### End of insertion by lyx2lyx (bibliography package options) ###",
1315 document.append_local_layout(bibliography_package_options)
1318 def revert_qualicites(document):
1319 "Revert qualified citation list commands to ERT"
1321 # Citation insets that support qualified lists, with their LaTeX code
1325 "citet": "textcites",
1326 "Citet": "Textcites",
1327 "citep": "parencites",
1328 "Citep": "Parencites",
1329 "Footcite": "Smartcites",
1330 "footcite": "smartcites",
1331 "Autocite": "Autocites",
1332 "autocite": "autocites",
1337 i = find_token(document.header, "\\cite_engine", 0)
1339 document.warning("Malformed document! Missing \\cite_engine")
1341 engine = get_value(document.header, "\\cite_engine", i)
1343 biblatex = engine in ["biblatex", "biblatex-natbib"]
1347 i = find_token(document.body, "\\begin_inset CommandInset citation", i)
1350 j = find_end_of_inset(document.body, i)
1352 document.warning("Can't find end of citation inset at line %d!!" % (i))
1355 pres = find_token(document.body, "pretextlist", i, j)
1356 posts = find_token(document.body, "posttextlist", i, j)
1357 if pres == -1 and posts == -1:
1361 pretexts = get_quoted_value(document.body, "pretextlist", pres)
1362 posttexts = get_quoted_value(document.body, "posttextlist", posts)
1363 k = find_token(document.body, "LatexCommand", i, j)
1365 document.warning("Can't find LatexCommand for citation inset at line %d!" % (i))
1368 cmd = get_value(document.body, "LatexCommand", k)
1369 if biblatex and cmd in list(ql_citations.keys()):
1370 pre = get_quoted_value(document.body, "before", i, j)
1371 post = get_quoted_value(document.body, "after", i, j)
1372 key = get_quoted_value(document.body, "key", i, j)
1374 document.warning("Citation inset at line %d does not have a key!" % (i))
1376 keys = key.split(",")
1377 prelist = pretexts.split("\t")
1380 ppp = pp.split(" ", 1)
1381 premap[ppp[0]] = ppp[1]
1382 postlist = posttexts.split("\t")
1385 ppp = pp.split(" ", 1)
1386 postmap[ppp[0]] = ppp[1]
1387 # Replace known new commands with ERT
1388 if "(" in pre or ")" in pre:
1389 pre = "{" + pre + "}"
1390 if "(" in post or ")" in post:
1391 post = "{" + post + "}"
1392 res = "\\" + ql_citations[cmd]
1394 res += "(" + pre + ")"
1396 res += "(" + post + ")"
1400 if premap.get(kk, "") != "":
1401 res += "[" + premap[kk] + "]"
1402 if postmap.get(kk, "") != "":
1403 res += "[" + postmap[kk] + "]"
1404 elif premap.get(kk, "") != "":
1406 res += "{" + kk + "}"
1407 document.body[i : j + 1] = put_cmd_in_ert([res])
1409 # just remove the params
1410 del document.body[posttexts]
1411 del document.body[pretexts]
1415 command_insets = ["bibitem", "citation", "href", "index_print", "nomenclature"]
1418 def convert_literalparam(document):
1421 pos = len("\\begin_inset CommandInset ")
1424 i = find_token(document.body, "\\begin_inset CommandInset", i)
1427 inset = document.body[i][pos:].strip()
1428 if not inset in command_insets:
1431 j = find_end_of_inset(document.body, i)
1434 "Malformed LyX document: Can't find end of %s inset at line %d" % (inset, i)
1438 while i < j and document.body[i].strip() != "":
1440 # href is already fully latexified. Here we can switch off literal.
1442 document.body.insert(i, 'literal "false"')
1444 document.body.insert(i, 'literal "true"')
1448 def revert_literalparam(document):
1449 "Remove param literal"
1451 for inset in command_insets:
1454 i = find_token(document.body, "\\begin_inset CommandInset %s" % inset, i + 1)
1457 j = find_end_of_inset(document.body, i)
1460 "Malformed LyX document: Can't find end of %s inset at line %d" % (inset, i)
1463 del_token(document.body, "literal", i, j)
1466 def revert_multibib(document):
1467 "Revert multibib support"
1469 # 1. Get cite engine
1471 i = find_token(document.header, "\\cite_engine", 0)
1473 document.warning("Malformed document! Missing \\cite_engine")
1475 engine = get_value(document.header, "\\cite_engine", i)
1477 # 2. Do we use biblatex?
1479 if engine in ["biblatex", "biblatex-natbib"]:
1482 # 3. Store and remove multibib document header
1484 i = find_token(document.header, "\\multibib", 0)
1486 multibib = get_value(document.header, "\\multibib", i)
1487 del document.header[i]
1492 # 4. The easy part: Biblatex
1494 i = find_token(document.header, "\\biblio_options", 0)
1496 k = find_token(document.header, "\\use_bibtopic", 0)
1498 # this should not happen
1499 document.warning("Malformed LyX document! No \\use_bibtopic header found!")
1501 document.header[k - 1 : k - 1] = ["\\biblio_options " + "refsection=" + multibib]
1503 biblio_options = get_value(document.header, "\\biblio_options", i)
1505 biblio_options += ","
1506 biblio_options += "refsection=" + multibib
1507 document.header[i] = "\\biblio_options " + biblio_options
1512 i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
1515 j = find_end_of_inset(document.body, i)
1517 document.warning("Can't find end of bibtex inset at line %d!!" % (i))
1520 btprint = get_quoted_value(document.body, "btprint", i, j)
1521 if btprint != "bibbysection":
1524 opts = get_quoted_value(document.body, "biblatexopts", i, j)
1525 # change btprint line
1526 k = find_token(document.body, "btprint", i, j)
1528 document.body[k] = 'btprint "btPrintCited"'
1529 # Insert ERT \\bibbysection and wrap bibtex inset to a Note
1530 pcmd = "bibbysection"
1532 pcmd += "[" + opts + "]"
1534 "\\begin_inset ERT",
1537 "\\begin_layout Plain Layout",
1549 "\\begin_layout Standard",
1550 "\\begin_inset Note Note",
1553 "\\begin_layout Plain Layout",
1555 repl += document.body[i : j + 1]
1556 repl += ["", "\\end_layout", "", "\\end_inset", "", ""]
1557 document.body[i : j + 1] = repl
1563 # 5. More tricky: Bibtex/Bibtopic
1564 k = find_token(document.header, "\\use_bibtopic", 0)
1566 # this should not happen
1567 document.warning("Malformed LyX document! No \\use_bibtopic header found!")
1569 document.header[k] = "\\use_bibtopic true"
1571 # Possible units. This assumes that the LyX name follows the std,
1572 # which might not always be the case. But it's as good as we can get.
1575 "chapter": "Chapter",
1576 "section": "Section",
1577 "subsection": "Subsection",
1580 if multibib not in units.keys():
1581 document.warning("Unknown multibib value `%s'!" % multibib)
1583 unit = units[multibib]
1587 i = find_token(document.body, "\\begin_layout " + unit, i)
1591 document.body[i - 1 : i - 1] = [
1592 "\\begin_layout Standard",
1593 "\\begin_inset ERT",
1596 "\\begin_layout Plain Layout",
1602 "\\begin_layout Plain Layout",
1605 "begin{btUnit}" "\\end_layout",
1615 document.body[i - 1 : i - 1] = [
1616 "\\begin_layout Standard",
1617 "\\begin_inset ERT",
1620 "\\begin_layout Plain Layout",
1624 "begin{btUnit}" "\\end_layout",
1637 i = find_token(document.body, "\\end_body", i)
1638 document.body[i - 1 : i - 1] = [
1639 "\\begin_layout Standard",
1640 "\\begin_inset ERT",
1643 "\\begin_layout Plain Layout",
1647 "end{btUnit}" "\\end_layout",
1657 def revert_chapterbib(document):
1658 "Revert chapterbib support"
1660 # 1. Get cite engine
1662 i = find_token(document.header, "\\cite_engine", 0)
1664 document.warning("Malformed document! Missing \\cite_engine")
1666 engine = get_value(document.header, "\\cite_engine", i)
1668 # 2. Do we use biblatex?
1670 if engine in ["biblatex", "biblatex-natbib"]:
1673 # 3. Store multibib document header value
1675 i = find_token(document.header, "\\multibib", 0)
1677 multibib = get_value(document.header, "\\multibib", i)
1679 if not multibib or multibib != "child":
1683 # 4. remove multibib header
1684 del document.header[i]
1688 # find include insets
1691 i = find_token(document.body, "\\begin_inset CommandInset include", i)
1694 j = find_end_of_inset(document.body, i)
1696 document.warning("Can't find end of bibtex inset at line %d!!" % (i))
1699 parent = get_containing_layout(document.body, i)
1702 # Insert ERT \\newrefsection before inset
1704 "\\begin_layout Standard",
1705 "\\begin_inset ERT",
1708 "\\begin_layout Plain Layout",
1712 "newrefsection" "\\end_layout",
1720 document.body[parbeg - 1 : parbeg - 1] = beg
1725 # 6. Bibtex/Bibtopic
1726 i = find_token(document.header, "\\use_bibtopic", 0)
1728 # this should not happen
1729 document.warning("Malformed LyX document! No \\use_bibtopic header found!")
1731 if get_value(document.header, "\\use_bibtopic", i) == "true":
1732 # find include insets
1735 i = find_token(document.body, "\\begin_inset CommandInset include", i)
1738 j = find_end_of_inset(document.body, i)
1740 document.warning("Can't find end of bibtex inset at line %d!!" % (i))
1743 parent = get_containing_layout(document.body, i)
1747 # Insert wrap inset into \\begin{btUnit}...\\end{btUnit}
1749 "\\begin_layout Standard",
1750 "\\begin_inset ERT",
1753 "\\begin_layout Plain Layout",
1757 "begin{btUnit}" "\\end_layout",
1766 "\\begin_layout Standard",
1767 "\\begin_inset ERT",
1770 "\\begin_layout Plain Layout",
1774 "end{btUnit}" "\\end_layout",
1782 document.body[parend + 1 : parend + 1] = end
1783 document.body[parbeg - 1 : parbeg - 1] = beg
1784 j += len(beg) + len(end)
1788 # 7. Chapterbib proper
1789 add_to_preamble(document, ["\\usepackage{chapterbib}"])
1792 def convert_dashligatures(document):
1793 """Set 'use_dash_ligatures' according to content."""
1794 # Look for and remove dashligatures workaround from 2.3->2.2 reversion,
1795 # set use_dash_ligatures to True if found, to None else.
1796 use_dash_ligatures = (
1800 "% Added by lyx2lyx",
1801 r"\renewcommand{\textendash}{--}",
1802 r"\renewcommand{\textemdash}{---}",
1808 if use_dash_ligatures is None:
1809 # Look for dashes (Documents by LyX 2.1 or older have "\twohyphens\n"
1810 # or "\threehyphens\n" as interim representation for -- an ---.)
1811 lines = document.body
1812 has_literal_dashes = has_ligature_dashes = False
1813 dash_pattern = re.compile(".*[\u2013\u2014]|\\twohyphens|\\threehyphens")
1816 # skip lines without dashes:
1817 i = find_re(lines, dash_pattern, i + 1)
1821 # skip label width string (see bug 10243):
1822 if line.startswith("\\labelwidthstring"):
1824 # do not touch hyphens in some insets (cf. lyx_2_2.convert_dashes):
1826 inset_type, start, end = get_containing_inset(lines, i)
1827 except TypeError: # no containing inset
1828 inset_type, start, end = "no inset", -1, -1
1830 inset_type.split()[0]
1841 or inset_type == "Flex Code"
1846 layoutname, start, end, j = get_containing_layout(lines, i)
1847 except TypeError: # no (or malformed) containing layout
1848 document.warning("Malformed LyX document: " "Can't find layout at line %d" % i)
1852 "Malformed LyX document: " "Missing layout name on line %d" % start
1854 if layoutname == "LyX-Code":
1858 # literal dash followed by a non-white-character or no-break space:
1859 if re.search("[\u2013\u2014]([\\S\u00a0\u202f\u2060]|$)", line, flags=re.UNICODE):
1860 has_literal_dashes = True
1861 # ligature dash followed by non-white-char or no-break space on next line:
1862 if re.search(r"(\\twohyphens|\\threehyphens)", line) and re.match(
1863 "[\\S\u00a0\u202f\u2060]", lines[i + 1], flags=re.UNICODE
1865 has_ligature_dashes = True
1866 if has_literal_dashes and has_ligature_dashes:
1867 # TODO: insert a warning note in the document?
1869 "This document contained both literal and "
1870 '"ligature" dashes.\n Line breaks may have changed. '
1871 "See UserGuide chapter 3.9.1 for details."
1875 if has_literal_dashes and not has_ligature_dashes:
1876 use_dash_ligatures = False
1877 elif has_ligature_dashes and not has_literal_dashes:
1878 use_dash_ligatures = True
1880 # insert the setting if there is a preferred value
1881 if use_dash_ligatures is True:
1882 document.header.insert(-1, "\\use_dash_ligatures true")
1883 elif use_dash_ligatures is False:
1884 document.header.insert(-1, "\\use_dash_ligatures false")
1887 def revert_dashligatures(document):
1888 """Remove font ligature settings for en- and em-dashes.
1889 Revert conversion of \twodashes or \threedashes to literal dashes.
1891 use_dash_ligatures = del_value(document.header, "\\use_dash_ligatures")
1892 if use_dash_ligatures != "true" or document.backend != "latex":
1895 dash_pattern = re.compile(".*[\u2013\u2014]")
1897 # skip lines without dashes:
1898 i = find_re(document.body, dash_pattern, i + 1)
1901 line = document.body[i]
1902 # skip label width string (see bug 10243):
1903 if line.startswith("\\labelwidthstring"):
1905 # do not touch hyphens in some insets (cf. lyx_2_2.convert_dashes):
1907 inset_type, start, end = get_containing_inset(document.body, i)
1908 except TypeError: # no containing inset
1909 inset_type, start, end = "no inset", -1, -1
1911 inset_type.split()[0]
1922 or inset_type == "Flex Code"
1927 layoutname, start, end, j = get_containing_layout(document.body, i)
1928 except TypeError: # no (or malformed) containing layout
1929 document.warning("Malformed LyX document: " "Can't find layout at body line %d" % i)
1931 if layoutname == "LyX-Code":
1934 # TODO: skip replacement in typewriter fonts
1935 line = line.replace("\u2013", "\\twohyphens\n")
1936 line = line.replace("\u2014", "\\threehyphens\n")
1937 document.body[i : i + 1] = line.split("\n")
1938 # redefine the dash LICRs to use ligature dashes:
1941 [r"\renewcommand{\textendash}{--}", r"\renewcommand{\textemdash}{---}"],
1945 def revert_noto(document):
1946 "Revert Noto font definitions to LaTeX"
1948 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1950 i = find_token(document.header, '\\font_roman "NotoSerif-TLF"', 0)
1952 add_to_preamble(document, ["\\renewcommand{\\rmdefault}{NotoSerif-TLF}"])
1953 document.header[i] = document.header[i].replace("NotoSerif-TLF", "default")
1954 i = find_token(document.header, '\\font_sans "NotoSans-TLF"', 0)
1956 add_to_preamble(document, ["\\renewcommand{\\sfdefault}{NotoSans-TLF}"])
1957 document.header[i] = document.header[i].replace("NotoSans-TLF", "default")
1958 i = find_token(document.header, '\\font_typewriter "NotoMono-TLF"', 0)
1960 add_to_preamble(document, ["\\renewcommand{\\ttdefault}{NotoMono-TLF}"])
1961 document.header[i] = document.header[i].replace("NotoMono-TLF", "default")
1964 def revert_xout(document):
1965 "Reverts \\xout font attribute"
1966 changed = revert_font_attrs(document.body, "\\xout", "\\xout")
1971 "% for proper cross-out",
1972 "\\PassOptionsToPackage{normalem}{ulem}",
1973 "\\usepackage{ulem}",
1978 def convert_mathindent(document):
1979 """Add the \\is_math_indent tag."""
1980 k = find_token(document.header, "\\quotes_style") # where to insert
1981 # check if the document uses the class option "fleqn"
1982 options = get_value(document.header, "\\options")
1983 if "fleqn" in options:
1984 document.header.insert(k, "\\is_math_indent 1")
1985 # delete the fleqn option
1986 i = find_token(document.header, "\\options")
1987 options = [option for option in options.split(",") if option.strip() != "fleqn"]
1989 document.header[i] = "\\options " + ",".join(options)
1991 del document.header[i]
1993 document.header.insert(k, "\\is_math_indent 0")
1996 def revert_mathindent(document):
1997 "Define mathindent if set in the document"
1998 # emulate and delete \math_indentation
1999 value = get_value(document.header, "\\math_indentation", default="default", delete=True)
2000 if value != "default":
2001 add_to_preamble(document, [r"\setlength{\mathindent}{%s}" % value])
2002 # delete \is_math_indent and emulate via document class option
2003 if not get_bool_value(document.header, "\\is_math_indent", delete=True):
2005 i = find_token(document.header, "\\options")
2007 document.header[i] = document.header[i].replace("\\options ", "\\options fleqn,")
2009 l = find_token(document.header, "\\use_default_options")
2010 document.header.insert(l, "\\options fleqn")
2013 def revert_baselineskip(document):
2014 "Revert baselineskips to TeX code"
2017 i = find_substring(document.body, "baselineskip%", i + 1)
2020 if document.body[i].startswith("\\begin_inset VSpace"):
2021 # output VSpace inset as TeX code
2022 end = find_end_of_inset(document.body, i)
2025 "Malformed LyX document: " "Can't find end of VSpace inset at line %d." % i
2028 # read out the value
2029 baselineskip = document.body[i].split()[-1]
2030 # check if it is the starred version
2031 star = "*" if "*" in document.body[i] else ""
2032 # now output TeX code
2033 cmd = "\\vspace%s{%s}" % (star, latex_length(baselineskip)[1])
2034 document.body[i : end + 1] = put_cmd_in_ert(cmd)
2037 begin, end = is_in_inset(document.body, i, "\\begin_inset space \\hspace")
2039 # output space inset as TeX code
2040 baselineskip = document.body[i].split()[-1]
2041 star = "*" if "*" in document.body[i - 1] else ""
2042 cmd = "\\hspace%s{%s}" % (star, latex_length(baselineskip)[1])
2043 document.body[begin : end + 1] = put_cmd_in_ert(cmd)
2046 def revert_rotfloat(document):
2047 "Revert placement options for rotated floats"
2052 i = find_token(document.body, "sideways true", i)
2055 if not document.body[i - 2].startswith("placement "):
2058 # we found a sideways float with placement options
2059 # at first store the placement
2060 beg = document.body[i - 2].rfind(" ")
2061 placement = document.body[i - 2][beg + 1 :]
2062 # check if the option'H' is used
2063 if placement.find("H") != -1:
2064 add_to_preamble(document, ["\\usepackage{float}"])
2065 # now check if it is a starred type
2066 if document.body[i - 1].find("wide true") != -1:
2070 # store the float type
2071 beg = document.body[i - 3].rfind(" ")
2072 fType = document.body[i - 3][beg + 1 :]
2073 # now output TeX code
2074 endInset = find_end_of_inset(document.body, i - 3)
2076 document.warning("Malformed LyX document: Missing '\\end_inset' of Float inset.")
2079 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert(
2080 "\\end{sideways" + fType + star + "}"
2082 document.body[i - 3 : i + 2] = put_cmd_in_ert(
2083 "\\begin{sideways" + fType + star + "}[" + placement + "]"
2085 add_to_preamble(document, ["\\usepackage{rotfloat}"])
2090 allowbreak_emulation = [
2091 r"\begin_inset space \hspace{}",
2098 def convert_allowbreak(document):
2099 r"Zero widths Space-inset -> \SpecialChar allowbreak."
2100 lines = document.body
2101 i = find_complete_lines(lines, allowbreak_emulation, 2)
2103 lines[i - 1 : i + 4] = [lines[i - 1] + r"\SpecialChar allowbreak"]
2104 i = find_complete_lines(lines, allowbreak_emulation, i + 3)
2107 def revert_allowbreak(document):
2108 r"\SpecialChar allowbreak -> Zero widths Space-inset."
2110 lines = document.body
2111 while i < len(lines):
2112 if lines[i].endswith(r"\SpecialChar allowbreak"):
2113 lines[i : i + 1] = [
2114 lines[i].replace(r"\SpecialChar allowbreak", "")
2115 ] + allowbreak_emulation
2121 def convert_mathnumberpos(document):
2122 "add the \\math_number_before tag"
2123 i = find_token(document.header, "\\quotes_style")
2124 # check if the document uses the class option "leqno"
2125 if is_document_option(document, "leqno"):
2126 remove_document_option(document, "leqno")
2127 document.header.insert(i, "\\math_number_before 1")
2129 document.header.insert(i, "\\math_number_before 0")
2132 def revert_mathnumberpos(document):
2133 """Remove \\math_number_before tag,
2134 add the document class option leqno if required.
2136 math_number_before = get_bool_value(document.header, "\\math_number_before", delete=True)
2137 if math_number_before:
2138 insert_document_option(document, "leqno")
2141 def convert_mathnumberingname(document):
2142 "rename the \\math_number_before tag to \\math_numbering_side"
2143 i = find_token(document.header, "\\math_number_before")
2144 math_number_before = get_bool_value(document.header, "\\math_number_before", i)
2145 if math_number_before:
2146 document.header[i] = "\\math_numbering_side left"
2148 # check if the document uses the class option "reqno"
2149 k = find_token(document.header, "\\options")
2150 if "reqno" in document.header[k]:
2151 document.header[i] = "\\math_numbering_side right"
2152 # delete the found option
2153 document.header[k] = document.header[k].replace(",reqno", "")
2154 document.header[k] = document.header[k].replace(", reqno", "")
2155 document.header[k] = document.header[k].replace("reqno,", "")
2156 if "reqno" in document.header[k]:
2157 # then we have reqno as the only option
2158 del document.header[k]
2160 document.header[i] = "\\math_numbering_side default"
2163 def revert_mathnumberingname(document):
2164 "rename the \\math_numbering_side tag back to \\math_number_before"
2165 i = find_token(document.header, "\\math_numbering_side")
2166 math_numbering_side = get_value(document.header, "\\math_numbering_side", i)
2167 # rename tag and set boolean value:
2168 if math_numbering_side == "left":
2169 document.header[i] = "\\math_number_before 1"
2170 elif math_numbering_side == "right":
2171 # also add the option reqno:
2172 document.header[i] = "\\math_number_before 0"
2173 k = find_token(document.header, "\\options")
2174 if k != -1 and "reqno" not in document.header[k]:
2175 document.header[k] = document.header[k].replace("\\options", "\\options reqno,")
2177 l = find_token(document.header, "\\use_default_options", 0)
2178 document.header.insert(l, "\\options reqno")
2180 document.header[i] = "\\math_number_before 0"
2183 def convert_minted(document):
2184 "add the \\use_minted tag"
2185 i = find_token(document.header, "\\index ")
2186 document.header.insert(i, "\\use_minted 0")
2189 def revert_minted(document):
2190 "remove the \\use_minted tag"
2191 del_token(document.header, "\\use_minted")
2194 def revert_longtable_lscape(document):
2195 "revert the longtable landcape mode to ERT"
2197 regexp = re.compile(r"^<features rotate=\"90\"\s.*islongtable=\"true\"\s.*$", re.IGNORECASE)
2199 i = find_re(document.body, regexp, i)
2203 document.body[i] = document.body[i].replace(' rotate="90"', "")
2204 lay = get_containing_layout(document.body, i)
2206 document.warning("Longtable has not layout!")
2209 begcmd = put_cmd_in_ert("\\begin{landscape}")
2210 endcmd = put_cmd_in_ert("\\end{landscape}")
2211 document.body[lay[2] : lay[2]] = endcmd + ["\\end_layout"]
2212 document.body[lay[1] : lay[1]] = ["\\begin_layout " + lay[0], ""] + begcmd
2214 add_to_preamble(document, ["\\usepackage{pdflscape}"])
2222 supported_versions = ["2.3.0", "2.3"]
2224 [509, [convert_microtype]],
2225 [510, [convert_dateinset]],
2226 [511, [convert_ibranches]],
2227 [512, [convert_beamer_article_styles]],
2231 [516, [convert_inputenc]],
2233 [518, [convert_iopart]],
2234 [519, [convert_quotestyle]],
2236 [521, [convert_frenchquotes]],
2239 [524, [convert_crimson]],
2247 [532, [convert_literalparam]],
2250 [535, [convert_dashligatures]],
2253 [538, [convert_mathindent]],
2256 [541, [convert_allowbreak]],
2257 [542, [convert_mathnumberpos]],
2258 [543, [convert_mathnumberingname]],
2259 [544, [convert_minted]],
2263 [543, [revert_minted, revert_longtable_lscape]],
2264 [542, [revert_mathnumberingname]],
2265 [541, [revert_mathnumberpos]],
2266 [540, [revert_allowbreak]],
2267 [539, [revert_rotfloat]],
2268 [538, [revert_baselineskip]],
2269 [537, [revert_mathindent]],
2270 [536, [revert_xout]],
2271 [535, [revert_noto]],
2272 [534, [revert_dashligatures]],
2273 [533, [revert_chapterbib]],
2274 [532, [revert_multibib]],
2275 [531, [revert_literalparam]],
2276 [530, [revert_qualicites]],
2277 [529, [revert_bibpackopts]],
2278 [528, [revert_citekeyonly]],
2279 [527, [revert_biblatex]],
2280 [526, [revert_noprefix]],
2281 [525, [revert_plural_refs]],
2282 [524, [revert_labelonly]],
2283 [523, [revert_crimson, revert_cochinealmath]],
2284 [522, [revert_cjkquotes]],
2285 [521, [revert_dynamicquotes]],
2289 revert_britishquotes,
2290 revert_swedishgquotes,
2291 revert_frenchquotes,
2292 revert_frenchinquotes,
2293 revert_russianquotes,
2297 [519, [revert_plainquote]],
2298 [518, [revert_quotestyle]],
2299 [517, [revert_iopart]],
2300 [516, [revert_quotes]],
2302 [514, [revert_urdu, revert_syriac]],
2303 [513, [revert_amharic, revert_asturian, revert_kannada, revert_khmer]],
2304 [512, [revert_new_babel_languages]],
2305 [511, [revert_beamer_article_styles]],
2306 [510, [revert_ibranches]],
2308 [508, [revert_microtype]],
2312 if __name__ == "__main__":