1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # Copyright (C) 2016 The LyX team
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 """ Convert files to the file format generated by lyx 2.3"""
25 # Uncomment only what you need to import, please.
27 from parser_tools import (del_token, del_value, del_complete_lines,
28 find_complete_lines, find_end_of, find_end_of_layout, find_end_of_inset,
29 find_re, find_token, find_token_backwards, get_containing_inset,
30 get_containing_layout, get_bool_value, get_value, get_quoted_value,
32 # find_tokens, find_token_exact, check_token, get_option_value
34 from lyx2lyx_tools import (add_to_preamble, put_cmd_in_ert, revert_font_attrs,
35 insert_to_preamble, latex_length)
36 # get_ert, lyx2latex, lyx2verbatim, length_in_bp, convert_info_insets
37 # revert_flex_inset, hex2ratio, str2bool
39 ####################################################################
40 # Private helper functions
44 ###############################################################################
46 ### Conversion and reversion routines
48 ###############################################################################
50 def convert_microtype(document):
51 " Add microtype settings. "
52 i = find_token(document.header, "\\font_tt_scale" , 0)
54 document.warning("Malformed LyX document: Can't find \\font_tt_scale.")
55 i = len(document.header) - 1
57 j = find_token(document.preamble, "\\usepackage{microtype}", 0)
59 document.header.insert(i + 1, "\\use_microtype false")
61 document.header.insert(i + 1, "\\use_microtype true")
62 del document.preamble[j]
65 def revert_microtype(document):
66 " Remove microtype settings. "
67 i = find_token(document.header, "\\use_microtype", 0)
70 use_microtype = get_bool_value(document.header, "\\use_microtype" , i)
71 del document.header[i]
73 add_to_preamble(document, ["\\usepackage{microtype}"])
76 def convert_dateinset(document):
77 ' Convert date external inset to ERT '
80 i = find_token(document.body, "\\begin_inset External", i)
83 j = find_end_of_inset(document.body, i)
85 document.warning("Malformed lyx document: Missing '\\end_inset' in convert_dateinset.")
88 if get_value(document.body, 'template', i, j) == "Date":
89 document.body[i : j + 1] = put_cmd_in_ert("\\today ")
93 def convert_inputenc(document):
94 " Replace no longer supported input encoding settings. "
95 i = find_token(document.header, "\\inputenc")
98 if get_value(document.header, "\\inputencoding", i) == "pt254":
99 document.header[i] = "\\inputencoding pt154"
102 def convert_ibranches(document):
103 ' Add "inverted 0" to branch insets'
106 i = find_token(document.body, "\\begin_inset Branch", i)
109 document.body.insert(i + 1, "inverted 0")
113 def revert_ibranches(document):
114 ' Convert inverted branches to explicit anti-branches'
115 # Get list of branches
119 i = find_token(document.header, "\\branch", i)
122 branch = document.header[i][8:].strip()
123 if document.header[i+1].startswith("\\selected "):
124 #document.warning(document.header[i+1])
125 #document.warning(document.header[i+1][10])
126 selected = int(document.header[i+1][10])
128 document.warning("Malformed LyX document: No selection indicator for branch " + branch)
131 # the value tells us whether the branch is selected
132 ourbranches[document.header[i][8:].strip()] = selected
135 # Figure out what inverted branches, if any, have been used
136 # and convert them to "Anti-OldBranch"
140 i = find_token(document.body, "\\begin_inset Branch", i)
143 if not document.body[i+1].startswith("inverted "):
144 document.warning("Malformed LyX document: Missing 'inverted' tag!")
147 inverted = document.body[i+1][9]
148 #document.warning(document.body[i+1])
151 branch = document.body[i][20:].strip()
152 #document.warning(branch)
153 if not branch in ibranches:
154 antibranch = "Anti-" + branch
155 while antibranch in ibranches:
156 antibranch = "x" + antibranch
157 ibranches[branch] = antibranch
159 antibranch = ibranches[branch]
160 #document.warning(antibranch)
161 document.body[i] = "\\begin_inset Branch " + antibranch
163 # remove "inverted" key
164 del document.body[i+1]
167 # now we need to add the new branches to the header
168 for old, new in ibranches.items():
169 i = find_token(document.header, "\\branch " + old, 0)
171 document.warning("Can't find branch %s even though we found it before!" % (old))
173 j = find_token(document.header, "\\end_branch", i)
175 document.warning("Malformed LyX document! Can't find end of branch " + old)
177 # ourbranches[old] - 1 inverts the selection status of the old branch
178 lines = ["\\branch " + new,
179 "\\selected " + str(ourbranches[old] - 1)]
180 # these are the old lines telling us color, etc.
181 lines += document.header[i+2 : j+1]
182 document.header[i:i] = lines
185 def revert_beamer_article_styles(document):
186 " Include (scr)article styles in beamer article "
188 beamer_articles = ["article-beamer", "scrarticle-beamer"]
189 if document.textclass not in beamer_articles:
192 inclusion = "article.layout"
193 if document.textclass == "scrarticle-beamer":
194 inclusion = "scrartcl.layout"
196 i = find_token(document.header, "\\begin_local_layout", 0)
198 k = find_token(document.header, "\\language", 0)
200 # this should not happen
201 document.warning("Malformed LyX document! No \\language header found!")
203 document.header[k-1 : k-1] = ["\\begin_local_layout", "\\end_local_layout"]
206 j = find_end_of(document.header, i, "\\begin_local_layout", "\\end_local_layout")
208 # this should not happen
209 document.warning("Malformed LyX document: Can't find end of local layout!")
212 document.header[i+1 : i+1] = [
213 "### Inserted by lyx2lyx (more [scr]article styles) ###",
214 "Input " + inclusion,
215 "Input beamer.layout",
216 "Provides geometry 0",
217 "Provides hyperref 0",
226 " \\usepackage{beamerarticle,pgf}",
227 " % this default might be overridden by plain title style",
228 " \\newcommand\makebeamertitle{\\frame{\\maketitle}}%",
229 " \\AtBeginDocument{",
230 " \\let\\origtableofcontents=\\tableofcontents",
231 " \\def\\tableofcontents{\\@ifnextchar[{\\origtableofcontents}{\\gobbletableofcontents}}",
232 " \\def\\gobbletableofcontents#1{\\origtableofcontents}",
235 "### End of insertion by lyx2lyx (more [scr]article styles) ###"
239 def convert_beamer_article_styles(document):
240 " Remove included (scr)article styles in beamer article "
242 beamer_articles = ["article-beamer", "scrarticle-beamer"]
243 if document.textclass not in beamer_articles:
246 i = find_token(document.header, "\\begin_local_layout", 0)
250 j = find_end_of(document.header, i, "\\begin_local_layout", "\\end_local_layout")
252 # this should not happen
253 document.warning("Malformed LyX document: Can't find end of local layout!")
256 k = find_token(document.header, "### Inserted by lyx2lyx (more [scr]article styles) ###", i, j)
258 l = find_token(document.header, "### End of insertion by lyx2lyx (more [scr]article styles) ###", i, j)
260 # this should not happen
261 document.warning("End of lyx2lyx local layout insertion not found!")
264 if k == i + 1 and l == j - 1:
265 # that was all the local layout there was
266 document.header[i : j + 1] = []
268 document.header[k : l + 1] = []
271 def revert_bosnian(document):
272 "Set the document language to English but assure Bosnian output"
274 if document.language == "bosnian":
275 document.language = "english"
276 i = find_token(document.header, "\\language bosnian", 0)
278 document.header[i] = "\\language english"
279 j = find_token(document.header, "\\language_package default", 0)
281 document.header[j] = "\\language_package babel"
282 k = find_token(document.header, "\\options", 0)
284 document.header[k] = document.header[k].replace("\\options", "\\options bosnian,")
286 l = find_token(document.header, "\\use_default_options", 0)
287 document.header.insert(l + 1, "\\options bosnian")
290 def revert_friulan(document):
291 "Set the document language to English but assure Friulan output"
293 if document.language == "friulan":
294 document.language = "english"
295 i = find_token(document.header, "\\language friulan", 0)
297 document.header[i] = "\\language english"
298 j = find_token(document.header, "\\language_package default", 0)
300 document.header[j] = "\\language_package babel"
301 k = find_token(document.header, "\\options", 0)
303 document.header[k] = document.header[k].replace("\\options", "\\options friulan,")
305 l = find_token(document.header, "\\use_default_options", 0)
306 document.header.insert(l + 1, "\\options friulan")
309 def revert_macedonian(document):
310 "Set the document language to English but assure Macedonian output"
312 if document.language == "macedonian":
313 document.language = "english"
314 i = find_token(document.header, "\\language macedonian", 0)
316 document.header[i] = "\\language english"
317 j = find_token(document.header, "\\language_package default", 0)
319 document.header[j] = "\\language_package babel"
320 k = find_token(document.header, "\\options", 0)
322 document.header[k] = document.header[k].replace("\\options", "\\options macedonian,")
324 l = find_token(document.header, "\\use_default_options", 0)
325 document.header.insert(l + 1, "\\options macedonian")
328 def revert_piedmontese(document):
329 "Set the document language to English but assure Piedmontese output"
331 if document.language == "piedmontese":
332 document.language = "english"
333 i = find_token(document.header, "\\language piedmontese", 0)
335 document.header[i] = "\\language english"
336 j = find_token(document.header, "\\language_package default", 0)
338 document.header[j] = "\\language_package babel"
339 k = find_token(document.header, "\\options", 0)
341 document.header[k] = document.header[k].replace("\\options", "\\options piedmontese,")
343 l = find_token(document.header, "\\use_default_options", 0)
344 document.header.insert(l + 1, "\\options piedmontese")
347 def revert_romansh(document):
348 "Set the document language to English but assure Romansh output"
350 if document.language == "romansh":
351 document.language = "english"
352 i = find_token(document.header, "\\language romansh", 0)
354 document.header[i] = "\\language english"
355 j = find_token(document.header, "\\language_package default", 0)
357 document.header[j] = "\\language_package babel"
358 k = find_token(document.header, "\\options", 0)
360 document.header[k] = document.header[k].replace("\\options", "\\options romansh,")
362 l = find_token(document.header, "\\use_default_options", 0)
363 document.header.insert(l + 1, "\\options romansh")
366 def revert_amharic(document):
367 "Set the document language to English but assure Amharic output"
369 if document.language == "amharic":
370 document.language = "english"
371 i = find_token(document.header, "\\language amharic", 0)
373 document.header[i] = "\\language english"
374 j = find_token(document.header, "\\language_package default", 0)
376 document.header[j] = "\\language_package default"
377 add_to_preamble(document, ["\\AtBeginDocument{\setotherlanguage{amharic}}"])
378 document.body[2 : 2] = ["\\begin_layout Standard",
379 "\\begin_inset ERT", "status open", "",
380 "\\begin_layout Plain Layout", "", "",
382 "resetdefaultlanguage{amharic}",
383 "\\end_layout", "", "\\end_inset", "", "",
387 def revert_asturian(document):
388 "Set the document language to English but assure Asturian output"
390 if document.language == "asturian":
391 document.language = "english"
392 i = find_token(document.header, "\\language asturian", 0)
394 document.header[i] = "\\language english"
395 j = find_token(document.header, "\\language_package default", 0)
397 document.header[j] = "\\language_package default"
398 add_to_preamble(document, ["\\AtBeginDocument{\setotherlanguage{asturian}}"])
399 document.body[2 : 2] = ["\\begin_layout Standard",
400 "\\begin_inset ERT", "status open", "",
401 "\\begin_layout Plain Layout", "", "",
403 "resetdefaultlanguage{asturian}",
404 "\\end_layout", "", "\\end_inset", "", "",
408 def revert_kannada(document):
409 "Set the document language to English but assure Kannada output"
411 if document.language == "kannada":
412 document.language = "english"
413 i = find_token(document.header, "\\language kannada", 0)
415 document.header[i] = "\\language english"
416 j = find_token(document.header, "\\language_package default", 0)
418 document.header[j] = "\\language_package default"
419 add_to_preamble(document, ["\\AtBeginDocument{\setotherlanguage{kannada}}"])
420 document.body[2 : 2] = ["\\begin_layout Standard",
421 "\\begin_inset ERT", "status open", "",
422 "\\begin_layout Plain Layout", "", "",
424 "resetdefaultlanguage{kannada}",
425 "\\end_layout", "", "\\end_inset", "", "",
429 def revert_khmer(document):
430 "Set the document language to English but assure Khmer output"
432 if document.language == "khmer":
433 document.language = "english"
434 i = find_token(document.header, "\\language khmer", 0)
436 document.header[i] = "\\language english"
437 j = find_token(document.header, "\\language_package default", 0)
439 document.header[j] = "\\language_package default"
440 add_to_preamble(document, ["\\AtBeginDocument{\setotherlanguage{khmer}}"])
441 document.body[2 : 2] = ["\\begin_layout Standard",
442 "\\begin_inset ERT", "status open", "",
443 "\\begin_layout Plain Layout", "", "",
445 "resetdefaultlanguage{khmer}",
446 "\\end_layout", "", "\\end_inset", "", "",
450 def revert_urdu(document):
451 "Set the document language to English but assure Urdu output"
453 if document.language == "urdu":
454 document.language = "english"
455 i = find_token(document.header, "\\language urdu", 0)
457 document.header[i] = "\\language english"
458 j = find_token(document.header, "\\language_package default", 0)
460 document.header[j] = "\\language_package default"
461 add_to_preamble(document, ["\\AtBeginDocument{\setotherlanguage{urdu}}"])
462 document.body[2 : 2] = ["\\begin_layout Standard",
463 "\\begin_inset ERT", "status open", "",
464 "\\begin_layout Plain Layout", "", "",
466 "resetdefaultlanguage{urdu}",
467 "\\end_layout", "", "\\end_inset", "", "",
471 def revert_syriac(document):
472 "Set the document language to English but assure Syriac output"
474 if document.language == "syriac":
475 document.language = "english"
476 i = find_token(document.header, "\\language syriac", 0)
478 document.header[i] = "\\language english"
479 j = find_token(document.header, "\\language_package default", 0)
481 document.header[j] = "\\language_package default"
482 add_to_preamble(document, ["\\AtBeginDocument{\setotherlanguage{syriac}}"])
483 document.body[2 : 2] = ["\\begin_layout Standard",
484 "\\begin_inset ERT", "status open", "",
485 "\\begin_layout Plain Layout", "", "",
487 "resetdefaultlanguage{syriac}",
488 "\\end_layout", "", "\\end_inset", "", "",
492 def revert_quotes(document):
493 " Revert Quote Insets in verbatim or Hebrew context to plain quotes "
495 # First handle verbatim insets
498 while i < len(document.body):
499 words = document.body[i].split()
500 if len(words) > 1 and words[0] == "\\begin_inset" and \
501 ( words[1] in ["ERT", "listings"] or ( len(words) > 2 and words[2] in ["URL", "Chunk", "Sweave", "S/R"]) ):
502 j = find_end_of_inset(document.body, i)
504 document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
508 k = find_token(document.body, '\\begin_inset Quotes', i, j)
512 l = find_end_of_inset(document.body, k)
514 document.warning("Malformed LyX document: Can't find end of Quote inset at line " + str(k))
518 if document.body[k].endswith("s"):
520 document.body[k:l+1] = [replace]
525 # Now verbatim layouts
528 while i < len(document.body):
529 words = document.body[i].split()
530 if len(words) > 1 and words[0] == "\\begin_layout" and \
531 words[1] in ["Verbatim", "Verbatim*", "Code", "Author_Email", "Author_URL"]:
532 j = find_end_of_layout(document.body, i)
534 document.warning("Malformed LyX document: Can't find end of " + words[1] + " layout at line " + str(i))
538 k = find_token(document.body, '\\begin_inset Quotes', i, j)
542 l = find_end_of_inset(document.body, k)
544 document.warning("Malformed LyX document: Can't find end of Quote inset at line " + str(k))
548 if document.body[k].endswith("s"):
550 document.body[k:l+1] = [replace]
556 if not document.language == "hebrew" and find_token(document.body, '\\lang hebrew', 0) == -1:
562 k = find_token(document.body, '\\begin_inset Quotes', i)
565 l = find_end_of_inset(document.body, k)
567 document.warning("Malformed LyX document: Can't find end of Quote inset at line " + str(k))
571 parent = get_containing_layout(document.body, k)
572 ql = find_token_backwards(document.body, "\\lang", k)
573 if ql == -1 or ql < parent[1]:
574 hebrew = document.language == "hebrew"
575 elif document.body[ql] == "\\lang hebrew":
579 if document.body[k].endswith("s"):
581 document.body[k:l+1] = [replace]
585 def revert_iopart(document):
586 " Input new styles via local layout "
587 if document.textclass != "iopart":
590 i = find_token(document.header, "\\begin_local_layout", 0)
592 k = find_token(document.header, "\\language", 0)
594 # this should not happen
595 document.warning("Malformed LyX document! No \\language header found!")
597 document.header[k-1 : k-1] = ["\\begin_local_layout", "\\end_local_layout"]
600 j = find_end_of(document.header, i, "\\begin_local_layout", "\\end_local_layout")
602 # this should not happen
603 document.warning("Malformed LyX document! Can't find end of local layout!")
606 document.header[i+1 : i+1] = [
607 "### Inserted by lyx2lyx (stdlayouts) ###",
608 "Input stdlayouts.inc",
609 "### End of insertion by lyx2lyx (stdlayouts) ###"
613 def convert_iopart(document):
614 " Remove local layout we added, if it is there "
615 if document.textclass != "iopart":
618 i = find_token(document.header, "\\begin_local_layout", 0)
622 j = find_end_of(document.header, i, "\\begin_local_layout", "\\end_local_layout")
624 # this should not happen
625 document.warning("Malformed LyX document! Can't find end of local layout!")
628 k = find_token(document.header, "### Inserted by lyx2lyx (stdlayouts) ###", i, j)
630 l = find_token(document.header, "### End of insertion by lyx2lyx (stdlayouts) ###", i, j)
632 # this should not happen
633 document.warning("End of lyx2lyx local layout insertion not found!")
635 if k == i + 1 and l == j - 1:
636 # that was all the local layout there was
637 document.header[i : j + 1] = []
639 document.header[k : l + 1] = []
642 def convert_quotestyle(document):
643 " Convert \\quotes_language to \\quotes_style "
644 i = find_token(document.header, "\\quotes_language", 0)
646 document.warning("Malformed LyX document! Can't find \\quotes_language!")
648 val = get_value(document.header, "\\quotes_language", i)
649 document.header[i] = "\\quotes_style " + val
652 def revert_quotestyle(document):
653 " Revert \\quotes_style to \\quotes_language "
654 i = find_token(document.header, "\\quotes_style", 0)
656 document.warning("Malformed LyX document! Can't find \\quotes_style!")
658 val = get_value(document.header, "\\quotes_style", i)
659 document.header[i] = "\\quotes_language " + val
662 def revert_plainquote(document):
663 " Revert plain quote insets "
665 # First, revert style setting
666 i = find_token(document.header, "\\quotes_style plain", 0)
668 document.header[i] = "\\quotes_style english"
674 k = find_token(document.body, '\\begin_inset Quotes q', i)
677 l = find_end_of_inset(document.body, k)
679 document.warning("Malformed LyX document: Can't find end of Quote inset at line " + str(k))
683 if document.body[k].endswith("s"):
685 document.body[k:l+1] = [replace]
689 def convert_frenchquotes(document):
690 " Convert french quote insets to swiss "
692 # First, revert style setting
693 i = find_token(document.header, "\\quotes_style french", 0)
695 document.header[i] = "\\quotes_style swiss"
700 i = find_token(document.body, '\\begin_inset Quotes f', i)
703 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
704 newval = val.replace("f", "c", 1)
705 document.body[i] = document.body[i].replace(val, newval)
709 def revert_swissquotes(document):
710 " Revert swiss quote insets to french "
712 # First, revert style setting
713 i = find_token(document.header, "\\quotes_style swiss", 0)
715 document.header[i] = "\\quotes_style french"
720 i = find_token(document.body, '\\begin_inset Quotes c', i)
723 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
724 newval = val.replace("c", "f", 1)
725 document.body[i] = document.body[i].replace(val, newval)
729 def revert_britishquotes(document):
730 " Revert british quote insets to english "
732 # First, revert style setting
733 i = find_token(document.header, "\\quotes_style british", 0)
735 document.header[i] = "\\quotes_style english"
740 i = find_token(document.body, '\\begin_inset Quotes b', i)
743 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
744 newval = val.replace("b", "e", 1)
747 newval = newval.replace("d", "s")
750 newval = newval.replace("s", "d")
751 document.body[i] = document.body[i].replace(val, newval)
755 def revert_swedishgquotes(document):
756 " Revert swedish quote insets "
758 # First, revert style setting
759 i = find_token(document.header, "\\quotes_style swedishg", 0)
761 document.header[i] = "\\quotes_style danish"
766 i = find_token(document.body, '\\begin_inset Quotes w', i)
769 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
772 newval = val.replace("w", "a", 1).replace("r", "l")
775 newval = val.replace("w", "s", 1)
776 document.body[i] = document.body[i].replace(val, newval)
780 def revert_frenchquotes(document):
781 " Revert french inner quote insets "
785 i = find_token(document.body, '\\begin_inset Quotes f', i)
788 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
791 newval = val.replace("f", "e", 1).replace("s", "d")
792 document.body[i] = document.body[i].replace(val, newval)
796 def revert_frenchinquotes(document):
797 " Revert inner frenchin quote insets "
799 # First, revert style setting
800 i = find_token(document.header, "\\quotes_style frenchin", 0)
802 document.header[i] = "\\quotes_style french"
807 i = find_token(document.body, '\\begin_inset Quotes i', i)
810 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
811 newval = val.replace("i", "f", 1)
814 newval = newval.replace("s", "d")
815 document.body[i] = document.body[i].replace(val, newval)
819 def revert_russianquotes(document):
820 " Revert russian quote insets "
822 # First, revert style setting
823 i = find_token(document.header, "\\quotes_style russian", 0)
825 document.header[i] = "\\quotes_style french"
830 i = find_token(document.body, '\\begin_inset Quotes r', i)
833 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
837 newval = val.replace("r", "g", 1).replace("s", "d")
840 newval = val.replace("r", "f", 1)
841 document.body[i] = document.body[i].replace(val, newval)
845 def revert_dynamicquotes(document):
846 " Revert dynamic quote insets "
848 # First, revert header
849 i = find_token(document.header, "\\dynamic_quotes", 0)
851 del document.header[i]
855 i = find_token(document.header, "\\quotes_style", 0)
857 document.warning("Malformed document! Missing \\quotes_style")
859 style = get_value(document.header, "\\quotes_style", i)
862 if style == "english":
864 elif style == "swedish":
866 elif style == "german":
868 elif style == "polish":
870 elif style == "swiss":
872 elif style == "danish":
874 elif style == "plain":
876 elif style == "british":
878 elif style == "swedishg":
880 elif style == "french":
882 elif style == "frenchin":
884 elif style == "russian":
887 # now transform the insets
890 i = find_token(document.body, '\\begin_inset Quotes x', i)
893 document.body[i] = document.body[i].replace("x", s)
897 def revert_cjkquotes(document):
898 " Revert cjk quote insets "
902 i = find_token(document.header, "\\quotes_style", 0)
904 document.warning("Malformed document! Missing \\quotes_style")
906 style = get_value(document.header, "\\quotes_style", i)
908 global_cjk = style.find("cjk") != -1
911 document.header[i] = "\\quotes_style english"
912 # transform dynamic insets
914 if style == "cjkangle":
918 i = find_token(document.body, '\\begin_inset Quotes x', i)
921 document.body[i] = document.body[i].replace("x", s)
924 cjk_langs = ["chinese-simplified", "chinese-traditional", "japanese", "japanese-cjk", "korean"]
929 k = find_token(document.body, '\\begin_inset Quotes j', i)
932 l = find_end_of_inset(document.body, k)
934 document.warning("Malformed LyX document: Can't find end of Quote inset at line " + str(k))
938 parent = get_containing_layout(document.body, k)
939 ql = find_token_backwards(document.body, "\\lang", k)
940 if ql == -1 or ql < parent[1]:
941 cjk = document.language in cjk_langs
942 elif document.body[ql].split()[1] in cjk_langs:
944 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
951 replace = [u"\u300E"]
953 replace = ["\\begin_inset Formula $\\llceil$", "\\end_inset"]
957 replace = [u"\u300F"]
959 replace = ["\\begin_inset Formula $\\rrfloor$", "\\end_inset"]
965 replace = [u"\u300C"]
967 replace = ["\\begin_inset Formula $\\lceil$", "\\end_inset"]
971 replace = [u"\u300D"]
973 replace = ["\\begin_inset Formula $\\rfloor$", "\\end_inset"]
975 document.body[k:l+1] = replace
981 k = find_token(document.body, '\\begin_inset Quotes k', i)
984 l = find_end_of_inset(document.body, k)
986 document.warning("Malformed LyX document: Can't find end of Quote inset at line " + str(k))
990 parent = get_containing_layout(document.body, k)
991 ql = find_token_backwards(document.body, "\\lang", k)
992 if ql == -1 or ql < parent[1]:
993 cjk = document.language in cjk_langs
994 elif document.body[ql].split()[1] in cjk_langs:
996 val = get_value(document.body, "\\begin_inset Quotes", i)[7:]
1001 # inner opening mark
1003 replace = [u"\u3008"]
1005 replace = ["\\begin_inset Formula $\\langle$", "\\end_inset"]
1007 # inner closing mark
1009 replace = [u"\u3009"]
1011 replace = ["\\begin_inset Formula $\\rangle$", "\\end_inset"]
1015 # outer opening mark
1017 replace = [u"\u300A"]
1019 replace = ["\\begin_inset Formula $\\langle\\kern -2.5pt\\langle$", "\\end_inset"]
1021 # outer closing mark
1023 replace = [u"\u300B"]
1025 replace = ["\\begin_inset Formula $\\rangle\\kern -2.5pt\\rangle$", "\\end_inset"]
1027 document.body[k:l+1] = replace
1031 def revert_crimson(document):
1032 " Revert native Cochineal/Crimson font definition to LaTeX "
1034 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1036 i = find_token(document.header, "\\font_roman \"cochineal\"", 0)
1039 j = find_token(document.header, "\\font_osf true", 0)
1042 preamble = "\\usepackage"
1044 document.header[j] = "\\font_osf false"
1045 preamble += "[proportional,osf]"
1046 preamble += "{cochineal}"
1047 add_to_preamble(document, [preamble])
1048 document.header[i] = document.header[i].replace("cochineal", "default")
1051 def revert_cochinealmath(document):
1052 " Revert cochineal newtxmath definitions to LaTeX "
1054 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1055 i = find_token(document.header, "\\font_math \"cochineal-ntxm\"", 0)
1057 add_to_preamble(document, "\\usepackage[cochineal]{newtxmath}")
1058 document.header[i] = document.header[i].replace("cochineal-ntxm", "auto")
1061 def revert_labelonly(document):
1062 " Revert labelonly tag for InsetRef "
1065 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1068 j = find_end_of_inset(document.body, i)
1070 document.warning("Can't find end of reference inset at line %d!!" %(i))
1073 k = find_token(document.body, "LatexCommand labelonly", i, j)
1077 label = get_quoted_value(document.body, "reference", i, j)
1079 document.warning("Can't find label for reference at line %d!" %(i))
1082 document.body[i:j+1] = put_cmd_in_ert([label])
1086 def revert_plural_refs(document):
1087 " Revert plural and capitalized references "
1088 i = find_token(document.header, "\\use_refstyle 1", 0)
1089 use_refstyle = (i != 0)
1093 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1096 j = find_end_of_inset(document.body, i)
1098 document.warning("Can't find end of reference inset at line %d!!" %(i))
1102 plural = caps = suffix = False
1103 k = find_token(document.body, "LaTeXCommand formatted", i, j)
1104 if k != -1 and use_refstyle:
1105 plural = get_bool_value(document.body, "plural", i, j, False)
1106 caps = get_bool_value(document.body, "caps", i, j, False)
1107 label = get_quoted_value(document.body, "reference", i, j)
1110 (prefix, suffix) = label.split(":", 1)
1112 document.warning("No `:' separator in formatted reference at line %d!" % (i))
1114 document.warning("Can't find label for reference at line %d!" % (i))
1116 # this effectively tests also for use_refstyle and a formatted reference
1117 # we do this complicated test because we would otherwise do this erasure
1118 # over and over and over
1119 if not ((plural or caps) and suffix):
1120 del_token(document.body, "plural", i, j)
1121 del_token(document.body, "caps", i, j - 1) # since we deleted a line
1126 prefix = prefix[0].title() + prefix[1:]
1127 cmd = "\\" + prefix + "ref"
1130 cmd += "{" + suffix + "}"
1131 document.body[i:j+1] = put_cmd_in_ert([cmd])
1135 def revert_noprefix(document):
1136 " Revert labelonly tags with 'noprefix' set "
1139 i = find_token(document.body, "\\begin_inset CommandInset ref", i)
1142 j = find_end_of_inset(document.body, i)
1144 document.warning("Can't find end of reference inset at line %d!!" %(i))
1147 k = find_token(document.body, "LatexCommand labelonly", i, j)
1150 noprefix = get_bool_value(document.body, "noprefix", i, j)
1152 # either it was not a labelonly command, or else noprefix was not set.
1153 # in that case, we just delete the option.
1154 del_token(document.body, "noprefix", i, j)
1157 label = get_quoted_value(document.body, "reference", i, j)
1159 document.warning("Can't find label for reference at line %d!" %(i))
1163 (prefix, suffix) = label.split(":", 1)
1165 document.warning("No `:' separator in formatted reference at line %d!" % (i))
1166 # we'll leave this as an ordinary labelonly reference
1167 del_token(document.body, "noprefix", i, j)
1170 document.body[i:j+1] = put_cmd_in_ert([suffix])
1174 def revert_biblatex(document):
1175 " Revert biblatex support "
1181 # 1. Get cite engine
1183 i = find_token(document.header, "\\cite_engine", 0)
1185 document.warning("Malformed document! Missing \\cite_engine")
1187 engine = get_value(document.header, "\\cite_engine", i)
1189 # 2. Store biblatex state and revert to natbib
1191 if engine in ["biblatex", "biblatex-natbib"]:
1193 document.header[i] = "\\cite_engine natbib"
1195 # 3. Store and remove new document headers
1197 i = find_token(document.header, "\\biblatex_bibstyle", 0)
1199 bibstyle = get_value(document.header, "\\biblatex_bibstyle", i)
1200 del document.header[i]
1203 i = find_token(document.header, "\\biblatex_citestyle", 0)
1205 citestyle = get_value(document.header, "\\biblatex_citestyle", i)
1206 del document.header[i]
1209 i = find_token(document.header, "\\biblio_options", 0)
1211 biblio_options = get_value(document.header, "\\biblio_options", i)
1212 del document.header[i]
1215 bbxopts = "[natbib=true"
1217 bbxopts += ",bibstyle=" + bibstyle
1219 bbxopts += ",citestyle=" + citestyle
1220 if biblio_options != "":
1221 bbxopts += "," + biblio_options
1223 add_to_preamble(document, "\\usepackage" + bbxopts + "{biblatex}")
1233 i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
1236 j = find_end_of_inset(document.body, i)
1238 document.warning("Can't find end of bibtex inset at line %d!!" %(i))
1241 bibs = get_quoted_value(document.body, "bibfiles", i, j)
1242 opts = get_quoted_value(document.body, "biblatexopts", i, j)
1245 bibresources += bibs.split(",")
1247 document.warning("Can't find bibfiles for bibtex inset at line %d!" %(i))
1248 # remove biblatexopts line
1249 k = find_token(document.body, "biblatexopts", i, j)
1251 del document.body[k]
1252 # Re-find inset end line
1253 j = find_end_of_inset(document.body, i)
1254 # Insert ERT \\printbibliography and wrap bibtex inset to a Note
1256 pcmd = "printbibliography"
1258 pcmd += "[" + opts + "]"
1259 repl = ["\\begin_inset ERT", "status open", "", "\\begin_layout Plain Layout",\
1260 "", "", "\\backslash", pcmd, "\\end_layout", "", "\\end_inset", "", "",\
1261 "\\end_layout", "", "\\begin_layout Standard", "\\begin_inset Note Note",\
1262 "status open", "", "\\begin_layout Plain Layout" ]
1263 repl += document.body[i:j+1]
1264 repl += ["", "\\end_layout", "", "\\end_inset", "", ""]
1265 document.body[i:j+1] = repl
1271 for b in bibresources:
1272 add_to_preamble(document, "\\addbibresource{" + b + ".bib}")
1274 # 2. Citation insets
1276 # Specific citation insets used in biblatex that need to be reverted to ERT
1279 "citebyear" : "citeyear",
1280 "citeyear" : "cite*",
1281 "Footcite" : "Smartcite",
1282 "footcite" : "smartcite",
1283 "Autocite" : "Autocite",
1284 "autocite" : "autocite",
1285 "citetitle" : "citetitle",
1286 "citetitle*" : "citetitle*",
1287 "fullcite" : "fullcite",
1288 "footfullcite" : "footfullcite",
1289 "supercite" : "supercite",
1290 "citeauthor" : "citeauthor",
1291 "citeauthor*" : "citeauthor*",
1292 "Citeauthor" : "Citeauthor",
1293 "Citeauthor*" : "Citeauthor*"
1296 # All commands accepted by LyX < 2.3. Everything else throws an error.
1297 old_citations = [ "cite", "nocite", "citet", "citep", "citealt", "citealp",\
1298 "citeauthor", "citeyear", "citeyearpar", "citet*", "citep*",\
1299 "citealt*", "citealp*", "citeauthor*", "Citet", "Citep",\
1300 "Citealt", "Citealp", "Citeauthor", "Citet*", "Citep*",\
1301 "Citealt*", "Citealp*", "Citeauthor*", "fullcite", "footcite",\
1302 "footcitet", "footcitep", "footcitealt", "footcitealp",\
1303 "footciteauthor", "footciteyear", "footciteyearpar",\
1304 "citefield", "citetitle", "cite*" ]
1308 i = find_token(document.body, "\\begin_inset CommandInset citation", i)
1311 j = find_end_of_inset(document.body, i)
1313 document.warning("Can't find end of citation inset at line %d!!" %(i))
1316 k = find_token(document.body, "LatexCommand", i, j)
1318 document.warning("Can't find LatexCommand for citation inset at line %d!" %(i))
1321 cmd = get_value(document.body, "LatexCommand", k)
1322 if biblatex and cmd in list(new_citations.keys()):
1323 pre = get_quoted_value(document.body, "before", i, j)
1324 post = get_quoted_value(document.body, "after", i, j)
1325 key = get_quoted_value(document.body, "key", i, j)
1327 document.warning("Citation inset at line %d does not have a key!" %(i))
1329 # Replace known new commands with ERT
1330 res = "\\" + new_citations[cmd]
1332 res += "[" + pre + "]"
1334 res += "[" + post + "]"
1337 res += "{" + key + "}"
1338 document.body[i:j+1] = put_cmd_in_ert([res])
1339 elif cmd not in old_citations:
1340 # Reset unknown commands to cite. This is what LyX does as well
1341 # (but LyX 2.2 would break on unknown commands)
1342 document.body[k] = "LatexCommand cite"
1343 document.warning("Reset unknown cite command '%s' with cite" % cmd)
1346 # Emulate the old biblatex-workaround (pretend natbib in order to use the styles)
1348 i = find_token(document.header, "\\begin_local_layout", 0)
1350 k = find_token(document.header, "\\language", 0)
1352 # this should not happen
1353 document.warning("Malformed LyX document! No \\language header found!")
1355 document.header[k-1 : k-1] = ["\\begin_local_layout", "\\end_local_layout"]
1358 j = find_end_of(document.header, i, "\\begin_local_layout", "\\end_local_layout")
1360 # this should not happen
1361 document.warning("Malformed LyX document! Can't find end of local layout!")
1364 document.header[i+1 : i+1] = [
1365 "### Inserted by lyx2lyx (biblatex emulation) ###",
1366 "Provides natbib 1",
1367 "### End of insertion by lyx2lyx (biblatex emulation) ###"
1371 def revert_citekeyonly(document):
1372 " Revert keyonly cite command to ERT "
1376 i = find_token(document.body, "\\begin_inset CommandInset citation", i)
1379 j = find_end_of_inset(document.body, i)
1381 document.warning("Can't find end of citation inset at line %d!!" %(i))
1384 k = find_token(document.body, "LatexCommand", i, j)
1386 document.warning("Can't find LatexCommand for citation inset at line %d!" %(i))
1389 cmd = get_value(document.body, "LatexCommand", k)
1390 if cmd != "keyonly":
1394 key = get_quoted_value(document.body, "key", i, j)
1396 document.warning("Citation inset at line %d does not have a key!" %(i))
1397 # Replace known new commands with ERT
1398 document.body[i:j+1] = put_cmd_in_ert([key])
1403 def revert_bibpackopts(document):
1404 " Revert support for natbib/jurabib package options "
1407 i = find_token(document.header, "\\cite_engine", 0)
1409 document.warning("Malformed document! Missing \\cite_engine")
1411 engine = get_value(document.header, "\\cite_engine", i)
1414 if engine not in ["natbib", "jurabib"]:
1417 i = find_token(document.header, "\\biblio_options", 0)
1419 # Nothing to do if we have no options
1422 biblio_options = get_value(document.header, "\\biblio_options", i)
1423 del document.header[i]
1425 if not biblio_options:
1426 # Nothing to do for empty options
1429 i = find_token(document.header, "\\begin_local_layout", 0)
1431 k = find_token(document.header, "\\language", 0)
1433 # this should not happen
1434 document.warning("Malformed LyX document! No \\language header found!")
1436 document.header[k-1 : k-1] = ["\\begin_local_layout", "\\end_local_layout"]
1439 j = find_end_of(document.header, i, "\\begin_local_layout", "\\end_local_layout")
1441 # this should not happen
1442 document.warning("Malformed LyX document! Can't find end of local layout!")
1445 document.header[i+1 : i+1] = [
1446 "### Inserted by lyx2lyx (bibliography package options) ###",
1447 "PackageOptions " + engine + " " + biblio_options,
1448 "### End of insertion by lyx2lyx (bibliography package options) ###"
1452 def revert_qualicites(document):
1453 " Revert qualified citation list commands to ERT "
1455 # Citation insets that support qualified lists, with their LaTeX code
1459 "citet" : "textcites",
1460 "Citet" : "Textcites",
1461 "citep" : "parencites",
1462 "Citep" : "Parencites",
1463 "Footcite" : "Smartcites",
1464 "footcite" : "smartcites",
1465 "Autocite" : "Autocites",
1466 "autocite" : "autocites",
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 biblatex = engine in ["biblatex", "biblatex-natbib"]
1481 i = find_token(document.body, "\\begin_inset CommandInset citation", i)
1484 j = find_end_of_inset(document.body, i)
1486 document.warning("Can't find end of citation inset at line %d!!" %(i))
1489 pres = find_token(document.body, "pretextlist", i, j)
1490 posts = find_token(document.body, "posttextlist", i, j)
1491 if pres == -1 and posts == -1:
1495 pretexts = get_quoted_value(document.body, "pretextlist", pres)
1496 posttexts = get_quoted_value(document.body, "posttextlist", posts)
1497 k = find_token(document.body, "LatexCommand", i, j)
1499 document.warning("Can't find LatexCommand for citation inset at line %d!" %(i))
1502 cmd = get_value(document.body, "LatexCommand", k)
1503 if biblatex and cmd in list(ql_citations.keys()):
1504 pre = get_quoted_value(document.body, "before", i, j)
1505 post = get_quoted_value(document.body, "after", i, j)
1506 key = get_quoted_value(document.body, "key", i, j)
1508 document.warning("Citation inset at line %d does not have a key!" %(i))
1510 keys = key.split(",")
1511 prelist = pretexts.split("\t")
1514 ppp = pp.split(" ", 1)
1515 premap[ppp[0]] = ppp[1]
1516 postlist = posttexts.split("\t")
1519 ppp = pp.split(" ", 1)
1520 postmap[ppp[0]] = ppp[1]
1521 # Replace known new commands with ERT
1522 if "(" in pre or ")" in pre:
1523 pre = "{" + pre + "}"
1524 if "(" in post or ")" in post:
1525 post = "{" + post + "}"
1526 res = "\\" + ql_citations[cmd]
1528 res += "(" + pre + ")"
1530 res += "(" + post + ")"
1534 if premap.get(kk, "") != "":
1535 res += "[" + premap[kk] + "]"
1536 if postmap.get(kk, "") != "":
1537 res += "[" + postmap[kk] + "]"
1538 elif premap.get(kk, "") != "":
1540 res += "{" + kk + "}"
1541 document.body[i:j+1] = put_cmd_in_ert([res])
1543 # just remove the params
1544 del document.body[posttexts]
1545 del document.body[pretexts]
1549 command_insets = ["bibitem", "citation", "href", "index_print", "nomenclature"]
1550 def convert_literalparam(document):
1551 " Add param literal "
1553 for inset in command_insets:
1556 i = find_token(document.body, '\\begin_inset CommandInset %s' % inset, i)
1559 j = find_end_of_inset(document.body, i)
1561 document.warning("Malformed LyX document: Can't find end of %s inset at line %d" % (inset, i))
1564 while i < j and document.body[i].strip() != '':
1566 # href is already fully latexified. Here we can switch off literal.
1568 document.body.insert(i, "literal \"false\"")
1570 document.body.insert(i, "literal \"true\"")
1574 def revert_literalparam(document):
1575 " Remove param literal "
1577 for inset in command_insets:
1580 i = find_token(document.body, '\\begin_inset CommandInset %s' % inset, i)
1583 j = find_end_of_inset(document.body, i)
1585 document.warning("Malformed LyX document: Can't find end of %s inset at line %d" % (inset, i))
1588 k = find_token(document.body, 'literal', i, j)
1592 del document.body[k]
1596 def revert_multibib(document):
1597 " Revert multibib support "
1599 # 1. Get cite engine
1601 i = find_token(document.header, "\\cite_engine", 0)
1603 document.warning("Malformed document! Missing \\cite_engine")
1605 engine = get_value(document.header, "\\cite_engine", i)
1607 # 2. Do we use biblatex?
1609 if engine in ["biblatex", "biblatex-natbib"]:
1612 # 3. Store and remove multibib document header
1614 i = find_token(document.header, "\\multibib", 0)
1616 multibib = get_value(document.header, "\\multibib", i)
1617 del document.header[i]
1622 # 4. The easy part: Biblatex
1624 i = find_token(document.header, "\\biblio_options", 0)
1626 k = find_token(document.header, "\\use_bibtopic", 0)
1628 # this should not happen
1629 document.warning("Malformed LyX document! No \\use_bibtopic header found!")
1631 document.header[k-1 : k-1] = ["\\biblio_options " + "refsection=" + multibib]
1633 biblio_options = get_value(document.header, "\\biblio_options", i)
1635 biblio_options += ","
1636 biblio_options += "refsection=" + multibib
1637 document.header[i] = "\\biblio_options " + biblio_options
1642 i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
1645 j = find_end_of_inset(document.body, i)
1647 document.warning("Can't find end of bibtex inset at line %d!!" %(i))
1650 btprint = get_quoted_value(document.body, "btprint", i, j)
1651 if btprint != "bibbysection":
1654 opts = get_quoted_value(document.body, "biblatexopts", i, j)
1655 # change btprint line
1656 k = find_token(document.body, "btprint", i, j)
1658 document.body[k] = "btprint \"btPrintCited\""
1659 # Insert ERT \\bibbysection and wrap bibtex inset to a Note
1660 pcmd = "bibbysection"
1662 pcmd += "[" + opts + "]"
1663 repl = ["\\begin_inset ERT", "status open", "", "\\begin_layout Plain Layout",\
1664 "", "", "\\backslash", pcmd, "\\end_layout", "", "\\end_inset", "", "",\
1665 "\\end_layout", "", "\\begin_layout Standard", "\\begin_inset Note Note",\
1666 "status open", "", "\\begin_layout Plain Layout" ]
1667 repl += document.body[i:j+1]
1668 repl += ["", "\\end_layout", "", "\\end_inset", "", ""]
1669 document.body[i:j+1] = repl
1675 # 5. More tricky: Bibtex/Bibtopic
1676 k = find_token(document.header, "\\use_bibtopic", 0)
1678 # this should not happen
1679 document.warning("Malformed LyX document! No \\use_bibtopic header found!")
1681 document.header[k] = "\\use_bibtopic true"
1683 # Possible units. This assumes that the LyX name follows the std,
1684 # which might not always be the case. But it's as good as we can get.
1687 "chapter" : "Chapter",
1688 "section" : "Section",
1689 "subsection" : "Subsection",
1692 if multibib not in units.keys():
1693 document.warning("Unknown multibib value `%s'!" % nultibib)
1695 unit = units[multibib]
1699 i = find_token(document.body, "\\begin_layout " + unit, i)
1703 document.body[i-1 : i-1] = ["\\begin_layout Standard",
1704 "\\begin_inset ERT", "status open", "",
1705 "\\begin_layout Plain Layout", "", "",
1707 "end{btUnit}", "\\end_layout",
1708 "\\begin_layout Plain Layout", "",
1711 "\\end_layout", "", "\\end_inset", "", "",
1715 document.body[i-1 : i-1] = ["\\begin_layout Standard",
1716 "\\begin_inset ERT", "status open", "",
1717 "\\begin_layout Plain Layout", "", "",
1720 "\\end_layout", "", "\\end_inset", "", "",
1727 i = find_token(document.body, "\\end_body", i)
1728 document.body[i-1 : i-1] = ["\\begin_layout Standard",
1729 "\\begin_inset ERT", "status open", "",
1730 "\\begin_layout Plain Layout", "", "",
1733 "\\end_layout", "", "\\end_inset", "", "",
1737 def revert_chapterbib(document):
1738 " Revert chapterbib support "
1740 # 1. Get cite engine
1742 i = find_token(document.header, "\\cite_engine", 0)
1744 document.warning("Malformed document! Missing \\cite_engine")
1746 engine = get_value(document.header, "\\cite_engine", i)
1748 # 2. Do we use biblatex?
1750 if engine in ["biblatex", "biblatex-natbib"]:
1753 # 3. Store multibib document header value
1755 i = find_token(document.header, "\\multibib", 0)
1757 multibib = get_value(document.header, "\\multibib", i)
1759 if not multibib or multibib != "child":
1763 # 4. remove multibib header
1764 del document.header[i]
1768 # find include insets
1771 i = find_token(document.body, "\\begin_inset CommandInset include", i)
1774 j = find_end_of_inset(document.body, i)
1776 document.warning("Can't find end of bibtex inset at line %d!!" %(i))
1779 parent = get_containing_layout(document.body, i)
1782 # Insert ERT \\newrefsection before inset
1783 beg = ["\\begin_layout Standard",
1784 "\\begin_inset ERT", "status open", "",
1785 "\\begin_layout Plain Layout", "", "",
1788 "\\end_layout", "", "\\end_inset", "", "",
1790 document.body[parbeg-1:parbeg-1] = beg
1795 # 6. Bibtex/Bibtopic
1796 i = find_token(document.header, "\\use_bibtopic", 0)
1798 # this should not happen
1799 document.warning("Malformed LyX document! No \\use_bibtopic header found!")
1801 if get_value(document.header, "\\use_bibtopic", i) == "true":
1802 # find include insets
1805 i = find_token(document.body, "\\begin_inset CommandInset include", i)
1808 j = find_end_of_inset(document.body, i)
1810 document.warning("Can't find end of bibtex inset at line %d!!" %(i))
1813 parent = get_containing_layout(document.body, i)
1817 # Insert wrap inset into \\begin{btUnit}...\\end{btUnit}
1818 beg = ["\\begin_layout Standard",
1819 "\\begin_inset ERT", "status open", "",
1820 "\\begin_layout Plain Layout", "", "",
1823 "\\end_layout", "", "\\end_inset", "", "",
1825 end = ["\\begin_layout Standard",
1826 "\\begin_inset ERT", "status open", "",
1827 "\\begin_layout Plain Layout", "", "",
1830 "\\end_layout", "", "\\end_inset", "", "",
1832 document.body[parend+1:parend+1] = end
1833 document.body[parbeg-1:parbeg-1] = beg
1834 j += len(beg) + len(end)
1838 # 7. Chapterbib proper
1839 add_to_preamble(document, ["\\usepackage{chapterbib}"])
1842 def convert_dashligatures(document):
1843 "Set 'use_dash_ligatures' according to content."
1844 # Look for and remove dashligatures workaround from 2.3->2.2 reversion,
1845 # set use_dash_ligatures to True if found, to None else.
1846 use_dash_ligatures = del_complete_lines(document.preamble,
1847 ['% Added by lyx2lyx',
1848 r'\renewcommand{\textendash}{--}',
1849 r'\renewcommand{\textemdash}{---}']) or None
1851 if use_dash_ligatures is None:
1852 # Look for dashes (Documents by LyX 2.1 or older have "\twohyphens\n"
1853 # or "\threehyphens\n" as interim representation for -- an ---.)
1854 lines = document.body
1855 has_literal_dashes = has_ligature_dashes = False
1857 while i+1 < len(lines):
1860 # skip lines without dashes:
1861 if not re.search(u"[\u2013\u2014]|\\twohyphens|\\threehyphens", line):
1863 # skip label width string (see bug 10243):
1864 if line.startswith("\\labelwidthstring"):
1866 # do not touch hyphens in some insets (cf. lyx_2_2.convert_dashes):
1868 inset_type, start, end = get_containing_inset(lines, i)
1869 except TypeError: # no containing inset
1870 inset_type, start, end = "no inset", -1, -1
1871 if (inset_type.split()[0] in
1872 ["CommandInset", "ERT", "External", "Formula",
1873 "FormulaMacro", "Graphics", "IPA", "listings"]
1874 or inset_type == "Flex Code"):
1878 layoutname, start, end, j = get_containing_layout(lines, i)
1879 except TypeError: # no (or malformed) containing layout
1880 document.warning("Malformed LyX document: "
1881 "Can't find layout at line %d" % i)
1883 if layoutname == "LyX-Code":
1887 # literal dash followed by a word or no-break space:
1888 if re.search(u"[\u2013\u2014]([\w\u00A0]|$)",
1889 line, flags=re.UNICODE):
1890 has_literal_dashes = True
1891 # ligature dash followed by word or no-break space on next line:
1892 if (re.search(r"(\\twohyphens|\\threehyphens)", line) and
1893 re.match(u"[\w\u00A0]", lines[i+1], flags=re.UNICODE)):
1894 has_ligature_dashes = True
1895 if has_literal_dashes and has_ligature_dashes:
1896 # TODO: insert a warning note in the document?
1897 document.warning('This document contained both literal and '
1898 '"ligature" dashes.\n Line breaks may have changed. '
1899 'See UserGuide chapter 3.9.1 for details.')
1902 if has_literal_dashes and not has_ligature_dashes:
1903 use_dash_ligatures = False
1904 elif has_ligature_dashes and not has_literal_dashes:
1905 use_dash_ligatures = True
1907 # insert the setting if there is a preferred value
1908 if use_dash_ligatures is not None:
1909 document.header.insert(-1, "\\use_dash_ligatures %s"
1910 % str(use_dash_ligatures).lower())
1913 def revert_dashligatures(document):
1914 """Remove font ligature settings for en- and em-dashes.
1915 Revert conversion of \twodashes or \threedashes to literal dashes."""
1916 use_dash_ligatures = del_value(document.header, "\\use_dash_ligatures")
1917 if use_dash_ligatures != "true" or document.backend != "latex":
1921 for i, line in enumerate(document.body):
1922 # Skip some document parts where dashes are not converted
1923 if (i < j) or line.startswith("\\labelwidthstring"):
1924 new_body.append(line)
1926 if (line.startswith("\\begin_inset ") and
1927 line[13:].split()[0] in ["CommandInset", "ERT", "External",
1928 "Formula", "FormulaMacro", "Graphics", "IPA", "listings"]
1929 or line == "\\begin_inset Flex Code"):
1930 j = find_end_of_inset(document.body, i)
1932 document.warning("Malformed LyX document: Can't find end of "
1933 + words[1] + " inset at line " + str(i))
1934 new_body.append(line)
1936 if line == "\\begin_layout LyX-Code":
1937 j = find_end_of_layout(document.body, i)
1939 document.warning("Malformed LyX document: "
1940 "Can't find end of %s layout at line %d" % (words[1],i))
1941 new_body.append(line)
1943 # TODO: skip replacement in typewriter fonts
1944 line = line.replace(u'\u2013', '\\twohyphens\n')
1945 line = line.replace(u'\u2014', '\\threehyphens\n')
1946 lines = line.split('\n')
1947 new_body.extend(line.split('\n'))
1948 document.body = new_body
1949 # redefine the dash LICRs to use ligature dashes:
1950 add_to_preamble(document, [r'\renewcommand{\textendash}{--}',
1951 r'\renewcommand{\textemdash}{---}'])
1954 def revert_noto(document):
1955 " Revert Noto font definitions to LaTeX "
1957 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
1959 i = find_token(document.header, "\\font_roman \"NotoSerif-TLF\"", 0)
1961 add_to_preamble(document, ["\\renewcommand{\\rmdefault}{NotoSerif-TLF}"])
1962 document.header[i] = document.header[i].replace("NotoSerif-TLF", "default")
1963 i = find_token(document.header, "\\font_sans \"NotoSans-TLF\"", 0)
1965 add_to_preamble(document, ["\\renewcommand{\\sfdefault}{NotoSans-TLF}"])
1966 document.header[i] = document.header[i].replace("NotoSans-TLF", "default")
1967 i = find_token(document.header, "\\font_typewriter \"NotoMono-TLF\"", 0)
1969 add_to_preamble(document, ["\\renewcommand{\\ttdefault}{NotoMono-TLF}"])
1970 document.header[i] = document.header[i].replace("NotoMono-TLF", "default")
1973 def revert_xout(document):
1974 " Reverts \\xout font attribute "
1975 changed = revert_font_attrs(document.body, "\\xout", "\\xout")
1977 insert_to_preamble(document, \
1978 ['% for proper cross-out',
1979 '\\PassOptionsToPackage{normalem}{ulem}',
1980 '\\usepackage{ulem}'])
1983 def convert_mathindent(document):
1984 """Add the \\is_math_indent tag.
1986 k = find_token(document.header, "\\quotes_style") # where to insert
1987 # check if the document uses the class option "fleqn"
1988 options = get_value(document.header, "\\options")
1989 if 'fleqn' in options:
1990 document.header.insert(k, "\\is_math_indent 1")
1991 # delete the fleqn option
1992 i = find_token(document.header, "\\options")
1993 options = [option for option in options.split(",")
1994 if option.strip() != "fleqn"]
1996 document.header[i] = "\\options " + ",".join(options)
1998 del document.header[i]
2000 document.header.insert(k, "\\is_math_indent 0")
2002 def revert_mathindent(document):
2003 " Define mathindent if set in the document "
2004 # emulate and delete \math_indentation
2005 value = get_value(document.header, "\\math_indentation",
2006 default="default", delete=True)
2007 if value != "default":
2008 add_to_preamble(document, [r"\setlength{\mathindent}{%s}"%value])
2009 # delete \is_math_indent and emulate via document class option
2010 if not get_bool_value(document.header, "\\is_math_indent", delete=True):
2012 i = find_token(document.header, "\\options")
2014 document.header[i] = document.header[i].replace("\\options ",
2017 l = find_token(document.header, "\\use_default_options")
2018 document.header.insert(l, "\\options fleqn")
2021 def revert_baselineskip(document):
2022 " Revert baselineskips to TeX code "
2024 regexp = re.compile(r'.*baselineskip%.*')
2027 i = find_re(document.body, regexp, i)
2030 if document.body[i].startswith("\\begin_inset VSpace"):
2031 # output VSpace inset as TeX code
2032 end = find_end_of_inset(document.body, i)
2034 document.warning("Malformed LyX document: "
2035 "Can't find end of VSpace inset at line %d." % i)
2037 # read out the value
2038 baselineskip = document.body[i].split()[-1]
2039 # check if it is the starred version
2040 star = '*' if '*' in document.body[i] else ''
2041 # now output TeX code
2042 cmd = "\\vspace%s{%s}" %(star, latex_length(baselineskip)[1])
2043 document.body[i:end+1] = put_cmd_in_ert(cmd)
2046 begin, end = is_in_inset(document.body, i, "\\begin_inset space \\hspace")
2048 # output space inset as TeX code
2049 baselineskip = document.body[i].split()[-1]
2050 star = '*' if '*' in document.body[i-1] else ''
2051 cmd = "\\hspace%s{%s}" %(star, latex_length(baselineskip)[1])
2052 document.body[begin:end+1] = put_cmd_in_ert(cmd)
2055 def revert_rotfloat(document):
2056 " Revert placement options for rotated floats "
2061 i = find_token(document.body, "sideways true", i)
2063 regexp = re.compile(r'^.*placement.*$')
2064 j = find_re(document.body, regexp, i-2)
2072 # we found a sideways float with placement options
2073 # at first store the placement
2074 beg = document.body[i-2].rfind(" ");
2075 placement = document.body[i-2][beg+1:]
2076 # check if the option'H' is used
2077 if placement.find("H") != -1:
2078 add_to_preamble(document, ["\\usepackage{float}"])
2079 # now check if it is a starred type
2080 if document.body[i-1].find("wide true") != -1:
2084 # store the float type
2085 beg = document.body[i-3].rfind(" ");
2086 fType = document.body[i-3][beg+1:]
2087 # now output TeX code
2088 endInset = find_end_of_inset(document.body, i-3)
2090 document.warning("Malformed LyX document: Missing '\\end_inset' of Float inset.")
2093 document.body[endInset-2: endInset+1] = put_cmd_in_ert("\\end{sideways" + fType + star + '}')
2094 document.body[i-3: i+2] = put_cmd_in_ert("\\begin{sideways" + fType + star + "}[" + placement + ']')
2095 add_to_preamble(document, ["\\usepackage{rotfloat}"])
2100 allowbreak_emulation = [r"\begin_inset space \hspace{}",
2105 def convert_allowbreak(document):
2106 " Zero widths Space-inset -> \SpecialChar allowbreak. "
2107 lines = document.body
2108 i = find_complete_lines(lines, allowbreak_emulation, 2)
2110 lines[i-1:i+4] = [lines[i-1] + r"\SpecialChar allowbreak"]
2111 i = find_complete_lines(lines, allowbreak_emulation, i)
2114 def revert_allowbreak(document):
2115 " \SpecialChar allowbreak -> Zero widths Space-inset. "
2117 lines = document.body
2118 while i < len(lines):
2119 if lines[i].endswith(r"\SpecialChar allowbreak"):
2120 lines[i:i+1] = [lines[i].replace(r"\SpecialChar allowbreak", "")
2121 ] + allowbreak_emulation
2127 def convert_mathnumberpos(document):
2128 " add the \\math_number_before tag "
2129 # check if the document uses the class option "leqno"
2130 k = find_token(document.header, "\\quotes_style", 0)
2131 m = find_token(document.header, "\\options", 0)
2132 regexp = re.compile(r'^.*leqno.*')
2133 i = find_re(document.header, regexp, 0)
2134 if i != -1 and i == m:
2135 document.header.insert(k, "\\math_number_before 1")
2136 # delete the found option
2137 document.header[i] = document.header[i].replace(",leqno", "")
2138 document.header[i] = document.header[i].replace(", leqno", "")
2139 document.header[i] = document.header[i].replace("leqno,", "")
2140 j = find_re(document.header, regexp, 0)
2142 # then we have leqno as the only option
2143 del document.header[i]
2145 document.header.insert(k, "\\math_number_before 0")
2148 def revert_mathnumberpos(document):
2149 " add the document class option leqno"
2150 regexp = re.compile(r'(\\math_number_before 1)')
2151 i = find_re(document.header, regexp, 0)
2153 regexp = re.compile(r'(\\math_number_before)')
2154 j = find_re(document.header, regexp, 0)
2155 del document.header[j]
2157 k = find_token(document.header, "\\options", 0)
2159 document.header[k] = document.header[k].replace("\\options", "\\options leqno,")
2160 del document.header[i]
2162 l = find_token(document.header, "\\use_default_options", 0)
2163 document.header.insert(l, "\\options leqno")
2164 del document.header[i + 1]
2167 def convert_mathnumberingname(document):
2168 " rename the \\math_number_before tag to \\math_numbering_side "
2169 regexp = re.compile(r'(\\math_number_before 1)')
2170 i = find_re(document.header, regexp, 0)
2172 document.header[i] = "\\math_numbering_side left"
2173 regexp = re.compile(r'(\\math_number_before 0)')
2174 i = find_re(document.header, regexp, 0)
2176 document.header[i] = "\\math_numbering_side default"
2177 # check if the document uses the class option "reqno"
2178 k = find_token(document.header, "\\math_numbering_side", 0)
2179 m = find_token(document.header, "\\options", 0)
2180 regexp = re.compile(r'^.*reqno.*')
2181 i = find_re(document.header, regexp, 0)
2182 if i != -1 and i == m:
2183 document.header[k] = "\\math_numbering_side right"
2184 # delete the found option
2185 document.header[i] = document.header[i].replace(",reqno", "")
2186 document.header[i] = document.header[i].replace(", reqno", "")
2187 document.header[i] = document.header[i].replace("reqno,", "")
2188 j = find_re(document.header, regexp, 0)
2190 # then we have reqno as the only option
2191 del document.header[i]
2194 def revert_mathnumberingname(document):
2195 " rename the \\math_numbering_side tag back to \\math_number_before "
2197 regexp = re.compile(r'(\\math_numbering_side left)')
2198 i = find_re(document.header, regexp, 0)
2200 document.header[i] = "\\math_number_before 1"
2201 # add the option reqno and delete the tag
2202 regexp = re.compile(r'(\\math_numbering_side right)')
2203 i = find_re(document.header, regexp, 0)
2205 document.header[i] = "\\math_number_before 0"
2206 k = find_token(document.header, "\\options", 0)
2208 document.header[k] = document.header[k].replace("\\options", "\\options reqno,")
2210 l = find_token(document.header, "\\use_default_options", 0)
2211 document.header.insert(l, "\\options reqno")
2212 # add the math_number_before tag
2213 regexp = re.compile(r'(\\math_numbering_side default)')
2214 i = find_re(document.header, regexp, 0)
2216 document.header[i] = "\\math_number_before 0"
2219 def convert_minted(document):
2220 " add the \\use_minted tag "
2221 i = find_token(document.header, "\\index ")
2222 document.header.insert(i, "\\use_minted 0")
2225 def revert_minted(document):
2226 " remove the \\use_minted tag "
2227 i = find_token(document.header, "\\use_minted", 0)
2229 document.header.pop(i)
2236 supported_versions = ["2.3.0", "2.3"]
2238 [509, [convert_microtype]],
2239 [510, [convert_dateinset]],
2240 [511, [convert_ibranches]],
2241 [512, [convert_beamer_article_styles]],
2245 [516, [convert_inputenc]],
2247 [518, [convert_iopart]],
2248 [519, [convert_quotestyle]],
2250 [521, [convert_frenchquotes]],
2261 [532, [convert_literalparam]],
2264 [535, [convert_dashligatures]],
2267 [538, [convert_mathindent]],
2270 [541, [convert_allowbreak]],
2271 [542, [convert_mathnumberpos]],
2272 [543, [convert_mathnumberingname]],
2273 [544, [convert_minted]]
2277 [543, [revert_minted]],
2278 [542, [revert_mathnumberingname]],
2279 [541, [revert_mathnumberpos]],
2280 [540, [revert_allowbreak]],
2281 [539, [revert_rotfloat]],
2282 [538, [revert_baselineskip]],
2283 [537, [revert_mathindent]],
2284 [536, [revert_xout]],
2285 [535, [revert_noto]],
2286 [534, [revert_dashligatures]],
2287 [533, [revert_chapterbib]],
2288 [532, [revert_multibib]],
2289 [531, [revert_literalparam]],
2290 [530, [revert_qualicites]],
2291 [529, [revert_bibpackopts]],
2292 [528, [revert_citekeyonly]],
2293 [527, [revert_biblatex]],
2294 [526, [revert_noprefix]],
2295 [525, [revert_plural_refs]],
2296 [524, [revert_labelonly]],
2297 [523, [revert_crimson, revert_cochinealmath]],
2298 [522, [revert_cjkquotes]],
2299 [521, [revert_dynamicquotes]],
2300 [520, [revert_britishquotes, revert_swedishgquotes, revert_frenchquotes, revert_frenchinquotes, revert_russianquotes, revert_swissquotes]],
2301 [519, [revert_plainquote]],
2302 [518, [revert_quotestyle]],
2303 [517, [revert_iopart]],
2304 [516, [revert_quotes]],
2306 [514, [revert_urdu, revert_syriac]],
2307 [513, [revert_amharic, revert_asturian, revert_kannada, revert_khmer]],
2308 [512, [revert_bosnian, revert_friulan, revert_macedonian, revert_piedmontese, revert_romansh]],
2309 [511, [revert_beamer_article_styles]],
2310 [510, [revert_ibranches]],
2312 [508, [revert_microtype]]
2316 if __name__ == "__main__":