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 from lyx2lyx_tools import (
24 insert_document_option,
29 remove_document_option,
34 # Uncomment only what you need to import, please.
35 from parser_tools import (
48 get_containing_layout,
55 ####################################################################
56 # Private helper functions
59 ###############################################################################
61 ### Conversion and reversion routines
63 ###############################################################################
66 def convert_microtype(document):
67 "Add microtype settings."
68 i = find_token(document.header, "\\font_tt_scale")
69 j = find_token(document.preamble, "\\usepackage{microtype}")
71 document.header.insert(i + 1, "\\use_microtype false")
73 document.header.insert(i + 1, "\\use_microtype true")
74 del document.preamble[j]
75 if j and document.preamble[j - 1] == "% Added by lyx2lyx":
76 del document.preamble[j - 1]
79 def revert_microtype(document):
80 "Remove microtype settings."
81 use_microtype = get_bool_value(document.header, "\\use_microtype", delete=True)
83 add_to_preamble(document, ["\\usepackage{microtype}"])
86 def convert_dateinset(document):
87 "Convert date external inset to ERT"
90 i = find_token(document.body, "\\begin_inset External", i + 1)
93 j = find_end_of_inset(document.body, i)
96 "Malformed lyx document: Missing '\\end_inset' in convert_dateinset."
99 if get_value(document.body, "template", i, j) == "Date":
100 document.body[i : j + 1] = put_cmd_in_ert("\\today ")
104 def convert_inputenc(document):
105 """Replace no longer supported input encoding setting."""
106 i = find_token(document.header, "\\inputencoding pt254")
108 document.header[i] = "\\inputencoding pt154"
111 def convert_ibranches(document):
112 'Add "inverted 0" to branch insets'
115 i = find_token(document.body, "\\begin_inset Branch", i + 1)
118 document.body.insert(i + 1, "inverted 0")
121 def revert_ibranches(document):
122 "Convert inverted branches to explicit anti-branches"
123 # Get list of branches
127 i = find_token(document.header, "\\branch", i + 1)
130 branch = document.header[i][8:].strip()
131 selected = get_bool_value(document.header, "\\selected", i + 1, i + 2)
134 "Malformed LyX document: No selection indicator " "for branch %s." % branch
137 # the value tells us whether the branch is selected
138 ourbranches[branch] = selected
140 # Find branch insets, remove "inverted" tag and
141 # convert inverted insets to "Anti-OldBranch" insets
145 i = find_token(document.body, "\\begin_inset Branch", i + 1)
148 inverted = get_bool_value(document.body, "inverted", i + 1, i + 2, delete=True)
150 document.warning("Malformed LyX document: Missing 'inverted' tag in branch inset.")
153 branch = document.body[i][20:].strip()
154 if branch not in antibranches:
155 antibranch = "Anti-" + branch
156 while antibranch in antibranches:
157 antibranch = "x" + antibranch
158 antibranches[branch] = antibranch
160 antibranch = antibranches[branch]
161 document.body[i] = "\\begin_inset Branch " + antibranch
163 # now we need to add the new branches to the header
164 for old, new in antibranches.items():
165 i = find_token(document.header, "\\branch " + old, 0)
167 document.warning("Can't find branch %s even though we found it before!" % (old))
169 j = find_token(document.header, "\\end_branch", i)
171 document.warning("Malformed LyX document! Can't find end of branch " + old)
173 lines = ["\\branch " + new, "\\selected %d" % (not ourbranches[old])]
174 # these are the old lines telling us color, etc.
175 lines += document.header[i + 2 : j + 1]
176 document.header[i:i] = lines
179 beamer_article_styles = [
180 "### Inserted by lyx2lyx (more [scr]article styles) ###",
181 "Input article.layout",
182 "Input beamer.layout",
183 "Provides geometry 0",
184 "Provides hyperref 0",
193 " \\usepackage{beamerarticle,pgf}",
194 " % this default might be overridden by plain title style",
195 " \\newcommand\\makebeamertitle{\\frame{\\maketitle}}%",
196 " \\AtBeginDocument{",
197 " \\let\\origtableofcontents=\\tableofcontents",
198 " \\def\\tableofcontents{\\@ifnextchar[{\\origtableofcontents}{\\gobbletableofcontents}}",
199 " \\def\\gobbletableofcontents#1{\\origtableofcontents}",
202 "### End of insertion by lyx2lyx (more [scr]article styles) ###",
206 def revert_beamer_article_styles(document):
207 "Include (scr)article styles in beamer article"
209 beamer_articles = ["article-beamer", "scrarticle-beamer"]
210 if document.textclass not in beamer_articles:
213 if document.textclass == "scrarticle-beamer":
214 beamer_article_styles[1] = "Input scrartcl.layout"
215 document.append_local_layout(beamer_article_styles)
218 def convert_beamer_article_styles(document):
219 "Remove included (scr)article styles in beamer article"
221 beamer_articles = ["article-beamer", "scrarticle-beamer"]
222 if document.textclass not in beamer_articles:
225 if document.textclass == "scrarticle-beamer":
226 beamer_article_styles[1] = "Input scrartcl.layout"
227 document.del_local_layout(beamer_article_styles)
230 def revert_new_babel_languages(document):
231 """Revert "bosnian", "friulan", "macedonian", "piedmontese", "romansh".
233 Set the document language to English but use correct babel setting.
236 nblanguages = ["bosnian", "friulan", "macedonian", "piedmontese", "romansh"]
238 for lang in nblanguages:
239 if lang == "bosnian" or lang == "macedonian":
240 # These are only supported by babel
241 revert_language(document, lang, lang, "")
243 # These are supported by babel and polyglossia
244 revert_language(document, lang, lang, lang)
248 # def convert_new_babel_languages(document)
249 # set to native support if get_value(document.header, "\\options") in
250 # ["bosnian", "friulan", "macedonian", "piedmontese", "romansh"]
254 def revert_amharic(document):
255 "Set the document language to English but assure Amharic output"
257 revert_language(document, "amharic", "", "amharic")
260 def revert_asturian(document):
261 "Set the document language to English but assure Asturian output"
263 revert_language(document, "asturian", "", "asturian")
266 def revert_kannada(document):
267 "Set the document language to English but assure Kannada output"
269 revert_language(document, "kannada", "", "kannada")
272 def revert_khmer(document):
273 "Set the document language to English but assure Khmer output"
275 revert_language(document, "khmer", "", "khmer")
278 def revert_urdu(document):
279 "Set the document language to English but assure Urdu output"
281 revert_language(document, "urdu", "", "urdu")
284 def revert_syriac(document):
285 "Set the document language to English but assure Syriac output"
287 revert_language(document, "syriac", "", "syriac")
290 def revert_quotes(document):
291 "Revert Quote Insets in verbatim or Hebrew context to plain quotes"
293 # First handle verbatim insets
296 while i < len(document.body):
297 words = document.body[i].split()
300 and words[0] == "\\begin_inset"
302 words[1] in ["ERT", "listings"]
303 or (len(words) > 2 and words[2] in ["URL", "Chunk", "Sweave", "S/R"])
306 j = find_end_of_inset(document.body, i)
310 "Malformed LyX document: Can't find end of "
318 k = find_token(document.body, "\\begin_inset Quotes", i, j)
322 l = find_end_of_inset(document.body, k)
325 "Malformed LyX document: Can't find end of Quote inset at line "
331 if document.body[k].endswith("s"):
333 document.body[k : l + 2] = [replace]
338 # Now verbatim layouts
341 while i < len(document.body):
342 words = document.body[i].split()
345 and words[0] == "\\begin_layout"
346 and words[1] in ["Verbatim", "Verbatim*", "Code", "Author_Email", "Author_URL"]
348 j = find_end_of_layout(document.body, i)
351 "Malformed LyX document: Can't find end of "
359 k = find_token(document.body, "\\begin_inset Quotes", i, j)
363 l = find_end_of_inset(document.body, k)
366 "Malformed LyX document: Can't find end of Quote inset at line "
372 if document.body[k].endswith("s"):
374 document.body[k : l + 2] = [replace]
381 not document.language == "hebrew"
382 and find_token(document.body, "\\lang hebrew", 0) == -1
389 k = find_token(document.body, "\\begin_inset Quotes", i)
392 l = find_end_of_inset(document.body, k)
395 "Malformed LyX document: Can't find end of Quote inset at line " + str(k)
400 parent = get_containing_layout(document.body, k)
401 ql = find_token_backwards(document.body, "\\lang", k)
402 if ql == -1 or ql < parent[1]:
403 hebrew = document.language == "hebrew"
404 elif document.body[ql] == "\\lang hebrew":
408 if document.body[k].endswith("s"):
410 document.body[k : l + 2] = [replace]
414 iopart_local_layout = [
415 "### Inserted by lyx2lyx (stdlayouts) ###",
416 "Input stdlayouts.inc",
417 "### End of insertion by lyx2lyx (stdlayouts) ###" "",
421 def revert_iopart(document):
422 "Input new styles via local layout"
423 if document.textclass != "iopart":
425 document.append_local_layout(iopart_local_layout)
428 def convert_iopart(document):
429 "Remove local layout we added, if it is there"
430 if document.textclass != "iopart":
432 document.del_local_layout(iopart_local_layout)
435 def convert_quotestyle(document):
436 "Convert \\quotes_language to \\quotes_style"
437 i = find_token(document.header, "\\quotes_language", 0)
439 document.warning("Malformed LyX document! Can't find \\quotes_language!")
441 val = get_value(document.header, "\\quotes_language", i)
442 document.header[i] = "\\quotes_style " + val
445 def revert_quotestyle(document):
446 "Revert \\quotes_style to \\quotes_language"
447 i = find_token(document.header, "\\quotes_style", 0)
449 document.warning("Malformed LyX document! Can't find \\quotes_style!")
451 val = get_value(document.header, "\\quotes_style", i)
452 document.header[i] = "\\quotes_language " + val
455 def revert_plainquote(document):
456 "Revert plain quote insets"
458 # First, revert style setting
459 i = find_token(document.header, "\\quotes_style plain", 0)
461 document.header[i] = "\\quotes_style english"
467 k = find_token(document.body, "\\begin_inset Quotes q", i)
470 l = find_end_of_inset(document.body, k)
473 "Malformed LyX document: Can't find end of Quote inset at line " + str(k)
478 if document.body[k].endswith("s"):
480 document.body[k : l + 2] = [replace]
484 def convert_frenchquotes(document):
485 "Convert french quote insets to swiss"
487 # First, revert style setting
488 i = find_token(document.header, "\\quotes_style french", 0)
490 document.header[i] = "\\quotes_style swiss"
495 i = find_token(document.body, "\\begin_inset Quotes f", i)
498 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
499 newval = val.replace("f", "c", 1)
500 document.body[i] = document.body[i].replace(val, newval)
504 def revert_swissquotes(document):
505 "Revert swiss quote insets to french"
507 # First, revert style setting
508 i = find_token(document.header, "\\quotes_style swiss", 0)
510 document.header[i] = "\\quotes_style french"
515 i = find_token(document.body, "\\begin_inset Quotes c", i)
518 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
519 newval = val.replace("c", "f", 1)
520 document.body[i] = document.body[i].replace(val, newval)
524 def revert_britishquotes(document):
525 "Revert british quote insets to english"
527 # First, revert style setting
528 i = find_token(document.header, "\\quotes_style british", 0)
530 document.header[i] = "\\quotes_style english"
535 i = find_token(document.body, "\\begin_inset Quotes b", i)
538 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
539 newval = val.replace("b", "e", 1)
542 newval = newval.replace("d", "s")
545 newval = newval.replace("s", "d")
546 document.body[i] = document.body[i].replace(val, newval)
550 def revert_swedishgquotes(document):
551 "Revert swedish quote insets"
553 # First, revert style setting
554 i = find_token(document.header, "\\quotes_style swedishg", 0)
556 document.header[i] = "\\quotes_style danish"
561 i = find_token(document.body, "\\begin_inset Quotes w", i)
564 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
567 newval = val.replace("w", "a", 1).replace("r", "l")
570 newval = val.replace("w", "s", 1)
571 document.body[i] = document.body[i].replace(val, newval)
575 def revert_frenchquotes(document):
576 "Revert french inner quote insets"
580 i = find_token(document.body, "\\begin_inset Quotes f", i)
583 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
586 newval = val.replace("f", "e", 1).replace("s", "d")
587 document.body[i] = document.body[i].replace(val, newval)
591 def revert_frenchinquotes(document):
592 "Revert inner frenchin quote insets"
594 # First, revert style setting
595 i = find_token(document.header, "\\quotes_style frenchin", 0)
597 document.header[i] = "\\quotes_style french"
602 i = find_token(document.body, "\\begin_inset Quotes i", i)
605 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
606 newval = val.replace("i", "f", 1)
609 newval = newval.replace("s", "d")
610 document.body[i] = document.body[i].replace(val, newval)
614 def revert_russianquotes(document):
615 "Revert russian quote insets"
617 # First, revert style setting
618 i = find_token(document.header, "\\quotes_style russian", 0)
620 document.header[i] = "\\quotes_style french"
625 i = find_token(document.body, "\\begin_inset Quotes r", i)
628 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
632 newval = val.replace("r", "g", 1).replace("s", "d")
635 newval = val.replace("r", "f", 1)
636 document.body[i] = document.body[i].replace(val, newval)
640 def revert_dynamicquotes(document):
641 "Revert dynamic quote insets"
643 # First, revert header
644 i = find_token(document.header, "\\dynamic_quotes", 0)
646 del document.header[i]
650 i = find_token(document.header, "\\quotes_style", 0)
652 document.warning("Malformed document! Missing \\quotes_style")
654 style = get_value(document.header, "\\quotes_style", i)
657 if style == "english":
659 elif style == "swedish":
661 elif style == "german":
663 elif style == "polish":
665 elif style == "swiss":
667 elif style == "danish":
669 elif style == "plain":
671 elif style == "british":
673 elif style == "swedishg":
675 elif style == "french":
677 elif style == "frenchin":
679 elif style == "russian":
682 # now transform the insets
685 i = find_token(document.body, "\\begin_inset Quotes x", i)
688 document.body[i] = document.body[i].replace("x", s)
692 def revert_cjkquotes(document):
693 "Revert cjk quote insets"
697 i = find_token(document.header, "\\quotes_style", 0)
699 document.warning("Malformed document! Missing \\quotes_style")
701 style = get_value(document.header, "\\quotes_style", i)
703 global_cjk = style.find("cjk") != -1
706 document.header[i] = "\\quotes_style english"
707 # transform dynamic insets
709 if style == "cjkangle":
713 i = find_token(document.body, "\\begin_inset Quotes x", i)
716 document.body[i] = document.body[i].replace("x", s)
720 "chinese-simplified",
721 "chinese-traditional",
730 k = find_token(document.body, "\\begin_inset Quotes j", i)
733 l = find_end_of_inset(document.body, k)
736 "Malformed LyX document: Can't find end of Quote inset at line " + str(k)
741 parent = get_containing_layout(document.body, k)
742 ql = find_token_backwards(document.body, "\\lang", k)
743 if ql == -1 or ql < parent[1]:
744 cjk = document.language in cjk_langs
745 elif document.body[ql].split()[1] in cjk_langs:
747 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
756 replace = ["\\begin_inset Formula $\\llceil$", "\\end_inset"]
762 replace = ["\\begin_inset Formula $\\rrfloor$", "\\end_inset"]
770 replace = ["\\begin_inset Formula $\\lceil$", "\\end_inset"]
776 replace = ["\\begin_inset Formula $\\rfloor$", "\\end_inset"]
778 document.body[k : l + 1] = replace
784 k = find_token(document.body, "\\begin_inset Quotes k", i)
787 l = find_end_of_inset(document.body, k)
790 "Malformed LyX document: Can't find end of Quote inset at line " + str(k)
795 parent = get_containing_layout(document.body, k)
796 ql = find_token_backwards(document.body, "\\lang", k)
797 if ql == -1 or ql < parent[1]:
798 cjk = document.language in cjk_langs
799 elif document.body[ql].split()[1] in cjk_langs:
801 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
810 replace = ["\\begin_inset Formula $\\langle$", "\\end_inset"]
816 replace = ["\\begin_inset Formula $\\rangle$", "\\end_inset"]
825 "\\begin_inset Formula $\\langle\\kern -2.5pt\\langle$",
834 "\\begin_inset Formula $\\rangle\\kern -2.5pt\\rangle$",
838 document.body[k : l + 1] = replace
842 def convert_crimson(document):
843 """Transform preamble code to native font setting."""
845 i = find_substring(document.preamble, "{cochineal}")
848 # Find and delete user-preamble code:
849 if document.preamble[i] == "\\usepackage[proportional,osf]{cochineal}":
851 elif document.preamble[i] == "\\usepackage{cochineal}":
855 del document.preamble[i]
856 if i and document.preamble[i - 1] == "% Added by lyx2lyx":
857 del document.preamble[i - 1]
859 # Convert to native font setting:
860 j = find_token(document.header, "\\font_roman")
862 romanfont = ["\font_roman", '"cochineal"', '"default"']
864 romanfont = document.header[j].split()
865 romanfont[1] = '"cochineal"'
866 document.header[j] = " ".join(romanfont)
868 set_bool_value(document.header, "\\font_osf", osf)
869 except ValueError: # no \\font_osf setting in document.header
871 document.header.insert(-1, "\\font_osf true")
874 def revert_crimson(document):
875 "Revert native Cochineal/Crimson font definition to LaTeX"
877 i = find_token(document.header, '\\font_roman "cochineal"')
880 # replace unsupported font setting
881 document.header[i] = document.header[i].replace("cochineal", "default")
882 # no need for preamble code with system fonts
883 if get_bool_value(document.header, "\\use_non_tex_fonts"):
885 # transfer old style figures setting to package options
886 j = find_token(document.header, "\\font_osf true")
888 options = "[proportional,osf]"
889 document.header[j] = "\\font_osf false"
892 add_to_preamble(document, ["\\usepackage%s{cochineal}" % options])
895 def revert_cochinealmath(document):
896 "Revert cochineal newtxmath definitions to LaTeX"
898 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
899 i = find_token(document.header, '\\font_math "cochineal-ntxm"', 0)
901 add_to_preamble(document, "\\usepackage[cochineal]{newtxmath}")
902 document.header[i] = document.header[i].replace("cochineal-ntxm", "auto")
905 def revert_labelonly(document):
906 "Revert labelonly tag for InsetRef"
909 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
912 j = find_end_of_inset(document.body, i)
914 document.warning("Can't find end of reference inset at line %d!!" % (i))
917 k = find_token(document.body, "LatexCommand labelonly", i, j)
921 label = get_quoted_value(document.body, "reference", i, j)
923 document.warning("Can't find label for reference at line %d!" % (i))
926 document.body[i : j + 1] = put_cmd_in_ert([label])
930 def revert_plural_refs(document):
931 "Revert plural and capitalized references"
932 i = find_token(document.header, "\\use_refstyle 1", 0)
933 use_refstyle = i != 0
937 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
940 j = find_end_of_inset(document.body, i)
942 document.warning("Can't find end of reference inset at line %d!!" % (i))
946 plural = caps = suffix = False
947 k = find_token(document.body, "LaTeXCommand formatted", i, j)
948 if k != -1 and use_refstyle:
949 plural = get_bool_value(document.body, "plural", i, j, False)
950 caps = get_bool_value(document.body, "caps", i, j, False)
951 label = get_quoted_value(document.body, "reference", i, j)
954 (prefix, suffix) = label.split(":", 1)
957 "No `:' separator in formatted reference at line %d!" % (i)
960 document.warning("Can't find label for reference at line %d!" % (i))
962 # this effectively tests also for use_refstyle and a formatted reference
963 # we do this complicated test because we would otherwise do this erasure
964 # over and over and over
965 if not ((plural or caps) and suffix):
966 del_token(document.body, "plural", i, j)
967 del_token(document.body, "caps", i, j - 1) # since we deleted a line
972 prefix = prefix[0].title() + prefix[1:]
973 cmd = "\\" + prefix + "ref"
976 cmd += "{" + suffix + "}"
977 document.body[i : j + 1] = put_cmd_in_ert([cmd])
981 def revert_noprefix(document):
982 "Revert labelonly tags with 'noprefix' set"
985 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
988 j = find_end_of_inset(document.body, i)
990 document.warning("Can't find end of reference inset at line %d!!" % (i))
993 k = find_token(document.body, "LatexCommand labelonly", i, j)
996 noprefix = get_bool_value(document.body, "noprefix", i, j)
998 # either it was not a labelonly command, or else noprefix was not set.
999 # in that case, we just delete the option.
1000 del_token(document.body, "noprefix", i, j)
1003 label = get_quoted_value(document.body, "reference", i, j)
1005 document.warning("Can't find label for reference at line %d!" % (i))
1009 (prefix, suffix) = label.split(":", 1)
1011 document.warning("No `:' separator in formatted reference at line %d!" % (i))
1012 # we'll leave this as an ordinary labelonly reference
1013 del_token(document.body, "noprefix", i, j)
1016 document.body[i : j + 1] = put_cmd_in_ert([suffix])
1020 def revert_biblatex(document):
1021 "Revert biblatex support"
1027 # 1. Get cite engine
1029 i = find_token(document.header, "\\cite_engine", 0)
1031 document.warning("Malformed document! Missing \\cite_engine")
1033 engine = get_value(document.header, "\\cite_engine", i)
1035 # 2. Store biblatex state and revert to natbib
1037 if engine in ["biblatex", "biblatex-natbib"]:
1039 document.header[i] = "\\cite_engine natbib"
1041 # 3. Store and remove new document headers
1043 i = find_token(document.header, "\\biblatex_bibstyle", 0)
1045 bibstyle = get_value(document.header, "\\biblatex_bibstyle", i)
1046 del document.header[i]
1049 i = find_token(document.header, "\\biblatex_citestyle", 0)
1051 citestyle = get_value(document.header, "\\biblatex_citestyle", i)
1052 del document.header[i]
1055 i = find_token(document.header, "\\biblio_options", 0)
1057 biblio_options = get_value(document.header, "\\biblio_options", i)
1058 del document.header[i]
1061 bbxopts = "[natbib=true"
1063 bbxopts += ",bibstyle=" + bibstyle
1065 bbxopts += ",citestyle=" + citestyle
1066 if biblio_options != "":
1067 bbxopts += "," + biblio_options
1069 add_to_preamble(document, "\\usepackage" + bbxopts + "{biblatex}")
1079 i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
1082 j = find_end_of_inset(document.body, i)
1084 document.warning("Can't find end of bibtex inset at line %d!!" % (i))
1087 bibs = get_quoted_value(document.body, "bibfiles", i, j)
1088 opts = get_quoted_value(document.body, "biblatexopts", i, j)
1091 bibresources += bibs.split(",")
1093 document.warning("Can't find bibfiles for bibtex inset at line %d!" % (i))
1094 # remove biblatexopts line
1095 k = find_token(document.body, "biblatexopts", i, j)
1097 del document.body[k]
1098 # Re-find inset end line
1099 j = find_end_of_inset(document.body, i)
1100 # Insert ERT \\printbibliography and wrap bibtex inset to a Note
1102 pcmd = "printbibliography"
1104 pcmd += "[" + opts + "]"
1106 "\\begin_inset ERT",
1109 "\\begin_layout Plain Layout",
1121 "\\begin_layout Standard",
1122 "\\begin_inset Note Note",
1125 "\\begin_layout Plain Layout",
1127 repl += document.body[i : j + 1]
1128 repl += ["", "\\end_layout", "", "\\end_inset", "", ""]
1129 document.body[i : j + 1] = repl
1135 for b in bibresources:
1136 add_to_preamble(document, "\\addbibresource{" + b + ".bib}")
1138 # 2. Citation insets
1140 # Specific citation insets used in biblatex that need to be reverted to ERT
1143 "citebyear": "citeyear",
1144 "citeyear": "cite*",
1145 "Footcite": "Smartcite",
1146 "footcite": "smartcite",
1147 "Autocite": "Autocite",
1148 "autocite": "autocite",
1149 "citetitle": "citetitle",
1150 "citetitle*": "citetitle*",
1151 "fullcite": "fullcite",
1152 "footfullcite": "footfullcite",
1153 "supercite": "supercite",
1154 "citeauthor": "citeauthor",
1155 "citeauthor*": "citeauthor*",
1156 "Citeauthor": "Citeauthor",
1157 "Citeauthor*": "Citeauthor*",
1160 # All commands accepted by LyX < 2.3. Everything else throws an error.
1202 i = find_token(document.body, "\\begin_inset CommandInset citation", i)
1205 j = find_end_of_inset(document.body, i)
1207 document.warning("Can't find end of citation inset at line %d!!" % (i))
1210 k = find_token(document.body, "LatexCommand", i, j)
1212 document.warning("Can't find LatexCommand for citation inset at line %d!" % (i))
1215 cmd = get_value(document.body, "LatexCommand", k)
1216 if biblatex and cmd in list(new_citations.keys()):
1217 pre = get_quoted_value(document.body, "before", i, j)
1218 post = get_quoted_value(document.body, "after", i, j)
1219 key = get_quoted_value(document.body, "key", i, j)
1221 document.warning("Citation inset at line %d does not have a key!" % (i))
1223 # Replace known new commands with ERT
1224 res = "\\" + new_citations[cmd]
1226 res += "[" + pre + "]"
1228 res += "[" + post + "]"
1231 res += "{" + key + "}"
1232 document.body[i : j + 1] = put_cmd_in_ert([res])
1233 elif cmd not in old_citations:
1234 # Reset unknown commands to cite. This is what LyX does as well
1235 # (but LyX 2.2 would break on unknown commands)
1236 document.body[k] = "LatexCommand cite"
1237 document.warning("Reset unknown cite command '%s' with cite" % cmd)
1240 # Emulate the old biblatex-workaround (pretend natbib in order to use the styles)
1242 biblatex_emulation = [
1243 "### Inserted by lyx2lyx (biblatex emulation) ###",
1244 "Provides natbib 1",
1245 "### End of insertion by lyx2lyx (biblatex emulation) ###",
1247 document.append_local_layout(biblatex_emulation)
1250 def revert_citekeyonly(document):
1251 "Revert keyonly cite command to ERT"
1255 i = find_token(document.body, "\\begin_inset CommandInset citation", i)
1258 j = find_end_of_inset(document.body, i)
1260 document.warning("Can't find end of citation inset at line %d!!" % (i))
1263 k = find_token(document.body, "LatexCommand", i, j)
1265 document.warning("Can't find LatexCommand for citation inset at line %d!" % (i))
1268 cmd = get_value(document.body, "LatexCommand", k)
1269 if cmd != "keyonly":
1273 key = get_quoted_value(document.body, "key", i, j)
1275 document.warning("Citation inset at line %d does not have a key!" % (i))
1276 # Replace known new commands with ERT
1277 document.body[i : j + 1] = put_cmd_in_ert([key])
1281 def revert_bibpackopts(document):
1282 "Revert support for natbib/jurabib package options"
1285 i = find_token(document.header, "\\cite_engine", 0)
1287 document.warning("Malformed document! Missing \\cite_engine")
1289 engine = get_value(document.header, "\\cite_engine", i)
1292 if engine not in ["natbib", "jurabib"]:
1295 i = find_token(document.header, "\\biblio_options", 0)
1297 # Nothing to do if we have no options
1300 biblio_options = get_value(document.header, "\\biblio_options", i)
1301 del document.header[i]
1303 if not biblio_options:
1304 # Nothing to do for empty options
1307 bibliography_package_options = [
1308 "### Inserted by lyx2lyx (bibliography package options) ###",
1309 "PackageOptions " + engine + " " + biblio_options,
1310 "### End of insertion by lyx2lyx (bibliography package options) ###",
1312 document.append_local_layout(bibliography_package_options)
1315 def revert_qualicites(document):
1316 "Revert qualified citation list commands to ERT"
1318 # Citation insets that support qualified lists, with their LaTeX code
1322 "citet": "textcites",
1323 "Citet": "Textcites",
1324 "citep": "parencites",
1325 "Citep": "Parencites",
1326 "Footcite": "Smartcites",
1327 "footcite": "smartcites",
1328 "Autocite": "Autocites",
1329 "autocite": "autocites",
1334 i = find_token(document.header, "\\cite_engine", 0)
1336 document.warning("Malformed document! Missing \\cite_engine")
1338 engine = get_value(document.header, "\\cite_engine", i)
1340 biblatex = engine in ["biblatex", "biblatex-natbib"]
1344 i = find_token(document.body, "\\begin_inset CommandInset citation", i)
1347 j = find_end_of_inset(document.body, i)
1349 document.warning("Can't find end of citation inset at line %d!!" % (i))
1352 pres = find_token(document.body, "pretextlist", i, j)
1353 posts = find_token(document.body, "posttextlist", i, j)
1354 if pres == -1 and posts == -1:
1358 pretexts = get_quoted_value(document.body, "pretextlist", pres)
1359 posttexts = get_quoted_value(document.body, "posttextlist", posts)
1360 k = find_token(document.body, "LatexCommand", i, j)
1362 document.warning("Can't find LatexCommand for citation inset at line %d!" % (i))
1365 cmd = get_value(document.body, "LatexCommand", k)
1366 if biblatex and cmd in list(ql_citations.keys()):
1367 pre = get_quoted_value(document.body, "before", i, j)
1368 post = get_quoted_value(document.body, "after", i, j)
1369 key = get_quoted_value(document.body, "key", i, j)
1371 document.warning("Citation inset at line %d does not have a key!" % (i))
1373 keys = key.split(",")
1374 prelist = pretexts.split("\t")
1377 ppp = pp.split(" ", 1)
1378 premap[ppp[0]] = ppp[1]
1379 postlist = posttexts.split("\t")
1382 ppp = pp.split(" ", 1)
1383 postmap[ppp[0]] = ppp[1]
1384 # Replace known new commands with ERT
1385 if "(" in pre or ")" in pre:
1386 pre = "{" + pre + "}"
1387 if "(" in post or ")" in post:
1388 post = "{" + post + "}"
1389 res = "\\" + ql_citations[cmd]
1391 res += "(" + pre + ")"
1393 res += "(" + post + ")"
1397 if premap.get(kk, "") != "":
1398 res += "[" + premap[kk] + "]"
1399 if postmap.get(kk, "") != "":
1400 res += "[" + postmap[kk] + "]"
1401 elif premap.get(kk, "") != "":
1403 res += "{" + kk + "}"
1404 document.body[i : j + 1] = put_cmd_in_ert([res])
1406 # just remove the params
1407 del document.body[posttexts]
1408 del document.body[pretexts]
1412 command_insets = ["bibitem", "citation", "href", "index_print", "nomenclature"]
1415 def convert_literalparam(document):
1418 pos = len("\\begin_inset CommandInset ")
1421 i = find_token(document.body, "\\begin_inset CommandInset", i)
1424 inset = document.body[i][pos:].strip()
1425 if inset not in command_insets:
1428 j = find_end_of_inset(document.body, i)
1431 "Malformed LyX document: Can't find end of %s inset at line %d" % (inset, i)
1435 while i < j and document.body[i].strip() != "":
1437 # href is already fully latexified. Here we can switch off literal.
1439 document.body.insert(i, 'literal "false"')
1441 document.body.insert(i, 'literal "true"')
1445 def revert_literalparam(document):
1446 "Remove param literal"
1448 for inset in command_insets:
1451 i = find_token(document.body, "\\begin_inset CommandInset %s" % inset, i + 1)
1454 j = find_end_of_inset(document.body, i)
1457 "Malformed LyX document: Can't find end of %s inset at line %d" % (inset, i)
1460 del_token(document.body, "literal", i, j)
1463 def revert_multibib(document):
1464 "Revert multibib support"
1466 # 1. Get cite engine
1468 i = find_token(document.header, "\\cite_engine", 0)
1470 document.warning("Malformed document! Missing \\cite_engine")
1472 engine = get_value(document.header, "\\cite_engine", i)
1474 # 2. Do we use biblatex?
1476 if engine in ["biblatex", "biblatex-natbib"]:
1479 # 3. Store and remove multibib document header
1481 i = find_token(document.header, "\\multibib", 0)
1483 multibib = get_value(document.header, "\\multibib", i)
1484 del document.header[i]
1489 # 4. The easy part: Biblatex
1491 i = find_token(document.header, "\\biblio_options", 0)
1493 k = find_token(document.header, "\\use_bibtopic", 0)
1495 # this should not happen
1496 document.warning("Malformed LyX document! No \\use_bibtopic header found!")
1498 document.header[k - 1 : k - 1] = ["\\biblio_options " + "refsection=" + multibib]
1500 biblio_options = get_value(document.header, "\\biblio_options", i)
1502 biblio_options += ","
1503 biblio_options += "refsection=" + multibib
1504 document.header[i] = "\\biblio_options " + biblio_options
1509 i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
1512 j = find_end_of_inset(document.body, i)
1514 document.warning("Can't find end of bibtex inset at line %d!!" % (i))
1517 btprint = get_quoted_value(document.body, "btprint", i, j)
1518 if btprint != "bibbysection":
1521 opts = get_quoted_value(document.body, "biblatexopts", i, j)
1522 # change btprint line
1523 k = find_token(document.body, "btprint", i, j)
1525 document.body[k] = 'btprint "btPrintCited"'
1526 # Insert ERT \\bibbysection and wrap bibtex inset to a Note
1527 pcmd = "bibbysection"
1529 pcmd += "[" + opts + "]"
1531 "\\begin_inset ERT",
1534 "\\begin_layout Plain Layout",
1546 "\\begin_layout Standard",
1547 "\\begin_inset Note Note",
1550 "\\begin_layout Plain Layout",
1552 repl += document.body[i : j + 1]
1553 repl += ["", "\\end_layout", "", "\\end_inset", "", ""]
1554 document.body[i : j + 1] = repl
1560 # 5. More tricky: Bibtex/Bibtopic
1561 k = find_token(document.header, "\\use_bibtopic", 0)
1563 # this should not happen
1564 document.warning("Malformed LyX document! No \\use_bibtopic header found!")
1566 document.header[k] = "\\use_bibtopic true"
1568 # Possible units. This assumes that the LyX name follows the std,
1569 # which might not always be the case. But it's as good as we can get.
1572 "chapter": "Chapter",
1573 "section": "Section",
1574 "subsection": "Subsection",
1577 if multibib not in units.keys():
1578 document.warning("Unknown multibib value `%s'!" % multibib)
1580 unit = units[multibib]
1584 i = find_token(document.body, "\\begin_layout " + unit, i)
1588 document.body[i - 1 : i - 1] = [
1589 "\\begin_layout Standard",
1590 "\\begin_inset ERT",
1593 "\\begin_layout Plain Layout",
1599 "\\begin_layout Plain Layout",
1602 "begin{btUnit}" "\\end_layout",
1612 document.body[i - 1 : i - 1] = [
1613 "\\begin_layout Standard",
1614 "\\begin_inset ERT",
1617 "\\begin_layout Plain Layout",
1621 "begin{btUnit}" "\\end_layout",
1634 i = find_token(document.body, "\\end_body", i)
1635 document.body[i - 1 : i - 1] = [
1636 "\\begin_layout Standard",
1637 "\\begin_inset ERT",
1640 "\\begin_layout Plain Layout",
1644 "end{btUnit}" "\\end_layout",
1654 def revert_chapterbib(document):
1655 "Revert chapterbib support"
1657 # 1. Get cite engine
1659 i = find_token(document.header, "\\cite_engine", 0)
1661 document.warning("Malformed document! Missing \\cite_engine")
1663 engine = get_value(document.header, "\\cite_engine", i)
1665 # 2. Do we use biblatex?
1667 if engine in ["biblatex", "biblatex-natbib"]:
1670 # 3. Store multibib document header value
1672 i = find_token(document.header, "\\multibib", 0)
1674 multibib = get_value(document.header, "\\multibib", i)
1676 if not multibib or multibib != "child":
1680 # 4. remove multibib header
1681 del document.header[i]
1685 # find include insets
1688 i = find_token(document.body, "\\begin_inset CommandInset include", i)
1691 j = find_end_of_inset(document.body, i)
1693 document.warning("Can't find end of bibtex inset at line %d!!" % (i))
1696 parent = get_containing_layout(document.body, i)
1699 # Insert ERT \\newrefsection before inset
1701 "\\begin_layout Standard",
1702 "\\begin_inset ERT",
1705 "\\begin_layout Plain Layout",
1709 "newrefsection" "\\end_layout",
1717 document.body[parbeg - 1 : parbeg - 1] = beg
1722 # 6. Bibtex/Bibtopic
1723 i = find_token(document.header, "\\use_bibtopic", 0)
1725 # this should not happen
1726 document.warning("Malformed LyX document! No \\use_bibtopic header found!")
1728 if get_value(document.header, "\\use_bibtopic", i) == "true":
1729 # find include insets
1732 i = find_token(document.body, "\\begin_inset CommandInset include", i)
1735 j = find_end_of_inset(document.body, i)
1737 document.warning("Can't find end of bibtex inset at line %d!!" % (i))
1740 parent = get_containing_layout(document.body, i)
1744 # Insert wrap inset into \\begin{btUnit}...\\end{btUnit}
1746 "\\begin_layout Standard",
1747 "\\begin_inset ERT",
1750 "\\begin_layout Plain Layout",
1754 "begin{btUnit}" "\\end_layout",
1763 "\\begin_layout Standard",
1764 "\\begin_inset ERT",
1767 "\\begin_layout Plain Layout",
1771 "end{btUnit}" "\\end_layout",
1779 document.body[parend + 1 : parend + 1] = end
1780 document.body[parbeg - 1 : parbeg - 1] = beg
1781 j += len(beg) + len(end)
1785 # 7. Chapterbib proper
1786 add_to_preamble(document, ["\\usepackage{chapterbib}"])
1789 def convert_dashligatures(document):
1790 """Set 'use_dash_ligatures' according to content."""
1791 # Look for and remove dashligatures workaround from 2.3->2.2 reversion,
1792 # set use_dash_ligatures to True if found, to None else.
1793 use_dash_ligatures = (
1797 "% Added by lyx2lyx",
1798 r"\renewcommand{\textendash}{--}",
1799 r"\renewcommand{\textemdash}{---}",
1805 if use_dash_ligatures is None:
1806 # Look for dashes (Documents by LyX 2.1 or older have "\twohyphens\n"
1807 # or "\threehyphens\n" as interim representation for -- an ---.)
1808 lines = document.body
1809 has_literal_dashes = has_ligature_dashes = False
1810 dash_pattern = re.compile(".*[\u2013\u2014]|\\twohyphens|\\threehyphens")
1813 # skip lines without dashes:
1814 i = find_re(lines, dash_pattern, i + 1)
1818 # skip label width string (see bug 10243):
1819 if line.startswith("\\labelwidthstring"):
1821 # do not touch hyphens in some insets (cf. lyx_2_2.convert_dashes):
1823 inset_type, start, end = get_containing_inset(lines, i)
1824 except TypeError: # no containing inset
1825 inset_type, start, end = "no inset", -1, -1
1827 inset_type.split()[0]
1838 or inset_type == "Flex Code"
1843 layoutname, start, end, j = get_containing_layout(lines, i)
1844 except TypeError: # no (or malformed) containing layout
1845 document.warning("Malformed LyX document: " "Can't find layout at line %d" % i)
1849 "Malformed LyX document: " "Missing layout name on line %d" % start
1851 if layoutname == "LyX-Code":
1855 # literal dash followed by a non-white-character or no-break space:
1856 if re.search("[\u2013\u2014]([\\S\u00a0\u202f\u2060]|$)", line, flags=re.UNICODE):
1857 has_literal_dashes = True
1858 # ligature dash followed by non-white-char or no-break space on next line:
1859 if re.search(r"(\\twohyphens|\\threehyphens)", line) and re.match(
1860 "[\\S\u00a0\u202f\u2060]", lines[i + 1], flags=re.UNICODE
1862 has_ligature_dashes = True
1863 if has_literal_dashes and has_ligature_dashes:
1864 # TODO: insert a warning note in the document?
1866 "This document contained both literal and "
1867 '"ligature" dashes.\n Line breaks may have changed. '
1868 "See UserGuide chapter 3.9.1 for details."
1872 if has_literal_dashes and not has_ligature_dashes:
1873 use_dash_ligatures = False
1874 elif has_ligature_dashes and not has_literal_dashes:
1875 use_dash_ligatures = True
1877 # insert the setting if there is a preferred value
1878 if use_dash_ligatures is True:
1879 document.header.insert(-1, "\\use_dash_ligatures true")
1880 elif use_dash_ligatures is False:
1881 document.header.insert(-1, "\\use_dash_ligatures false")
1884 def revert_dashligatures(document):
1885 """Remove font ligature settings for en- and em-dashes.
1886 Revert conversion of \twodashes or \threedashes to literal dashes.
1888 use_dash_ligatures = del_value(document.header, "\\use_dash_ligatures")
1889 if use_dash_ligatures != "true" or document.backend != "latex":
1892 dash_pattern = re.compile(".*[\u2013\u2014]")
1894 # skip lines without dashes:
1895 i = find_re(document.body, dash_pattern, i + 1)
1898 line = document.body[i]
1899 # skip label width string (see bug 10243):
1900 if line.startswith("\\labelwidthstring"):
1902 # do not touch hyphens in some insets (cf. lyx_2_2.convert_dashes):
1904 inset_type, start, end = get_containing_inset(document.body, i)
1905 except TypeError: # no containing inset
1906 inset_type, start, end = "no inset", -1, -1
1908 inset_type.split()[0]
1919 or inset_type == "Flex Code"
1924 layoutname, start, end, j = get_containing_layout(document.body, i)
1925 except TypeError: # no (or malformed) containing layout
1926 document.warning("Malformed LyX document: " "Can't find layout at body line %d" % i)
1928 if layoutname == "LyX-Code":
1931 # TODO: skip replacement in typewriter fonts
1932 line = line.replace("\u2013", "\\twohyphens\n")
1933 line = line.replace("\u2014", "\\threehyphens\n")
1934 document.body[i : i + 1] = line.split("\n")
1935 # redefine the dash LICRs to use ligature dashes:
1938 [r"\renewcommand{\textendash}{--}", r"\renewcommand{\textemdash}{---}"],
1942 def revert_noto(document):
1943 "Revert Noto font definitions to LaTeX"
1945 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1947 i = find_token(document.header, '\\font_roman "NotoSerif-TLF"', 0)
1949 add_to_preamble(document, ["\\renewcommand{\\rmdefault}{NotoSerif-TLF}"])
1950 document.header[i] = document.header[i].replace("NotoSerif-TLF", "default")
1951 i = find_token(document.header, '\\font_sans "NotoSans-TLF"', 0)
1953 add_to_preamble(document, ["\\renewcommand{\\sfdefault}{NotoSans-TLF}"])
1954 document.header[i] = document.header[i].replace("NotoSans-TLF", "default")
1955 i = find_token(document.header, '\\font_typewriter "NotoMono-TLF"', 0)
1957 add_to_preamble(document, ["\\renewcommand{\\ttdefault}{NotoMono-TLF}"])
1958 document.header[i] = document.header[i].replace("NotoMono-TLF", "default")
1961 def revert_xout(document):
1962 "Reverts \\xout font attribute"
1963 changed = revert_font_attrs(document.body, "\\xout", "\\xout")
1968 "% for proper cross-out",
1969 "\\PassOptionsToPackage{normalem}{ulem}",
1970 "\\usepackage{ulem}",
1975 def convert_mathindent(document):
1976 """Add the \\is_math_indent tag."""
1977 k = find_token(document.header, "\\quotes_style") # where to insert
1978 # check if the document uses the class option "fleqn"
1979 options = get_value(document.header, "\\options")
1980 if "fleqn" in options:
1981 document.header.insert(k, "\\is_math_indent 1")
1982 # delete the fleqn option
1983 i = find_token(document.header, "\\options")
1984 options = [option for option in options.split(",") if option.strip() != "fleqn"]
1986 document.header[i] = "\\options " + ",".join(options)
1988 del document.header[i]
1990 document.header.insert(k, "\\is_math_indent 0")
1993 def revert_mathindent(document):
1994 "Define mathindent if set in the document"
1995 # emulate and delete \math_indentation
1996 value = get_value(document.header, "\\math_indentation", default="default", delete=True)
1997 if value != "default":
1998 add_to_preamble(document, [r"\setlength{\mathindent}{%s}" % value])
1999 # delete \is_math_indent and emulate via document class option
2000 if not get_bool_value(document.header, "\\is_math_indent", delete=True):
2002 i = find_token(document.header, "\\options")
2004 document.header[i] = document.header[i].replace("\\options ", "\\options fleqn,")
2006 l = find_token(document.header, "\\use_default_options")
2007 document.header.insert(l, "\\options fleqn")
2010 def revert_baselineskip(document):
2011 "Revert baselineskips to TeX code"
2014 i = find_substring(document.body, "baselineskip%", i + 1)
2017 if document.body[i].startswith("\\begin_inset VSpace"):
2018 # output VSpace inset as TeX code
2019 end = find_end_of_inset(document.body, i)
2022 "Malformed LyX document: " "Can't find end of VSpace inset at line %d." % i
2025 # read out the value
2026 baselineskip = document.body[i].split()[-1]
2027 # check if it is the starred version
2028 star = "*" if "*" in document.body[i] else ""
2029 # now output TeX code
2030 cmd = "\\vspace%s{%s}" % (star, latex_length(baselineskip)[1])
2031 document.body[i : end + 1] = put_cmd_in_ert(cmd)
2034 begin, end = is_in_inset(document.body, i, "\\begin_inset space \\hspace")
2036 # output space inset as TeX code
2037 baselineskip = document.body[i].split()[-1]
2038 star = "*" if "*" in document.body[i - 1] else ""
2039 cmd = "\\hspace%s{%s}" % (star, latex_length(baselineskip)[1])
2040 document.body[begin : end + 1] = put_cmd_in_ert(cmd)
2043 def revert_rotfloat(document):
2044 "Revert placement options for rotated floats"
2049 i = find_token(document.body, "sideways true", i)
2052 if not document.body[i - 2].startswith("placement "):
2055 # we found a sideways float with placement options
2056 # at first store the placement
2057 beg = document.body[i - 2].rfind(" ")
2058 placement = document.body[i - 2][beg + 1 :]
2059 # check if the option'H' is used
2060 if placement.find("H") != -1:
2061 add_to_preamble(document, ["\\usepackage{float}"])
2062 # now check if it is a starred type
2063 if document.body[i - 1].find("wide true") != -1:
2067 # store the float type
2068 beg = document.body[i - 3].rfind(" ")
2069 fType = document.body[i - 3][beg + 1 :]
2070 # now output TeX code
2071 endInset = find_end_of_inset(document.body, i - 3)
2073 document.warning("Malformed LyX document: Missing '\\end_inset' of Float inset.")
2076 document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert(
2077 "\\end{sideways" + fType + star + "}"
2079 document.body[i - 3 : i + 2] = put_cmd_in_ert(
2080 "\\begin{sideways" + fType + star + "}[" + placement + "]"
2082 add_to_preamble(document, ["\\usepackage{rotfloat}"])
2087 allowbreak_emulation = [
2088 r"\begin_inset space \hspace{}",
2095 def convert_allowbreak(document):
2096 r"Zero widths Space-inset -> \SpecialChar allowbreak."
2097 lines = document.body
2098 i = find_complete_lines(lines, allowbreak_emulation, 2)
2100 lines[i - 1 : i + 4] = [lines[i - 1] + r"\SpecialChar allowbreak"]
2101 i = find_complete_lines(lines, allowbreak_emulation, i + 3)
2104 def revert_allowbreak(document):
2105 r"\SpecialChar allowbreak -> Zero widths Space-inset."
2107 lines = document.body
2108 while i < len(lines):
2109 if lines[i].endswith(r"\SpecialChar allowbreak"):
2110 lines[i : i + 1] = [
2111 lines[i].replace(r"\SpecialChar allowbreak", "")
2112 ] + allowbreak_emulation
2118 def convert_mathnumberpos(document):
2119 "add the \\math_number_before tag"
2120 i = find_token(document.header, "\\quotes_style")
2121 # check if the document uses the class option "leqno"
2122 if is_document_option(document, "leqno"):
2123 remove_document_option(document, "leqno")
2124 document.header.insert(i, "\\math_number_before 1")
2126 document.header.insert(i, "\\math_number_before 0")
2129 def revert_mathnumberpos(document):
2130 """Remove \\math_number_before tag,
2131 add the document class option leqno if required.
2133 math_number_before = get_bool_value(document.header, "\\math_number_before", delete=True)
2134 if math_number_before:
2135 insert_document_option(document, "leqno")
2138 def convert_mathnumberingname(document):
2139 "rename the \\math_number_before tag to \\math_numbering_side"
2140 i = find_token(document.header, "\\math_number_before")
2141 math_number_before = get_bool_value(document.header, "\\math_number_before", i)
2142 if math_number_before:
2143 document.header[i] = "\\math_numbering_side left"
2145 # check if the document uses the class option "reqno"
2146 k = find_token(document.header, "\\options")
2147 if "reqno" in document.header[k]:
2148 document.header[i] = "\\math_numbering_side right"
2149 # delete the found option
2150 document.header[k] = document.header[k].replace(",reqno", "")
2151 document.header[k] = document.header[k].replace(", reqno", "")
2152 document.header[k] = document.header[k].replace("reqno,", "")
2153 if "reqno" in document.header[k]:
2154 # then we have reqno as the only option
2155 del document.header[k]
2157 document.header[i] = "\\math_numbering_side default"
2160 def revert_mathnumberingname(document):
2161 "rename the \\math_numbering_side tag back to \\math_number_before"
2162 i = find_token(document.header, "\\math_numbering_side")
2163 math_numbering_side = get_value(document.header, "\\math_numbering_side", i)
2164 # rename tag and set boolean value:
2165 if math_numbering_side == "left":
2166 document.header[i] = "\\math_number_before 1"
2167 elif math_numbering_side == "right":
2168 # also add the option reqno:
2169 document.header[i] = "\\math_number_before 0"
2170 k = find_token(document.header, "\\options")
2171 if k != -1 and "reqno" not in document.header[k]:
2172 document.header[k] = document.header[k].replace("\\options", "\\options reqno,")
2174 l = find_token(document.header, "\\use_default_options", 0)
2175 document.header.insert(l, "\\options reqno")
2177 document.header[i] = "\\math_number_before 0"
2180 def convert_minted(document):
2181 "add the \\use_minted tag"
2182 i = find_token(document.header, "\\index ")
2183 document.header.insert(i, "\\use_minted 0")
2186 def revert_minted(document):
2187 "remove the \\use_minted tag"
2188 del_token(document.header, "\\use_minted")
2191 def revert_longtable_lscape(document):
2192 "revert the longtable landcape mode to ERT"
2194 regexp = re.compile(r"^<features rotate=\"90\"\s.*islongtable=\"true\"\s.*$", re.IGNORECASE)
2196 i = find_re(document.body, regexp, i)
2200 document.body[i] = document.body[i].replace(' rotate="90"', "")
2201 lay = get_containing_layout(document.body, i)
2203 document.warning("Longtable has not layout!")
2206 begcmd = put_cmd_in_ert("\\begin{landscape}")
2207 endcmd = put_cmd_in_ert("\\end{landscape}")
2208 document.body[lay[2] : lay[2]] = endcmd + ["\\end_layout"]
2209 document.body[lay[1] : lay[1]] = ["\\begin_layout " + lay[0], ""] + begcmd
2211 add_to_preamble(document, ["\\usepackage{pdflscape}"])
2219 supported_versions = ["2.3.0", "2.3"]
2221 [509, [convert_microtype]],
2222 [510, [convert_dateinset]],
2223 [511, [convert_ibranches]],
2224 [512, [convert_beamer_article_styles]],
2228 [516, [convert_inputenc]],
2230 [518, [convert_iopart]],
2231 [519, [convert_quotestyle]],
2233 [521, [convert_frenchquotes]],
2236 [524, [convert_crimson]],
2244 [532, [convert_literalparam]],
2247 [535, [convert_dashligatures]],
2250 [538, [convert_mathindent]],
2253 [541, [convert_allowbreak]],
2254 [542, [convert_mathnumberpos]],
2255 [543, [convert_mathnumberingname]],
2256 [544, [convert_minted]],
2260 [543, [revert_minted, revert_longtable_lscape]],
2261 [542, [revert_mathnumberingname]],
2262 [541, [revert_mathnumberpos]],
2263 [540, [revert_allowbreak]],
2264 [539, [revert_rotfloat]],
2265 [538, [revert_baselineskip]],
2266 [537, [revert_mathindent]],
2267 [536, [revert_xout]],
2268 [535, [revert_noto]],
2269 [534, [revert_dashligatures]],
2270 [533, [revert_chapterbib]],
2271 [532, [revert_multibib]],
2272 [531, [revert_literalparam]],
2273 [530, [revert_qualicites]],
2274 [529, [revert_bibpackopts]],
2275 [528, [revert_citekeyonly]],
2276 [527, [revert_biblatex]],
2277 [526, [revert_noprefix]],
2278 [525, [revert_plural_refs]],
2279 [524, [revert_labelonly]],
2280 [523, [revert_crimson, revert_cochinealmath]],
2281 [522, [revert_cjkquotes]],
2282 [521, [revert_dynamicquotes]],
2286 revert_britishquotes,
2287 revert_swedishgquotes,
2288 revert_frenchquotes,
2289 revert_frenchinquotes,
2290 revert_russianquotes,
2294 [519, [revert_plainquote]],
2295 [518, [revert_quotestyle]],
2296 [517, [revert_iopart]],
2297 [516, [revert_quotes]],
2299 [514, [revert_urdu, revert_syriac]],
2300 [513, [revert_amharic, revert_asturian, revert_kannada, revert_khmer]],
2301 [512, [revert_new_babel_languages]],
2302 [511, [revert_beamer_article_styles]],
2303 [510, [revert_ibranches]],
2305 [508, [revert_microtype]],
2309 if __name__ == "__main__":