1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # Copyright (C) 2018 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.4"""
25 # Uncomment only what you need to import, please.
27 from parser_tools import (count_pars_in_inset, find_end_of_inset, find_end_of_layout,
28 find_token, get_bool_value, get_option_value, get_value, get_quoted_value)
29 # del_token, del_value, del_complete_lines,
30 # find_complete_lines, find_end_of,
31 # find_re, find_substring, find_token_backwards,
32 # get_containing_inset, get_containing_layout,
33 # is_in_inset, set_bool_value
34 # find_tokens, find_token_exact, check_token
36 from lyx2lyx_tools import (put_cmd_in_ert, add_to_preamble)
37 # revert_font_attrs, insert_to_preamble, latex_length
38 # get_ert, lyx2latex, lyx2verbatim, length_in_bp, convert_info_insets
39 # revert_flex_inset, hex2ratio, str2bool
41 ####################################################################
42 # Private helper functions
46 ###############################################################################
48 ### Conversion and reversion routines
50 ###############################################################################
52 def removeFrontMatterStyles(document):
53 " Remove styles Begin/EndFromatter"
55 layouts = ['BeginFrontmatter', 'EndFrontmatter']
56 for layout in layouts:
59 i = find_token(document.body, '\\begin_layout ' + layout, i)
62 j = find_end_of_layout(document.body, i)
64 document.warning("Malformed LyX document: Can't find end of layout at line %d" % i)
67 if document.body[j] == '':
69 del document.body[i:j+1]
71 def addFrontMatterStyles(document):
72 " Use styles Begin/EndFrontmatter for elsarticle"
74 def insertFrontmatter(prefix, line):
75 document.body[line:line] = ['\\begin_layout ' + prefix + 'Frontmatter',
76 '\\begin_inset Note Note',
78 '\\begin_layout Plain Layout',
81 '\\end_inset', '', '',
84 if document.textclass == "elsarticle":
85 layouts = ['Title', 'Title footnote', 'Author', 'Author footnote',
86 'Corresponding author', 'Address', 'Email', 'Abstract', 'Keywords']
89 for layout in layouts:
92 i = find_token(document.body, '\\begin_layout ' + layout, i)
95 k = find_end_of_layout(document.body, i)
97 document.warning("Malformed LyX document: Can't find end of layout at line %d" % i)
100 if first == -1 or i < first:
102 if last == -1 or last <= k:
107 if first > 0 and document.body[first-1] == '':
109 if document.body[last] == '':
111 insertFrontmatter('End', last)
112 insertFrontmatter('Begin', first)
114 def convert_lst_literalparam(document):
115 " Add param literal to include inset "
119 i = find_token(document.body, '\\begin_inset CommandInset include', i)
122 j = find_end_of_inset(document.body, i)
124 document.warning("Malformed LyX document: Can't find end of command inset at line %d" % i)
127 while i < j and document.body[i].strip() != '':
129 document.body.insert(i, "literal \"true\"")
132 def revert_lst_literalparam(document):
133 " Remove param literal from include inset "
137 i = find_token(document.body, '\\begin_inset CommandInset include', i)
140 j = find_end_of_inset(document.body, i)
142 document.warning("Malformed LyX document: Can't find end of include inset at line %d" % i)
145 k = find_token(document.body, 'literal', i, j)
152 def revert_paratype(document):
153 " Revert ParaType font definitions to LaTeX "
155 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
157 i1 = find_token(document.header, "\\font_roman \"PTSerif-TLF\"", 0)
158 i2 = find_token(document.header, "\\font_sans \"default\"", 0)
159 i3 = find_token(document.header, "\\font_typewriter \"default\"", 0)
160 j = find_token(document.header, "\\font_sans \"PTSans-TLF\"", 0)
161 sfval = get_value(document.header, "\\font_sf_scale", 0)
166 sfoption = "scaled=" + format(float(sfval) / 100, '.2f')
167 k = find_token(document.header, "\\font_typewriter \"PTMono-TLF\"", 0)
168 ttval = get_value(document.header, "\\font_tt_scale", 0)
173 ttoption = "scaled=" + format(float(ttval) / 100, '.2f')
174 if i1 != -1 and i2 != -1 and i3!= -1:
175 add_to_preamble(document, ["\\usepackage{paratype}"])
178 add_to_preamble(document, ["\\usepackage{PTSerif}"])
179 document.header[i1] = document.header[i1].replace("PTSerif-TLF", "default")
182 add_to_preamble(document, ["\\usepackage[" + sfoption + "]{PTSans}"])
184 add_to_preamble(document, ["\\usepackage{PTSans}"])
185 document.header[j] = document.header[j].replace("PTSans-TLF", "default")
188 add_to_preamble(document, ["\\usepackage[" + ttoption + "]{PTMono}"])
190 add_to_preamble(document, ["\\usepackage{PTMono}"])
191 document.header[k] = document.header[k].replace("PTMono-TLF", "default")
194 def revert_xcharter(document):
195 " Revert XCharter font definitions to LaTeX "
197 i = find_token(document.header, "\\font_roman \"xcharter\"", 0)
201 # replace unsupported font setting
202 document.header[i] = document.header[i].replace("xcharter", "default")
203 # no need for preamble code with system fonts
204 if get_bool_value(document.header, "\\use_non_tex_fonts"):
207 # transfer old style figures setting to package options
208 j = find_token(document.header, "\\font_osf true")
211 document.header[j] = "\\font_osf false"
215 add_to_preamble(document, ["\\usepackage%s{XCharter}"%options])
218 def revert_lscape(document):
219 " Reverts the landscape environment (Landscape module) to TeX-code "
221 if not "landscape" in document.get_module_list():
226 i = find_token(document.body, "\\begin_inset Flex Landscape", i)
229 j = find_end_of_inset(document.body, i)
231 document.warning("Malformed LyX document: Can't find end of Landscape inset")
235 if document.body[i] == "\\begin_inset Flex Landscape (Floating)":
236 document.body[j - 2 : j + 1] = put_cmd_in_ert("\\end{landscape}}")
237 document.body[i : i + 4] = put_cmd_in_ert("\\afterpage{\\begin{landscape}")
238 add_to_preamble(document, ["\\usepackage{afterpage}"])
240 document.body[j - 2 : j + 1] = put_cmd_in_ert("\\end{landscape}")
241 document.body[i : i + 4] = put_cmd_in_ert("\\begin{landscape}")
243 add_to_preamble(document, ["\\usepackage{pdflscape}"])
247 def convert_fontenc(document):
248 " Convert default fontenc setting "
250 i = find_token(document.header, "\\fontencoding global", 0)
254 document.header[i] = document.header[i].replace("global", "auto")
257 def revert_fontenc(document):
258 " Revert default fontenc setting "
260 i = find_token(document.header, "\\fontencoding auto", 0)
264 document.header[i] = document.header[i].replace("auto", "global")
267 def revert_nospellcheck(document):
268 " Remove nospellcheck font info param "
272 i = find_token(document.body, '\\nospellcheck', i)
278 def revert_floatpclass(document):
279 " Remove float placement params 'document' and 'class' "
282 i = find_token(document.header, "\\float_placement class", 0)
284 del document.header[i]
288 i = find_token(document.body, '\\begin_inset Float', i)
291 j = find_end_of_inset(document.body, i)
292 k = find_token(document.body, 'placement class', i, i + 2)
294 k = find_token(document.body, 'placement document', i, i + 2)
302 def revert_floatalignment(document):
303 " Remove float alignment params "
306 i = find_token(document.header, "\\float_alignment", 0)
309 galignment = get_value(document.header, "\\float_alignment", i)
310 del document.header[i]
314 i = find_token(document.body, '\\begin_inset Float', i)
317 j = find_end_of_inset(document.body, i)
319 document.warning("Malformed LyX document: Can't find end of inset at line " + str(i))
321 k = find_token(document.body, 'alignment', i, i + 4)
325 alignment = get_value(document.body, "alignment", k)
326 if alignment == "document":
327 alignment = galignment
329 l = find_token(document.body, "\\begin_layout Plain Layout", i, j)
331 document.warning("Can't find float layout!")
335 if alignment == "left":
336 alcmd = put_cmd_in_ert("\\raggedright{}")
337 elif alignment == "center":
338 alcmd = put_cmd_in_ert("\\centering{}")
339 elif alignment == "right":
340 alcmd = put_cmd_in_ert("\\raggedleft{}")
342 document.body[l+1:l+1] = alcmd
346 def revert_tuftecite(document):
347 " Revert \cite commands in tufte classes "
349 tufte = ["tufte-book", "tufte-handout"]
350 if document.textclass not in tufte:
355 i = find_token(document.body, "\\begin_inset CommandInset citation", i)
358 j = find_end_of_inset(document.body, i)
360 document.warning("Can't find end of citation inset at line %d!!" %(i))
363 k = find_token(document.body, "LatexCommand", i, j)
365 document.warning("Can't find LatexCommand for citation inset at line %d!" %(i))
368 cmd = get_value(document.body, "LatexCommand", k)
372 pre = get_quoted_value(document.body, "before", i, j)
373 post = get_quoted_value(document.body, "after", i, j)
374 key = get_quoted_value(document.body, "key", i, j)
376 document.warning("Citation inset at line %d does not have a key!" %(i))
378 # Replace command with ERT
381 res += "[" + pre + "]"
383 res += "[" + post + "]"
386 res += "{" + key + "}"
387 document.body[i:j+1] = put_cmd_in_ert([res])
391 def revert_stretchcolumn(document):
392 " We remove the column varwidth flags or everything else will become a mess. "
395 i = find_token(document.body, "\\begin_inset Tabular", i)
398 j = find_end_of_inset(document.body, i + 1)
400 document.warning("Malformed LyX document: Could not find end of tabular.")
402 for k in range(i, j):
403 if re.search('^<column.*varwidth="[^"]+".*>$', document.body[k]):
404 document.warning("Converting 'tabularx'/'xltabular' table to normal table.")
405 document.body[k] = document.body[k].replace(' varwidth="true"', '')
409 def revert_vcolumns(document):
410 " Revert standard columns with line breaks etc. "
416 i = find_token(document.body, "\\begin_inset Tabular", i)
419 j = find_end_of_inset(document.body, i)
421 document.warning("Malformed LyX document: Could not find end of tabular.")
425 # Collect necessary column information
427 nrows = int(document.body[i+1].split('"')[3])
428 ncols = int(document.body[i+1].split('"')[5])
430 for k in range(ncols):
431 m = find_token(document.body, "<column", m)
432 width = get_option_value(document.body[m], 'width')
433 varwidth = get_option_value(document.body[m], 'varwidth')
434 alignment = get_option_value(document.body[m], 'alignment')
435 special = get_option_value(document.body[m], 'special')
436 col_info.append([width, varwidth, alignment, special, m])
441 for row in range(nrows):
442 for col in range(ncols):
443 m = find_token(document.body, "<cell", m)
444 multicolumn = get_option_value(document.body[m], 'multicolumn')
445 multirow = get_option_value(document.body[m], 'multirow')
446 width = get_option_value(document.body[m], 'width')
447 rotate = get_option_value(document.body[m], 'rotate')
448 # Check for: linebreaks, multipars, non-standard environments
450 endcell = find_token(document.body, "</cell>", begcell)
452 if find_token(document.body, "\\begin_inset Newline", begcell, endcell) != -1:
454 elif count_pars_in_inset(document.body, begcell + 2) > 1:
456 elif get_value(document.body, "\\begin_layout", begcell) != "Plain Layout":
458 if vcand and rotate == "" and ((multicolumn == "" and multirow == "") or width == ""):
459 if col_info[col][0] == "" and col_info[col][1] == "" and col_info[col][3] == "":
461 alignment = col_info[col][2]
462 col_line = col_info[col][4]
464 if alignment == "center":
465 vval = ">{\\centering}"
466 elif alignment == "left":
467 vval = ">{\\raggedright}"
468 elif alignment == "right":
469 vval = ">{\\raggedleft}"
472 vval += "V{\\linewidth}"
474 document.body[col_line] = document.body[col_line][:-1] + " special=\"" + vval + "\">"
475 # ERT newlines and linebreaks (since LyX < 2.4 automatically inserts parboxes
476 # with newlines, and we do not want that)
478 endcell = find_token(document.body, "</cell>", begcell)
480 nl = find_token(document.body, "\\begin_inset Newline newline", begcell, endcell)
482 nl = find_token(document.body, "\\begin_inset Newline linebreak", begcell, endcell)
486 nle = find_end_of_inset(document.body, nl)
487 del(document.body[nle:nle+1])
489 document.body[nl:nl+1] = put_cmd_in_ert("\\linebreak{}")
491 document.body[nl:nl+1] = put_cmd_in_ert("\\\\")
497 if needarray == True:
498 add_to_preamble(document, ["\\usepackage{array}"])
499 if needvarwidth == True:
500 add_to_preamble(document, ["\\usepackage{varwidth}"])
503 def revert_bibencoding(document):
504 " Revert bibliography encoding "
508 i = find_token(document.header, "\\cite_engine", 0)
510 document.warning("Malformed document! Missing \\cite_engine")
512 engine = get_value(document.header, "\\cite_engine", i)
516 if engine in ["biblatex", "biblatex-natbib"]:
519 # Map lyx to latex encoding names
523 "armscii8" : "armscii8",
524 "iso8859-1" : "latin1",
525 "iso8859-2" : "latin2",
526 "iso8859-3" : "latin3",
527 "iso8859-4" : "latin4",
528 "iso8859-5" : "iso88595",
529 "iso8859-6" : "8859-6",
530 "iso8859-7" : "iso-8859-7",
531 "iso8859-8" : "8859-8",
532 "iso8859-9" : "latin5",
533 "iso8859-13" : "latin7",
534 "iso8859-15" : "latin9",
535 "iso8859-16" : "latin10",
536 "applemac" : "applemac",
538 "cp437de" : "cp437de",
555 "utf8-platex" : "utf8",
562 i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
565 j = find_end_of_inset(document.body, i)
567 document.warning("Can't find end of bibtex inset at line %d!!" %(i))
570 encoding = get_quoted_value(document.body, "encoding", i, j)
574 # remove encoding line
575 k = find_token(document.body, "encoding", i, j)
578 # Re-find inset end line
579 j = find_end_of_inset(document.body, i)
582 h = find_token(document.header, "\\biblio_options", 0)
584 biblio_options = get_value(document.header, "\\biblio_options", h)
585 if not "bibencoding" in biblio_options:
586 document.header[h] += ",bibencoding=%s" % encodings[encoding]
588 bs = find_token(document.header, "\\biblatex_bibstyle", 0)
590 # this should not happen
591 document.warning("Malformed LyX document! No \\biblatex_bibstyle header found!")
593 document.header[bs-1 : bs-1] = ["\\biblio_options bibencoding=" + encodings[encoding]]
595 document.body[j+1:j+1] = put_cmd_in_ert("\\egroup")
596 document.body[i:i] = put_cmd_in_ert("\\bgroup\\inputencoding{" + encodings[encoding] + "}")
602 def convert_vcsinfo(document):
603 " Separate vcs Info inset from buffer Info inset. "
606 "vcs-revision" : "revision",
607 "vcs-tree-revision" : "tree-revision",
608 "vcs-author" : "author",
614 i = find_token(document.body, "\\begin_inset Info", i)
617 j = find_end_of_inset(document.body, i + 1)
619 document.warning("Malformed LyX document: Could not find end of Info inset.")
622 tp = find_token(document.body, 'type', i, j)
623 tpv = get_quoted_value(document.body, "type", tp)
627 arg = find_token(document.body, 'arg', i, j)
628 argv = get_quoted_value(document.body, "arg", arg)
629 if argv not in list(types.keys()):
632 document.body[tp] = "type \"vcs\""
633 document.body[arg] = "arg \"" + types[argv] + "\""
637 def revert_vcsinfo(document):
638 " Merge vcs Info inset to buffer Info inset. "
640 args = ["revision", "tree-revision", "author", "time", "date" ]
643 i = find_token(document.body, "\\begin_inset Info", i)
646 j = find_end_of_inset(document.body, i + 1)
648 document.warning("Malformed LyX document: Could not find end of Info inset.")
651 tp = find_token(document.body, 'type', i, j)
652 tpv = get_quoted_value(document.body, "type", tp)
656 arg = find_token(document.body, 'arg', i, j)
657 argv = get_quoted_value(document.body, "arg", arg)
659 document.warning("Malformed Info inset. Invalid vcs arg.")
662 document.body[tp] = "type \"buffer\""
663 document.body[arg] = "arg \"vcs-" + argv + "\""
671 supported_versions = ["2.4.0", "2.4"]
673 [545, [convert_lst_literalparam]],
678 [550, [convert_fontenc]],
685 [557, [convert_vcsinfo]],
686 [558, [removeFrontMatterStyles]]
690 [557, [addFrontMatterStyles]],
691 [556, [revert_vcsinfo]],
692 [555, [revert_bibencoding]],
693 [554, [revert_vcolumns]],
694 [553, [revert_stretchcolumn]],
695 [552, [revert_tuftecite]],
696 [551, [revert_floatpclass, revert_floatalignment]],
697 [550, [revert_nospellcheck]],
698 [549, [revert_fontenc]],
699 [548, []],# dummy format change
700 [547, [revert_lscape]],
701 [546, [revert_xcharter]],
702 [545, [revert_paratype]],
703 [544, [revert_lst_literalparam]]
707 if __name__ == "__main__":