1 # This file is part of lyx2lyx
2 # -*- coding: iso-8859-1 -*-
3 # Copyright (C) 2002 Dekel Tsur <dekel@lyx.org>
4 # Copyright (C) 2002-2004 José Matos <jamatos@lyx.org>
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 from os import access, F_OK
23 from parser_tools import find_token, find_end_of_inset, get_next_paragraph, \
24 get_paragraph, get_value, del_token, is_nonempty_line,\
25 find_tokens, find_end_of, find_token2
27 from string import replace, split, find, strip, join
32 def add_end_header(header):
33 header.append("\\end_header");
36 def rm_end_header(lines):
37 i = find_token(lines, "\\end_header", 0)
44 # \SpecialChar ~ -> \InsetSpace ~
46 def convert_spaces(lines):
47 for i in range(len(lines)):
48 lines[i] = replace(lines[i],"\\SpecialChar ~","\\InsetSpace ~")
51 def revert_spaces(lines):
52 for i in range(len(lines)):
53 lines[i] = replace(lines[i],"\\InsetSpace ~", "\\SpecialChar ~")
59 def convert_bibtex(lines):
60 for i in range(len(lines)):
61 lines[i] = replace(lines[i],"\\begin_inset LatexCommand \\BibTeX",
62 "\\begin_inset LatexCommand \\bibtex")
65 def revert_bibtex(lines):
66 for i in range(len(lines)):
67 lines[i] = replace(lines[i], "\\begin_inset LatexCommand \\bibtex",
68 "\\begin_inset LatexCommand \\BibTeX")
74 def remove_insetparent(lines):
77 i = find_token(lines, "\\begin_inset LatexCommand \\lyxparent", i)
86 def convert_external(lines):
87 external_rexp = re.compile(r'\\begin_inset External ([^,]*),"([^"]*)",')
88 external_header = "\\begin_inset External"
91 i = find_token(lines, external_header, i)
94 look = external_rexp.search(lines[i])
97 args[0] = look.group(1)
98 args[1] = look.group(2)
99 #FIXME: if the previous search fails then warn
101 if args[0] == "RasterImage":
102 # Convert a RasterImage External Inset to a Graphics Inset.
103 top = "\\begin_inset Graphics"
105 filename = "\tfilename " + args[1]
106 lines[i:i+1] = [top, filename]
109 # Convert the old External Inset format to the new.
110 top = external_header
111 template = "\ttemplate " + args[0]
113 filename = "\tfilename " + args[1]
114 lines[i:i+1] = [top, template, filename]
117 lines[i:i+1] = [top, template]
121 def revert_external_1(lines):
122 external_header = "\\begin_inset External"
125 i = find_token(lines, external_header, i)
129 template = split(lines[i+1])
133 filename = split(lines[i+1])
137 params = split(lines[i+1])
139 if lines[i+1]: del lines[i+1]
141 lines[i] = lines[i] + " " + template[0]+ ', "' + filename[0] + '", " '+ join(params[1:]) + '"'
145 def revert_external_2(lines):
146 draft_token = '\tdraft'
149 i = find_token(lines, '\\begin_inset External', i)
152 j = find_end_of_inset(lines, i + 1)
154 #this should not happen
156 k = find_token(lines, draft_token, i+1, j-1)
157 if (k != -1 and len(draft_token) == len(lines[k])):
165 def convert_comment(lines):
167 comment = "\\layout Comment"
169 i = find_token(lines, comment, i)
173 lines[i:i+1] = ["\\layout Standard","","",
174 "\\begin_inset Comment",
181 i = find_token(lines, "\\layout", i)
184 lines[i:i] = ["\\end_inset ","",""]
187 j = find_token(lines, '\\begin_deeper', old_i, i)
188 if j == -1: j = i + 1
189 k = find_token(lines, '\\begin_inset', old_i, i)
190 if k == -1: k = i + 1
195 i = find_end_of( lines, i, "\\begin_deeper","\\end_deeper")
197 #This case should not happen
198 #but if this happens deal with it greacefully adding
199 #the missing \end_deeper.
201 lines[i:i] = ["\end_deeper","","","\\end_inset ","",""]
209 i = find_end_of( lines, i, "\\begin_inset","\\end_inset")
211 #This case should not happen
212 #but if this happens deal with it greacefully adding
213 #the missing \end_inset.
215 lines[i:i] = ["\\end_inset ","","","\\end_inset ","",""]
221 if find(lines[i], comment) == -1:
222 lines[i:i] = ["\\end_inset"]
225 lines[i:i+1] = ["\\layout Standard"]
229 def revert_comment(lines):
232 i = find_tokens(lines, ["\\begin_inset Comment", "\\begin_inset Greyedout"], i)
236 lines[i] = "\\begin_inset Note"
243 def add_end_layout(lines):
244 i = find_token(lines, '\\layout', 0)
250 struct_stack = ["\\layout"]
253 i = find_tokens(lines, ["\\begin_inset", "\\end_inset", "\\layout",
254 "\\begin_deeper", "\\end_deeper", "\\the_end"], i)
256 token = split(lines[i])[0]
258 if token == "\\begin_inset":
259 struct_stack.append(token)
263 if token == "\\end_inset":
264 tail = struct_stack.pop()
265 if tail == "\\layout":
267 lines.insert(i,"\\end_layout")
269 #Check if it is the correct tag
274 if token == "\\layout":
275 tail = struct_stack.pop()
278 lines.insert(i,"\\end_layout")
281 struct_stack.append(tail)
283 struct_stack.append(token)
286 if token == "\\begin_deeper":
288 lines.insert(i,"\\end_layout")
290 struct_stack.append(token)
293 if token == "\\end_deeper":
295 lines.insert(i,"\\end_layout")
297 while struct_stack[-1] != "\\begin_deeper":
303 lines.insert(i, "\\end_layout")
307 def rm_end_layout(lines):
310 i = find_token(lines, '\\end_layout', i)
319 # Handle change tracking keywords
321 def insert_tracking_changes(lines):
322 i = find_token(lines, "\\tracking_changes", 0)
324 lines.append("\\tracking_changes 0")
326 def rm_tracking_changes(lines):
327 i = find_token(lines, "\\author", 0)
331 i = find_token(lines, "\\tracking_changes", 0)
337 def rm_body_changes(lines):
340 i = find_token(lines, "\\change_", i)
348 # \layout -> \begin_layout
350 def layout2begin_layout(lines):
353 i = find_token(lines, '\\layout', i)
357 lines[i] = replace(lines[i], '\\layout', '\\begin_layout')
361 def begin_layout2layout(lines):
364 i = find_token(lines, '\\begin_layout', i)
368 lines[i] = replace(lines[i], '\\begin_layout', '\\layout')
373 # valignment="center" -> valignment="middle"
375 def convert_valignment_middle(lines, start, end):
376 for i in range(start, end):
377 if re.search('^<(column|cell) .*valignment="center".*>$', lines[i]):
378 lines[i] = replace(lines[i], 'valignment="center"', 'valignment="middle"')
381 def convert_table_valignment_middle(lines):
384 i = find_token(lines, '\\begin_inset Tabular', i)
387 j = find_end_of_inset(lines, i + 1)
389 #this should not happen
390 convert_valignment_middle(lines, i + 1, len(lines))
392 convert_valignment_middle(lines, i + 1, j)
396 def revert_table_valignment_middle(lines, start, end):
397 for i in range(start, end):
398 if re.search('^<(column|cell) .*valignment="middle".*>$', lines[i]):
399 lines[i] = replace(lines[i], 'valignment="middle"', 'valignment="center"')
402 def revert_valignment_middle(lines):
405 i = find_token(lines, '\\begin_inset Tabular', i)
408 j = find_end_of_inset(lines, i + 1)
410 #this should not happen
411 revert_table_valignment_middle(lines, i + 1, len(lines))
413 revert_table_valignment_middle(lines, i + 1, j)
418 # \the_end -> \end_document
420 def convert_end_document(lines):
421 i = find_token(lines, "\\the_end", 0)
423 lines.append("\\end_document")
425 lines[i] = "\\end_document"
428 def revert_end_document(lines):
429 i = find_token(lines, "\\end_document", 0)
431 lines.append("\\the_end")
433 lines[i] = "\\the_end"
437 # Convert line and page breaks
440 #\line_top \line_bottom \pagebreak_top \pagebreak_bottom \added_space_top xxx \added_space_bottom yyy
444 #\begin layout Standard
449 #\begin_inset VSpace xxx
453 #\begin_layout Standard
457 #\begin_layout Standard
459 #\begin_inset VSpace xxx
466 def convert_breaks(lines):
469 i = find_token(lines, "\\begin_layout", i)
473 line_top = find(lines[i],"\\line_top")
474 line_bot = find(lines[i],"\\line_bottom")
475 pb_top = find(lines[i],"\\pagebreak_top")
476 pb_bot = find(lines[i],"\\pagebreak_bottom")
477 vspace_top = find(lines[i],"\\added_space_top")
478 vspace_bot = find(lines[i],"\\added_space_bottom")
480 if line_top == -1 and line_bot == -1 and pb_bot == -1 and pb_top == -1 and vspace_top == -1 and vspace_bot == -1:
483 for tag in "\\line_top", "\\line_bottom", "\\pagebreak_top", "\\pagebreak_bottom":
484 lines[i] = replace(lines[i], tag, "")
487 # the position could be change because of the removal of other
488 # paragraph properties above
489 vspace_top = find(lines[i],"\\added_space_top")
490 tmp_list = split(lines[i][vspace_top:])
491 vspace_top_value = tmp_list[1]
492 lines[i] = lines[i][:vspace_top] + join(tmp_list[2:])
495 # the position could be change because of the removal of other
496 # paragraph properties above
497 vspace_bot = find(lines[i],"\\added_space_bottom")
498 tmp_list = split(lines[i][vspace_bot:])
499 vspace_bot_value = tmp_list[1]
500 lines[i] = lines[i][:vspace_bot] + join(tmp_list[2:])
502 lines[i] = strip(lines[i])
505 # Create an empty paragraph for line and page break that belong
506 # above the paragraph
507 if pb_top !=-1 or line_top != -1 or vspace_bot != -1:
509 paragraph_above = ['','\\begin_layout Standard','','']
512 paragraph_above.extend(['\\newpage ',''])
515 paragraph_above.extend(['\\begin_inset VSpace ' + vspace_top_value,'\\end_inset ','',''])
518 paragraph_above.extend(['\\lyxline ',''])
520 paragraph_above.extend(['\\end_layout',''])
522 #inset new paragraph above the current paragraph
523 lines[i-2:i-2] = paragraph_above
524 i = i + len(paragraph_above)
526 # Ensure that nested style are converted later.
527 k = find_end_of(lines, i, "\\begin_layout", "\\end_layout")
532 if pb_top !=-1 or line_top != -1 or vspace_bot != -1:
534 paragraph_bellow = ['','\\begin_layout Standard','','']
537 paragraph_bellow.extend(['\\lyxline ',''])
540 paragraph_bellow.extend(['\\begin_inset VSpace ' + vspace_bot_value,'\\end_inset ','',''])
543 paragraph_bellow.extend(['\\newpage ',''])
545 paragraph_bellow.extend(['\\end_layout',''])
547 #inset new paragraph above the current paragraph
548 lines[k + 1: k + 1] = paragraph_bellow
554 def convert_note(lines):
557 i = find_tokens(lines, ["\\begin_inset Note",
558 "\\begin_inset Comment",
559 "\\begin_inset Greyedout"], i)
563 lines[i] = lines[i][0:13] + 'Note ' + lines[i][13:]
567 def revert_note(lines):
568 note_header = "\\begin_inset Note "
571 i = find_token(lines, note_header, i)
575 lines[i] = "\\begin_inset " + lines[i][len(note_header):]
582 def convert_box(lines):
585 i = find_tokens(lines, ["\\begin_inset Boxed",
586 "\\begin_inset Doublebox",
587 "\\begin_inset Frameless",
588 "\\begin_inset ovalbox",
589 "\\begin_inset Ovalbox",
590 "\\begin_inset Shadowbox"], i)
594 lines[i] = lines[i][0:13] + 'Box ' + lines[i][13:]
598 def revert_box(lines):
599 box_header = "\\begin_inset Box "
602 i = find_token(lines, box_header, i)
606 lines[i] = "\\begin_inset " + lines[i][len(box_header):]
613 def convert_collapsable(lines, opt):
616 i = find_tokens(lines, ["\\begin_inset Box",
617 "\\begin_inset Branch",
618 "\\begin_inset CharStyle",
619 "\\begin_inset Float",
620 "\\begin_inset Foot",
621 "\\begin_inset Marginal",
622 "\\begin_inset Note",
623 "\\begin_inset OptArg",
624 "\\begin_inset Wrap"], i)
628 # Seach for a line starting 'collapsed'
629 # If, however, we find a line starting '\begin_layout'
630 # (_always_ present) then break with a warning message
633 if (lines[i] == "collapsed false"):
634 lines[i] = "status open"
636 elif (lines[i] == "collapsed true"):
637 lines[i] = "status collapsed"
639 elif (lines[i][:13] == "\\begin_layout"):
640 opt.warning("Malformed LyX file.")
647 def revert_collapsable(lines, opt):
650 i = find_tokens(lines, ["\\begin_inset Box",
651 "\\begin_inset Branch",
652 "\\begin_inset CharStyle",
653 "\\begin_inset Float",
654 "\\begin_inset Foot",
655 "\\begin_inset Marginal",
656 "\\begin_inset Note",
657 "\\begin_inset OptArg",
658 "\\begin_inset Wrap"], i)
662 # Seach for a line starting 'status'
663 # If, however, we find a line starting '\begin_layout'
664 # (_always_ present) then break with a warning message
667 if (lines[i] == "status open"):
668 lines[i] = "collapsed false"
670 elif (lines[i] == "status collapsed" or
671 lines[i] == "status inlined"):
672 lines[i] = "collapsed true"
674 elif (lines[i][:13] == "\\begin_layout"):
675 opt.warning("Malformed LyX file.")
685 def convert_ert(lines, opt):
688 i = find_token(lines, "\\begin_inset ERT", i)
692 # Seach for a line starting 'status'
693 # If, however, we find a line starting '\begin_layout'
694 # (_always_ present) then break with a warning message
697 if (lines[i] == "status Open"):
698 lines[i] = "status open"
700 elif (lines[i] == "status Collapsed"):
701 lines[i] = "status collapsed"
703 elif (lines[i] == "status Inlined"):
704 lines[i] = "status inlined"
706 elif (lines[i][:13] == "\\begin_layout"):
707 opt.warning("Malformed LyX file.")
714 def revert_ert(lines, opt):
717 i = find_token(lines, "\\begin_inset ERT", i)
721 # Seach for a line starting 'status'
722 # If, however, we find a line starting '\begin_layout'
723 # (_always_ present) then break with a warning message
726 if (lines[i] == "status open"):
727 lines[i] = "status Open"
729 elif (lines[i] == "status collapsed"):
730 lines[i] = "status Collapsed"
732 elif (lines[i] == "status inlined"):
733 lines[i] = "status Inlined"
735 elif (lines[i][:13] == "\\begin_layout"):
736 opt.warning("Malformed LyX file.")
746 def convert_minipage(lines):
747 """ Convert minipages to the box inset.
748 We try to use the same order of arguments as lyx does.
751 inner_pos = ["c","t","b","s"]
755 i = find_token(lines, "\\begin_inset Minipage", i)
759 lines[i] = "\\begin_inset Box Frameless"
762 # convert old to new position using the pos list
763 if lines[i][:8] == "position":
764 lines[i] = 'position "%s"' % pos[int(lines[i][9])]
766 lines.insert(i, 'position "%s"' % pos[0])
769 lines.insert(i, 'hor_pos "c"')
771 lines.insert(i, 'has_inner_box 1')
774 # convert the inner_position
775 if lines[i][:14] == "inner_position":
776 lines[i] = 'inner_pos "%s"' % inner_pos[int(lines[i][15])]
778 lines.insert('inner_pos "%s"' % inner_pos[0])
781 # We need this since the new file format has a height and width
782 # in a different order.
783 if lines[i][:6] == "height":
784 height = lines[i][6:]
785 # test for default value of 221 and convert it accordingly
786 if height == ' "0pt"':
792 if lines[i][:5] == "width":
798 if lines[i][:9] == "collapsed":
799 if lines[i][9:] == "true":
807 lines.insert(i, 'use_parbox 0')
809 lines.insert(i, 'width' + width)
811 lines.insert(i, 'special "none"')
813 lines.insert(i, 'height' + height)
815 lines.insert(i, 'height_special "totalheight"')
817 lines.insert(i, 'status ' + status)
821 # -------------------------------------------------------------------------------------------
822 # Convert backslashes into valid ERT code, append the converted text to
823 # lines[i] and return the (maybe incremented) line index i
824 def convert_ertbackslash(lines, i, ert):
827 lines[i] = lines[i] + '\\backslash '
831 lines[i] = lines[i] + c
835 def convert_vspace(header, lines, opt):
837 # Get default spaceamount
838 i = find_token(header, '\\defskip', 0)
840 defskipamount = 'medskip'
842 defskipamount = split(header[i])[1]
847 i = find_token(lines, '\\begin_inset VSpace', i)
850 spaceamount = split(lines[i])[2]
852 # Are we at the beginning or end of a paragraph?
854 start = get_paragraph(lines, i) + 1
855 for k in range(start, i):
856 if is_nonempty_line(lines[k]):
860 j = find_end_of_inset(lines, i)
862 opt.warning("Malformed LyX file: Missing '\\end_inset'.")
865 end = get_next_paragraph(lines, i)
866 for k in range(j + 1, end):
867 if is_nonempty_line(lines[k]):
871 # Convert to paragraph formatting if we are at the beginning or end
872 # of a paragraph and the resulting paragraph would not be empty
873 if ((paragraph_start and not paragraph_end) or
874 (paragraph_end and not paragraph_start)):
875 # The order is important: del and insert invalidate some indices
879 lines.insert(start, '\\added_space_top ' + spaceamount + ' ')
881 lines.insert(start, '\\added_space_bottom ' + spaceamount + ' ')
885 lines[i:i+1] = ['\\begin_inset ERT', 'status Collapsed', '',
886 '\\layout Standard', '', '\\backslash ']
888 if spaceamount[-1] == '*':
889 spaceamount = spaceamount[:-1]
894 # Replace defskip by the actual value
895 if spaceamount == 'defskip':
896 spaceamount = defskipamount
898 # LaTeX does not know \\smallskip* etc
900 if spaceamount == 'smallskip':
901 spaceamount = '\\smallskipamount'
902 elif spaceamount == 'medskip':
903 spaceamount = '\\medskipamount'
904 elif spaceamount == 'bigskip':
905 spaceamount = '\\bigskipamount'
906 elif spaceamount == 'vfill':
907 spaceamount = '\\fill'
909 # Finally output the LaTeX code
910 if (spaceamount == 'smallskip' or spaceamount == 'medskip' or
911 spaceamount == 'bigskip' or spaceamount == 'vfill'):
912 lines.insert(i, spaceamount)
915 lines.insert(i, 'vspace*{')
917 lines.insert(i, 'vspace{')
918 i = convert_ertbackslash(lines, i, spaceamount)
919 lines[i] = lines[i] + '}'
923 # Convert a LyX length into valid ERT code and append it to lines[i]
924 # Return the (maybe incremented) line index i
925 def convert_ertlen(lines, i, len, special):
926 units = {"text%":"\\textwidth", "col%":"\\columnwidth",
927 "page%":"\\pagewidth", "line%":"\\linewidth",
928 "theight%":"\\textheight", "pheight%":"\\pageheight"}
930 # Convert special lengths
931 if special != 'none':
932 len = '%f\\' % len2value(len) + special
934 # Convert LyX units to LaTeX units
935 for unit in units.keys():
936 if find(len, unit) != -1:
937 len = '%f' % (len2value(len) / 100) + units[unit]
940 # Convert backslashes and insert the converted length into lines
941 return convert_ertbackslash(lines, i, len)
944 # Return the value of len without the unit in numerical form
946 result = re.search('([+-]?[0-9.]+)', len)
948 return float(result.group(1))
949 # No number means 1.0
953 def convert_frameless_box(lines, opt):
954 pos = ['t', 'c', 'b']
955 inner_pos = ['c', 't', 'b', 's']
958 i = find_token(lines, '\\begin_inset Frameless', i)
961 j = find_end_of_inset(lines, i)
963 opt.warning("Malformed LyX file: Missing '\\end_inset'\n")
969 params = {'position':'0', 'hor_pos':'c', 'has_inner_box':'1',
970 'inner_pos':'1', 'use_parbox':'0', 'width':'100col%',
971 'special':'none', 'height':'1in',
972 'height_special':'totalheight', 'collapsed':'false'}
973 for key in params.keys():
974 value = replace(get_value(lines, key, i, j), '"', '')
976 if key == 'position':
977 # convert new to old position: 'position "t"' -> 0
978 value = find_token(pos, value, 0)
981 elif key == 'inner_pos':
982 # convert inner position
983 value = find_token(inner_pos, value, 0)
988 j = del_token(lines, key, i, j)
991 # Convert to minipage or ERT?
992 # Note that the inner_position and height parameters of a minipage
993 # inset are ignored and not accessible for the user, although they
994 # are present in the file format and correctly read in and written.
995 # Therefore we convert to ERT if they do not have their LaTeX
996 # defaults. These are:
997 # - the value of "position" for "inner_pos"
998 # - "\totalheight" for "height"
999 if (params['use_parbox'] != '0' or
1000 params['has_inner_box'] != '1' or
1001 params['special'] != 'none' or
1002 inner_pos[params['inner_pos']] != pos[params['position']] or
1003 params['height_special'] != 'totalheight' or
1004 len2value(params['height']) != 1.0):
1007 if params['collapsed'] == 'true':
1008 params['collapsed'] = 'Collapsed'
1010 params['collapsed'] = 'Open'
1011 lines[i : i] = ['\\begin_inset ERT', 'status ' + params['collapsed'],
1012 '', '\\layout Standard', '', '\\backslash ']
1014 if params['use_parbox'] == '1':
1015 lines.insert(i, 'parbox')
1017 lines.insert(i, 'begin{minipage}')
1018 lines[i] = lines[i] + '[' + pos[params['position']] + ']['
1019 i = convert_ertlen(lines, i, params['height'], params['height_special'])
1020 lines[i] = lines[i] + '][' + inner_pos[params['inner_pos']] + ']{'
1021 i = convert_ertlen(lines, i, params['width'], params['special'])
1022 if params['use_parbox'] == '1':
1023 lines[i] = lines[i] + '}{'
1025 lines[i] = lines[i] + '}'
1027 lines[i:i] = ['', '\\end_inset ']
1029 j = find_end_of_inset(lines, i)
1031 opt.warning("Malformed LyX file: Missing '\\end_inset'.")
1033 lines[j-1:j-1] = ['\\begin_inset ERT', 'status ' + params['collapsed'],
1034 '', '\\layout Standard', '']
1036 if params['use_parbox'] == '1':
1037 lines.insert(j, '}')
1039 lines[j:j] = ['\\backslash ', 'end{minipage}']
1042 # Convert to minipage
1043 lines[i:i] = ['\\begin_inset Minipage',
1044 'position %d' % params['position'],
1045 'inner_position %d' % params['inner_pos'],
1046 'height "' + params['height'] + '"',
1047 'width "' + params['width'] + '"',
1048 'collapsed ' + params['collapsed']]
1055 def convert_jurabib(header, opt):
1056 i = find_token(header, '\\use_numerical_citations', 0)
1058 opt.warning("Malformed lyx file: Missing '\\use_numerical_citations'")
1060 header.insert(i + 1, '\\use_jurabib 0')
1063 def revert_jurabib(header, opt):
1064 i = find_token(header, '\\use_jurabib', 0)
1066 opt.warning("Malformed lyx file: Missing '\\use_jurabib'")
1068 if get_value(header, '\\use_jurabib', 0) != "0":
1069 opt.warning("Conversion of '\\use_jurabib = 1' not yet implemented.")
1070 # Don't remove '\\use_jurabib' so that people will get warnings by lyx
1078 def convert_bibtopic(header, opt):
1079 i = find_token(header, '\\use_jurabib', 0)
1081 opt.warning("Malformed lyx file: Missing '\\use_jurabib'")
1083 header.insert(i + 1, '\\use_bibtopic 0')
1086 def revert_bibtopic(header, opt):
1087 i = find_token(header, '\\use_bibtopic', 0)
1089 opt.warning("Malformed lyx file: Missing '\\use_bibtopic'")
1091 if get_value(header, '\\use_bibtopic', 0) != "0":
1092 opt.warning("Conversion of '\\use_bibtopic = 1' not yet implemented.")
1093 # Don't remove '\\use_jurabib' so that people will get warnings by lyx
1100 def convert_float(lines, opt):
1103 i = find_token(lines, '\\begin_inset Float', i)
1106 # Seach for a line starting 'wide'
1107 # If, however, we find a line starting '\begin_layout'
1108 # (_always_ present) then break with a warning message
1111 if (lines[i][:4] == "wide"):
1112 lines.insert(i + 1, 'sideways false')
1114 elif (lines[i][:13] == "\\begin_layout"):
1115 opt.warning("Malformed lyx file")
1121 def revert_float(lines, opt):
1124 i = find_token(lines, '\\begin_inset Float', i)
1127 j = find_end_of_inset(lines, i)
1129 opt.warning("Malformed lyx file: Missing '\\end_inset'")
1132 if get_value(lines, 'sideways', i, j) != "false":
1133 opt.warning("Conversion of 'sideways true' not yet implemented.")
1134 # Don't remove 'sideways' so that people will get warnings by lyx
1137 del_token(lines, 'sideways', i, j)
1141 def convert_graphics(lines, opt):
1142 """ Add extension to filenames of insetgraphics if necessary.
1146 i = find_token(lines, "\\begin_inset Graphics", i)
1150 j = find_token2(lines, "filename", i)
1154 filename = split(lines[j])[1]
1155 absname = os.path.normpath(os.path.join(opt.dir, filename))
1156 if opt.input == stdin and not os.path.isabs(filename):
1157 # We don't know the directory and cannot check the file.
1158 # We could use a heuristic and take the current directory,
1159 # and we could try to find out if filename has an extension,
1160 # but that would be just guesses and could be wrong.
1161 opt.warning("""Warning: Can not determine whether file
1163 needs an extension when reading from standard input.
1164 You may need to correct the file manually or run
1165 lyx2lyx again with the .lyx file as commandline argument.""" % filename)
1167 # This needs to be the same algorithm as in pre 233 insetgraphics
1168 if access(absname, F_OK):
1170 if access(absname + ".ps", F_OK):
1171 lines[j] = replace(lines[j], filename, filename + ".ps")
1173 if access(absname + ".eps", F_OK):
1174 lines[j] = replace(lines[j], filename, filename + ".eps")
1178 # Convert firstname and surname from styles -> char styles
1180 def convert_names(lines, opt):
1181 """ Convert in the docbook backend from firstname and surname style
1184 if opt.backend != "docbook":
1190 i = find_token(lines, "\\begin_layout Author", i)
1195 while lines[i] == "":
1198 if lines[i][:11] != "\\end_layout" or lines[i+2][:13] != "\\begin_deeper":
1203 i = find_end_of( lines, i+3, "\\begin_deeper","\\end_deeper")
1205 # something is really wrong, abort
1206 opt.warning("Missing \\end_deeper,after style Author")
1207 opt.warning("Aborted attempt to parse FirstName and Surname")
1209 firstname, surname = "", ""
1213 j = find_token(name, "\\begin_layout FirstName", 0)
1216 while(name[j] != "\\end_layout"):
1217 firstname = firstname + name[j]
1220 j = find_token(name, "\\begin_layout Surname", 0)
1223 while(name[j] != "\\end_layout"):
1224 surname = surname + name[j]
1230 lines[k-1:k-1] = ["", "",
1231 "\\begin_inset CharStyle Firstname",
1234 "\\begin_layout Standard",
1242 "\\begin_inset CharStyle Surname",
1245 "\\begin_layout Standard",
1254 def revert_names(lines, opt):
1255 """ Revert in the docbook backend from firstname and surname char style
1258 if opt.backend != "docbook":
1263 # \use_natbib 1 \cite_engine <style>
1264 # \use_numerical_citations 0 -> where <style> is one of
1265 # \use_jurabib 0 "basic", "natbib_authoryear",
1266 # "natbib_numerical" or "jurabib"
1267 def convert_cite_engine(header, opt):
1268 a = find_token(header, "\\use_natbib", 0)
1270 opt.warning("Malformed lyx file: Missing '\\use_natbib'")
1273 b = find_token(header, "\\use_numerical_citations", 0)
1274 if b == -1 or b != a+1:
1275 opt.warning("Malformed lyx file: Missing '\\use_numerical_citations'")
1278 c = find_token(header, "\\use_jurabib", 0)
1279 if c == -1 or c != b+1:
1280 opt.warning("Malformed lyx file: Missing '\\use_jurabib'")
1283 use_natbib = int(split(header[a])[1])
1284 use_numerical_citations = int(split(header[b])[1])
1285 use_jurabib = int(split(header[c])[1])
1287 cite_engine = "basic"
1289 if use_numerical_citations:
1290 cite_engine = "natbib_numerical"
1292 cite_engine = "natbib_authoryear"
1294 cite_engine = "jurabib"
1297 header.insert(a, "\\cite_engine " + cite_engine)
1300 def revert_cite_engine(header, opt):
1301 i = find_token(header, "\\cite_engine", 0)
1303 opt.warning("Malformed lyx file: Missing '\\cite_engine'")
1306 cite_engine = split(header[i])[1]
1311 if cite_engine == "natbib_numerical":
1314 elif cite_engine == "natbib_authoryear":
1316 elif cite_engine == "jurabib":
1320 header.insert(i, "\\use_jurabib " + use_jurabib)
1321 header.insert(i, "\\use_numerical_citations " + use_numerical)
1322 header.insert(i, "\\use_natbib " + use_natbib)
1329 def convert(header, body, opt):
1330 if opt.format < 223:
1331 insert_tracking_changes(header)
1332 add_end_header(header)
1333 convert_spaces(body)
1334 convert_bibtex(body)
1335 remove_insetparent(body)
1337 if opt.end == opt.format: return
1339 if opt.format < 224:
1340 convert_external(body)
1341 convert_comment(body)
1343 if opt.end == opt.format: return
1345 if opt.format < 225:
1346 add_end_layout(body)
1347 layout2begin_layout(body)
1348 convert_end_document(body)
1349 convert_table_valignment_middle(body)
1350 convert_breaks(body)
1352 if opt.end == opt.format: return
1354 if opt.format < 226:
1357 if opt.end == opt.format: return
1359 if opt.format < 227:
1362 if opt.end == opt.format: return
1364 if opt.format < 228:
1365 convert_collapsable(body, opt)
1366 convert_ert(body, opt)
1368 if opt.end == opt.format: return
1370 if opt.format < 229:
1371 convert_minipage(body)
1373 if opt.end == opt.format: return
1375 if opt.format < 230:
1376 convert_jurabib(header, opt)
1378 if opt.end == opt.format: return
1380 if opt.format < 231:
1381 convert_float(body, opt)
1383 if opt.end == opt.format: return
1385 if opt.format < 232:
1386 convert_bibtopic(header, opt)
1388 if opt.end == opt.format: return
1390 if opt.format < 233:
1391 convert_graphics(body, opt)
1392 convert_names(body, opt)
1394 if opt.end == opt.format: return
1396 if opt.format < 234:
1397 convert_cite_engine(header, opt)
1400 def revert(header, body, opt):
1401 if opt.format > 233:
1402 revert_cite_engine(header, opt)
1404 if opt.end == opt.format: return
1406 if opt.format > 232:
1407 revert_names(body, opt)
1409 if opt.end == opt.format: return
1411 if opt.format > 231:
1412 revert_bibtopic(header, opt)
1414 if opt.end == opt.format: return
1416 if opt.format > 230:
1417 revert_float(body, opt)
1419 if opt.end == opt.format: return
1421 if opt.format > 229:
1422 revert_jurabib(header, opt)
1424 if opt.end == opt.format: return
1426 if opt.format > 228:
1428 if opt.end == opt.format: return
1430 if opt.format > 227:
1431 revert_collapsable(body, opt)
1432 revert_ert(body, opt)
1434 if opt.end == opt.format: return
1436 if opt.format > 226:
1438 revert_external_2(body)
1440 if opt.end == opt.format: return
1442 if opt.format > 225:
1445 if opt.end == opt.format: return
1447 if opt.format > 224:
1449 begin_layout2layout(body)
1450 revert_end_document(body)
1451 revert_valignment_middle(body)
1452 convert_vspace(header, body, opt)
1453 convert_frameless_box(body, opt)
1454 if opt.end == opt.format: return
1456 if opt.format > 223:
1457 revert_external_2(body)
1458 revert_comment(body)
1460 if opt.end == opt.format: return
1462 if opt.format > 221:
1463 rm_end_header(header)
1466 rm_tracking_changes(header)
1467 rm_body_changes(body)
1471 if __name__ == "__main__":