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
30 # Remove \color default
32 def remove_color_default(file):
35 i = find_token(file.body, "\\color default", i)
38 file.body[i] = replace(file.body[i], "\\color default",
45 def add_end_header(file):
46 file.header.append("\\end_header");
49 def rm_end_header(file):
50 i = find_token(file.header, "\\end_header", 0)
57 # \SpecialChar ~ -> \InsetSpace ~
59 def convert_spaces(file):
60 for i in range(len(file.body)):
61 file.body[i] = replace(file.body[i],"\\SpecialChar ~","\\InsetSpace ~")
64 def revert_spaces(file):
65 for i in range(len(file.body)):
66 file.body[i] = replace(file.body[i],"\\InsetSpace ~", "\\SpecialChar ~")
72 def convert_bibtex(file):
73 for i in range(len(file.body)):
74 file.body[i] = replace(file.body[i],"\\begin_inset LatexCommand \\BibTeX",
75 "\\begin_inset LatexCommand \\bibtex")
78 def revert_bibtex(file):
79 for i in range(len(file.body)):
80 file.body[i] = replace(file.body[i], "\\begin_inset LatexCommand \\bibtex",
81 "\\begin_inset LatexCommand \\BibTeX")
87 def remove_insetparent(file):
90 i = find_token(file.body, "\\begin_inset LatexCommand \\lyxparent", i)
99 def convert_external(file):
100 external_rexp = re.compile(r'\\begin_inset External ([^,]*),"([^"]*)",')
101 external_header = "\\begin_inset External"
104 i = find_token(file.body, external_header, i)
107 look = external_rexp.search(file.body[i])
110 args[0] = look.group(1)
111 args[1] = look.group(2)
112 #FIXME: if the previous search fails then warn
114 if args[0] == "RasterImage":
115 # Convert a RasterImage External Inset to a Graphics Inset.
116 top = "\\begin_inset Graphics"
118 filename = "\tfilename " + args[1]
119 file.body[i:i+1] = [top, filename]
122 # Convert the old External Inset format to the new.
123 top = external_header
124 template = "\ttemplate " + args[0]
126 filename = "\tfilename " + args[1]
127 file.body[i:i+1] = [top, template, filename]
130 file.body[i:i+1] = [top, template]
134 def revert_external_1(file):
135 external_header = "\\begin_inset External"
138 i = find_token(file.body, external_header, i)
142 template = split(file.body[i+1])
146 filename = split(file.body[i+1])
150 params = split(file.body[i+1])
152 if file.body[i+1]: del file.body[i+1]
154 file.body[i] = file.body[i] + " " + template[0]+ ', "' + filename[0] + '", " '+ join(params[1:]) + '"'
158 def revert_external_2(file):
159 draft_token = '\tdraft'
162 i = find_token(file.body, '\\begin_inset External', i)
165 j = find_end_of_inset(file.body, i + 1)
167 #this should not happen
169 k = find_token(file.body, draft_token, i+1, j-1)
170 if (k != -1 and len(draft_token) == len(file.body[k])):
178 def convert_comment(file):
180 comment = "\\layout Comment"
182 i = find_token(file.body, comment, i)
186 file.body[i:i+1] = ["\\layout Standard","","",
187 "\\begin_inset Comment",
194 i = find_token(file.body, "\\layout", i)
196 i = len(file.body) - 1
197 file.body[i:i] = ["\\end_inset","",""]
200 j = find_token(file.body, '\\begin_deeper', old_i, i)
201 if j == -1: j = i + 1
202 k = find_token(file.body, '\\begin_inset', old_i, i)
203 if k == -1: k = i + 1
208 i = find_end_of( file.body, i, "\\begin_deeper","\\end_deeper")
210 #This case should not happen
211 #but if this happens deal with it greacefully adding
212 #the missing \end_deeper.
213 i = len(file.body) - 1
214 file.body[i:i] = ["\end_deeper",""]
222 i = find_end_of( file.body, i, "\\begin_inset","\\end_inset")
224 #This case should not happen
225 #but if this happens deal with it greacefully adding
226 #the missing \end_inset.
227 i = len(file.body) - 1
228 file.body[i:i] = ["\\end_inset","","","\\end_inset","",""]
234 if find(file.body[i], comment) == -1:
235 file.body[i:i] = ["\\end_inset"]
238 file.body[i:i+1] = ["\\layout Standard"]
242 def revert_comment(file):
245 i = find_tokens(file.body, ["\\begin_inset Comment", "\\begin_inset Greyedout"], i)
249 file.body[i] = "\\begin_inset Note"
256 def add_end_layout(file):
257 i = find_token(file.body, '\\layout', 0)
263 struct_stack = ["\\layout"]
266 i = find_tokens(file.body, ["\\begin_inset", "\\end_inset", "\\layout",
267 "\\begin_deeper", "\\end_deeper", "\\the_end"], i)
269 token = split(file.body[i])[0]
271 if token == "\\begin_inset":
272 struct_stack.append(token)
276 if token == "\\end_inset":
277 tail = struct_stack.pop()
278 if tail == "\\layout":
279 file.body.insert(i,"")
280 file.body.insert(i,"\\end_layout")
282 #Check if it is the correct tag
287 if token == "\\layout":
288 tail = struct_stack.pop()
290 file.body.insert(i,"")
291 file.body.insert(i,"\\end_layout")
294 struct_stack.append(tail)
296 struct_stack.append(token)
299 if token == "\\begin_deeper":
300 file.body.insert(i,"")
301 file.body.insert(i,"\\end_layout")
303 struct_stack.append(token)
306 if token == "\\end_deeper":
307 if struct_stack[-1] == '\\layout':
308 file.body.insert(i, '\\end_layout')
315 file.body.insert(i, "")
316 file.body.insert(i, "\\end_layout")
320 def rm_end_layout(file):
323 i = find_token(file.body, '\\end_layout', i)
332 # Handle change tracking keywords
334 def insert_tracking_changes(file):
335 i = find_token(file.header, "\\tracking_changes", 0)
337 file.header.append("\\tracking_changes 0")
340 def rm_tracking_changes(file):
341 i = find_token(file.header, "\\author", 0)
345 i = find_token(file.header, "\\tracking_changes", 0)
351 def rm_body_changes(file):
354 i = find_token(file.body, "\\change_", i)
362 # \layout -> \begin_layout
364 def layout2begin_layout(file):
367 i = find_token(file.body, '\\layout', i)
371 file.body[i] = replace(file.body[i], '\\layout', '\\begin_layout')
375 def begin_layout2layout(file):
378 i = find_token(file.body, '\\begin_layout', i)
382 file.body[i] = replace(file.body[i], '\\begin_layout', '\\layout')
387 # valignment="center" -> valignment="middle"
389 def convert_valignment_middle(body, start, end):
390 for i in range(start, end):
391 if re.search('^<(column|cell) .*valignment="center".*>$', body[i]):
392 body[i] = replace(body[i], 'valignment="center"', 'valignment="middle"')
395 def convert_table_valignment_middle(file):
398 i = find_token(file.body, '\\begin_inset Tabular', i)
401 j = find_end_of_inset(file.body, i + 1)
403 #this should not happen
404 convert_valignment_middle(file.body, i + 1, len(file.body))
406 convert_valignment_middle(file.body, i + 1, j)
410 def revert_table_valignment_middle(body, start, end):
411 for i in range(start, end):
412 if re.search('^<(column|cell) .*valignment="middle".*>$', body[i]):
413 body[i] = replace(body[i], 'valignment="middle"', 'valignment="center"')
416 def revert_valignment_middle(file):
419 i = find_token(file.body, '\\begin_inset Tabular', i)
422 j = find_end_of_inset(file.body, i + 1)
424 #this should not happen
425 revert_table_valignment_middle(file.body, i + 1, len(file.body))
427 revert_table_valignment_middle(file.body, i + 1, j)
432 # \the_end -> \end_document
434 def convert_end_document(file):
435 i = find_token(file.body, "\\the_end", 0)
437 file.body.append("\\end_document")
439 file.body[i] = "\\end_document"
442 def revert_end_document(file):
443 i = find_token(file.body, "\\end_document", 0)
445 file.body.append("\\the_end")
447 file.body[i] = "\\the_end"
451 # Convert line and page breaks
454 #\line_top \line_bottom \pagebreak_top \pagebreak_bottom \added_space_top xxx \added_space_bottom yyy
458 #\begin layout Standard
463 #\begin_inset VSpace xxx
467 #\begin_layout Standard
471 #\begin_layout Standard
473 #\begin_inset VSpace xxx
480 def convert_breaks(file):
483 i = find_token(file.body, "\\begin_layout", i)
487 line_top = find(file.body[i],"\\line_top")
488 line_bot = find(file.body[i],"\\line_bottom")
489 pb_top = find(file.body[i],"\\pagebreak_top")
490 pb_bot = find(file.body[i],"\\pagebreak_bottom")
491 vspace_top = find(file.body[i],"\\added_space_top")
492 vspace_bot = find(file.body[i],"\\added_space_bottom")
494 if line_top == -1 and line_bot == -1 and pb_bot == -1 and pb_top == -1 and vspace_top == -1 and vspace_bot == -1:
497 for tag in "\\line_top", "\\line_bottom", "\\pagebreak_top", "\\pagebreak_bottom":
498 file.body[i] = replace(file.body[i], tag, "")
501 # the position could be change because of the removal of other
502 # paragraph properties above
503 vspace_top = find(file.body[i],"\\added_space_top")
504 tmp_list = split(file.body[i][vspace_top:])
505 vspace_top_value = tmp_list[1]
506 file.body[i] = file.body[i][:vspace_top] + join(tmp_list[2:])
509 # the position could be change because of the removal of other
510 # paragraph properties above
511 vspace_bot = find(file.body[i],"\\added_space_bottom")
512 tmp_list = split(file.body[i][vspace_bot:])
513 vspace_bot_value = tmp_list[1]
514 file.body[i] = file.body[i][:vspace_bot] + join(tmp_list[2:])
516 file.body[i] = strip(file.body[i])
519 # Create an empty paragraph for line and page break that belong
520 # above the paragraph
521 if pb_top !=-1 or line_top != -1 or vspace_bot != -1:
523 paragraph_above = ['','\\begin_layout Standard','','']
526 paragraph_above.extend(['\\newpage ',''])
529 paragraph_above.extend(['\\begin_inset VSpace ' + vspace_top_value,'\\end_inset','',''])
532 paragraph_above.extend(['\\lyxline ',''])
534 paragraph_above.extend(['\\end_layout',''])
536 #inset new paragraph above the current paragraph
537 file.body[i-2:i-2] = paragraph_above
538 i = i + len(paragraph_above)
540 # Ensure that nested style are converted later.
541 k = find_end_of(file.body, i, "\\begin_layout", "\\end_layout")
546 if pb_top !=-1 or line_top != -1 or vspace_bot != -1:
548 paragraph_bellow = ['','\\begin_layout Standard','','']
551 paragraph_bellow.extend(['\\lyxline ',''])
554 paragraph_bellow.extend(['\\begin_inset VSpace ' + vspace_bot_value,'\\end_inset','',''])
557 paragraph_bellow.extend(['\\newpage ',''])
559 paragraph_bellow.extend(['\\end_layout',''])
561 #inset new paragraph above the current paragraph
562 file.body[k + 1: k + 1] = paragraph_bellow
568 def convert_note(file):
571 i = find_tokens(file.body, ["\\begin_inset Note",
572 "\\begin_inset Comment",
573 "\\begin_inset Greyedout"], i)
577 file.body[i] = file.body[i][0:13] + 'Note ' + file.body[i][13:]
581 def revert_note(file):
582 note_header = "\\begin_inset Note "
585 i = find_token(file.body, note_header, i)
589 file.body[i] = "\\begin_inset " + file.body[i][len(note_header):]
596 def convert_box(file):
599 i = find_tokens(file.body, ["\\begin_inset Boxed",
600 "\\begin_inset Doublebox",
601 "\\begin_inset Frameless",
602 "\\begin_inset ovalbox",
603 "\\begin_inset Ovalbox",
604 "\\begin_inset Shadowbox"], i)
608 file.body[i] = file.body[i][0:13] + 'Box ' + file.body[i][13:]
612 def revert_box(file):
613 box_header = "\\begin_inset Box "
616 i = find_token(file.body, box_header, i)
620 file.body[i] = "\\begin_inset " + file.body[i][len(box_header):]
627 def convert_collapsable(file):
630 i = find_tokens(file.body, ["\\begin_inset Box",
631 "\\begin_inset Branch",
632 "\\begin_inset CharStyle",
633 "\\begin_inset Float",
634 "\\begin_inset Foot",
635 "\\begin_inset Marginal",
636 "\\begin_inset Note",
637 "\\begin_inset OptArg",
638 "\\begin_inset Wrap"], i)
642 # Seach for a line starting 'collapsed'
643 # If, however, we find a line starting '\begin_layout'
644 # (_always_ present) then break with a warning message
647 if (file.body[i] == "collapsed false"):
648 file.body[i] = "status open"
650 elif (file.body[i] == "collapsed true"):
651 file.body[i] = "status collapsed"
653 elif (file.body[i][:13] == "\\begin_layout"):
654 file.warning("Malformed LyX file.")
661 def revert_collapsable(file):
664 i = find_tokens(file.body, ["\\begin_inset Box",
665 "\\begin_inset Branch",
666 "\\begin_inset CharStyle",
667 "\\begin_inset Float",
668 "\\begin_inset Foot",
669 "\\begin_inset Marginal",
670 "\\begin_inset Note",
671 "\\begin_inset OptArg",
672 "\\begin_inset Wrap"], i)
676 # Seach for a line starting 'status'
677 # If, however, we find a line starting '\begin_layout'
678 # (_always_ present) then break with a warning message
681 if (file.body[i] == "status open"):
682 file.body[i] = "collapsed false"
684 elif (file.body[i] == "status collapsed" or
685 file.body[i] == "status inlined"):
686 file.body[i] = "collapsed true"
688 elif (file.body[i][:13] == "\\begin_layout"):
689 file.warning("Malformed LyX file.")
699 def convert_ert(file):
702 i = find_token(file.body, "\\begin_inset ERT", i)
706 # Seach for a line starting 'status'
707 # If, however, we find a line starting '\begin_layout'
708 # (_always_ present) then break with a warning message
711 if (file.body[i] == "status Open"):
712 file.body[i] = "status open"
714 elif (file.body[i] == "status Collapsed"):
715 file.body[i] = "status collapsed"
717 elif (file.body[i] == "status Inlined"):
718 file.body[i] = "status inlined"
720 elif (file.body[i][:13] == "\\begin_layout"):
721 file.warning("Malformed LyX file.")
728 def revert_ert(file):
731 i = find_token(file.body, "\\begin_inset ERT", i)
735 # Seach for a line starting 'status'
736 # If, however, we find a line starting '\begin_layout'
737 # (_always_ present) then break with a warning message
740 if (file.body[i] == "status open"):
741 file.body[i] = "status Open"
743 elif (file.body[i] == "status collapsed"):
744 file.body[i] = "status Collapsed"
746 elif (file.body[i] == "status inlined"):
747 file.body[i] = "status Inlined"
749 elif (file.body[i][:13] == "\\begin_layout"):
750 file.warning("Malformed LyX file.")
760 def convert_minipage(file):
761 """ Convert minipages to the box inset.
762 We try to use the same order of arguments as lyx does.
765 inner_pos = ["c","t","b","s"]
769 i = find_token(file.body, "\\begin_inset Minipage", i)
773 file.body[i] = "\\begin_inset Box Frameless"
776 # convert old to new position using the pos list
777 if file.body[i][:8] == "position":
778 file.body[i] = 'position "%s"' % pos[int(file.body[i][9])]
780 file.body.insert(i, 'position "%s"' % pos[0])
783 file.body.insert(i, 'hor_pos "c"')
785 file.body.insert(i, 'has_inner_box 1')
788 # convert the inner_position
789 if file.body[i][:14] == "inner_position":
790 file.body[i] = 'inner_pos "%s"' % inner_pos[int(file.body[i][15])]
792 file.body.insert('inner_pos "%s"' % inner_pos[0])
795 # We need this since the new file format has a height and width
796 # in a different order.
797 if file.body[i][:6] == "height":
798 height = file.body[i][6:]
799 # test for default value of 221 and convert it accordingly
800 if height == ' "0pt"':
806 if file.body[i][:5] == "width":
807 width = file.body[i][5:]
812 if file.body[i][:9] == "collapsed":
813 if file.body[i][9:] == "true":
821 file.body.insert(i, 'use_parbox 0')
823 file.body.insert(i, 'width' + width)
825 file.body.insert(i, 'special "none"')
827 file.body.insert(i, 'height' + height)
829 file.body.insert(i, 'height_special "totalheight"')
831 file.body.insert(i, 'status ' + status)
835 # -------------------------------------------------------------------------------------------
836 # Convert backslashes into valid ERT code, append the converted text to
837 # file.body[i] and return the (maybe incremented) line index i
838 def convert_ertbackslash(body, i, ert):
841 body[i] = body[i] + '\\backslash '
845 body[i] = body[i] + c
849 def convert_vspace(file):
851 # Get default spaceamount
852 i = find_token(file.header, '\\defskip', 0)
854 defskipamount = 'medskip'
856 defskipamount = split(file.header[i])[1]
861 i = find_token(file.body, '\\begin_inset VSpace', i)
864 spaceamount = split(file.body[i])[2]
866 # Are we at the beginning or end of a paragraph?
868 start = get_paragraph(file.body, i) + 1
869 for k in range(start, i):
870 if is_nonempty_line(file.body[k]):
874 j = find_end_of_inset(file.body, i)
876 file.warning("Malformed LyX file: Missing '\\end_inset'.")
879 end = get_next_paragraph(file.body, i)
880 for k in range(j + 1, end):
881 if is_nonempty_line(file.body[k]):
885 # Convert to paragraph formatting if we are at the beginning or end
886 # of a paragraph and the resulting paragraph would not be empty
887 if ((paragraph_start and not paragraph_end) or
888 (paragraph_end and not paragraph_start)):
889 # The order is important: del and insert invalidate some indices
893 file.body.insert(start, '\\added_space_top ' + spaceamount + ' ')
895 file.body.insert(start, '\\added_space_bottom ' + spaceamount + ' ')
899 file.body[i:i+1] = ['\\begin_inset ERT', 'status Collapsed', '',
900 '\\layout Standard', '', '\\backslash ']
902 if spaceamount[-1] == '*':
903 spaceamount = spaceamount[:-1]
908 # Replace defskip by the actual value
909 if spaceamount == 'defskip':
910 spaceamount = defskipamount
912 # LaTeX does not know \\smallskip* etc
914 if spaceamount == 'smallskip':
915 spaceamount = '\\smallskipamount'
916 elif spaceamount == 'medskip':
917 spaceamount = '\\medskipamount'
918 elif spaceamount == 'bigskip':
919 spaceamount = '\\bigskipamount'
920 elif spaceamount == 'vfill':
921 spaceamount = '\\fill'
923 # Finally output the LaTeX code
924 if (spaceamount == 'smallskip' or spaceamount == 'medskip' or
925 spaceamount == 'bigskip' or spaceamount == 'vfill'):
926 file.body.insert(i, spaceamount)
929 file.body.insert(i, 'vspace*{')
931 file.body.insert(i, 'vspace{')
932 i = convert_ertbackslash(file.body, i, spaceamount)
933 file.body[i] = file.body[i] + '}'
937 # Convert a LyX length into valid ERT code and append it to body[i]
938 # Return the (maybe incremented) line index i
939 def convert_ertlen(body, i, len, special):
940 units = {"text%":"\\textwidth", "col%":"\\columnwidth",
941 "page%":"\\pagewidth", "line%":"\\linewidth",
942 "theight%":"\\textheight", "pheight%":"\\pageheight"}
944 # Convert special lengths
945 if special != 'none':
946 len = '%f\\' % len2value(len) + special
948 # Convert LyX units to LaTeX units
949 for unit in units.keys():
950 if find(len, unit) != -1:
951 len = '%f' % (len2value(len) / 100) + units[unit]
954 # Convert backslashes and insert the converted length into body
955 return convert_ertbackslash(body, i, len)
958 # Return the value of len without the unit in numerical form
960 result = re.search('([+-]?[0-9.]+)', len)
962 return float(result.group(1))
963 # No number means 1.0
967 def convert_frameless_box(file):
968 pos = ['t', 'c', 'b']
969 inner_pos = ['c', 't', 'b', 's']
972 i = find_token(file.body, '\\begin_inset Frameless', i)
975 j = find_end_of_inset(file.body, i)
977 file.warning("Malformed LyX file: Missing '\\end_inset'.")
983 params = {'position':'0', 'hor_pos':'c', 'has_inner_box':'1',
984 'inner_pos':'1', 'use_parbox':'0', 'width':'100col%',
985 'special':'none', 'height':'1in',
986 'height_special':'totalheight', 'collapsed':'false'}
987 for key in params.keys():
988 value = replace(get_value(file.body, key, i, j), '"', '')
990 if key == 'position':
991 # convert new to old position: 'position "t"' -> 0
992 value = find_token(pos, value, 0)
995 elif key == 'inner_pos':
996 # convert inner position
997 value = find_token(inner_pos, value, 0)
1002 j = del_token(file.body, key, i, j)
1005 # Convert to minipage or ERT?
1006 # Note that the inner_position and height parameters of a minipage
1007 # inset are ignored and not accessible for the user, although they
1008 # are present in the file format and correctly read in and written.
1009 # Therefore we convert to ERT if they do not have their LaTeX
1010 # defaults. These are:
1011 # - the value of "position" for "inner_pos"
1012 # - "\totalheight" for "height"
1013 if (params['use_parbox'] != '0' or
1014 params['has_inner_box'] != '1' or
1015 params['special'] != 'none' or
1016 inner_pos[params['inner_pos']] != pos[params['position']] or
1017 params['height_special'] != 'totalheight' or
1018 len2value(params['height']) != 1.0):
1021 if params['collapsed'] == 'true':
1022 params['collapsed'] = 'Collapsed'
1024 params['collapsed'] = 'Open'
1025 file.body[i : i] = ['\\begin_inset ERT', 'status ' + params['collapsed'],
1026 '', '\\layout Standard', '', '\\backslash ']
1028 if params['use_parbox'] == '1':
1029 file.body.insert(i, 'parbox')
1031 file.body.insert(i, 'begin{minipage}')
1032 file.body[i] = file.body[i] + '[' + pos[params['position']] + ']['
1033 i = convert_ertlen(file.body, i, params['height'], params['height_special'])
1034 file.body[i] = file.body[i] + '][' + inner_pos[params['inner_pos']] + ']{'
1035 i = convert_ertlen(file.body, i, params['width'], params['special'])
1036 if params['use_parbox'] == '1':
1037 file.body[i] = file.body[i] + '}{'
1039 file.body[i] = file.body[i] + '}'
1041 file.body[i:i] = ['', '\\end_inset']
1043 j = find_end_of_inset(file.body, i)
1045 file.warning("Malformed LyX file: Missing '\\end_inset'.")
1047 file.body[j-1:j-1] = ['\\begin_inset ERT', 'status ' + params['collapsed'],
1048 '', '\\layout Standard', '']
1050 if params['use_parbox'] == '1':
1051 file.body.insert(j, '}')
1053 file.body[j:j] = ['\\backslash ', 'end{minipage}']
1056 # Convert to minipage
1057 file.body[i:i] = ['\\begin_inset Minipage',
1058 'position %d' % params['position'],
1059 'inner_position %d' % params['inner_pos'],
1060 'height "' + params['height'] + '"',
1061 'width "' + params['width'] + '"',
1062 'collapsed ' + params['collapsed']]
1069 def convert_jurabib(file):
1070 i = find_token(file.header, '\\use_numerical_citations', 0)
1072 file.warning("Malformed lyx file: Missing '\\use_numerical_citations'.")
1074 file.header.insert(i + 1, '\\use_jurabib 0')
1077 def revert_jurabib(file):
1078 i = find_token(file.header, '\\use_jurabib', 0)
1080 file.warning("Malformed lyx file: Missing '\\use_jurabib'.")
1082 if get_value(file.header, '\\use_jurabib', 0) != "0":
1083 file.warning("Conversion of '\\use_jurabib = 1' not yet implemented.")
1084 # Don't remove '\\use_jurabib' so that people will get warnings by lyx
1092 def convert_bibtopic(file):
1093 i = find_token(file.header, '\\use_jurabib', 0)
1095 file.warning("Malformed lyx file: Missing '\\use_jurabib'.")
1097 file.header.insert(i + 1, '\\use_bibtopic 0')
1100 def revert_bibtopic(file):
1101 i = find_token(file.header, '\\use_bibtopic', 0)
1103 file.warning("Malformed lyx file: Missing '\\use_bibtopic'.")
1105 if get_value(file.header, '\\use_bibtopic', 0) != "0":
1106 file.warning("Conversion of '\\use_bibtopic = 1' not yet implemented.")
1107 # Don't remove '\\use_jurabib' so that people will get warnings by lyx
1114 def convert_float(file):
1117 i = find_token(file.body, '\\begin_inset Float', i)
1120 # Seach for a line starting 'wide'
1121 # If, however, we find a line starting '\begin_layout'
1122 # (_always_ present) then break with a warning message
1125 if (file.body[i][:4] == "wide"):
1126 file.body.insert(i + 1, 'sideways false')
1128 elif (file.body[i][:13] == "\\begin_layout"):
1129 file.warning("Malformed lyx file.")
1135 def revert_float(file):
1138 i = find_token(file.body, '\\begin_inset Float', i)
1141 j = find_end_of_inset(file.body, i)
1143 file.warning("Malformed lyx file: Missing '\\end_inset'.")
1146 if get_value(file.body, 'sideways', i, j) != "false":
1147 file.warning("Conversion of 'sideways true' not yet implemented.")
1148 # Don't remove 'sideways' so that people will get warnings by lyx
1151 del_token(file.body, 'sideways', i, j)
1155 def convert_graphics(file):
1156 """ Add extension to filenames of insetgraphics if necessary.
1160 i = find_token(file.body, "\\begin_inset Graphics", i)
1164 j = find_token2(file.body, "filename", i)
1168 filename = split(file.body[j])[1]
1169 absname = os.path.normpath(os.path.join(file.dir, filename))
1170 if file.input == stdin and not os.path.isabs(filename):
1171 # We don't know the directory and cannot check the file.
1172 # We could use a heuristic and take the current directory,
1173 # and we could try to find out if filename has an extension,
1174 # but that would be just guesses and could be wrong.
1175 file.warning("""Warning: Can not determine whether file
1177 needs an extension when reading from standard input.
1178 You may need to correct the file manually or run
1179 lyx2lyx again with the .lyx file as commandline argument.""" % filename)
1181 # This needs to be the same algorithm as in pre 233 insetgraphics
1182 if access(absname, F_OK):
1184 if access(absname + ".ps", F_OK):
1185 file.body[j] = replace(file.body[j], filename, filename + ".ps")
1187 if access(absname + ".eps", F_OK):
1188 file.body[j] = replace(file.body[j], filename, filename + ".eps")
1192 # Convert firstname and surname from styles -> char styles
1194 def convert_names(file):
1195 """ Convert in the docbook backend from firstname and surname style
1198 if file.backend != "docbook":
1204 i = find_token(file.body, "\\begin_layout Author", i)
1209 while file.body[i] == "":
1212 if file.body[i][:11] != "\\end_layout" or file.body[i+2][:13] != "\\begin_deeper":
1217 i = find_end_of( file.body, i+3, "\\begin_deeper","\\end_deeper")
1219 # something is really wrong, abort
1220 file.warning("Missing \\end_deeper, after style Author.")
1221 file.warning("Aborted attempt to parse FirstName and Surname.")
1223 firstname, surname = "", ""
1225 name = file.body[k:i]
1227 j = find_token(name, "\\begin_layout FirstName", 0)
1230 while(name[j] != "\\end_layout"):
1231 firstname = firstname + name[j]
1234 j = find_token(name, "\\begin_layout Surname", 0)
1237 while(name[j] != "\\end_layout"):
1238 surname = surname + name[j]
1242 del file.body[k+2:i+1]
1244 file.body[k-1:k-1] = ["", "",
1245 "\\begin_inset CharStyle Firstname",
1248 "\\begin_layout Standard",
1256 "\\begin_inset CharStyle Surname",
1259 "\\begin_layout Standard",
1268 def revert_names(file):
1269 """ Revert in the docbook backend from firstname and surname char style
1272 if file.backend != "docbook":
1277 # \use_natbib 1 \cite_engine <style>
1278 # \use_numerical_citations 0 -> where <style> is one of
1279 # \use_jurabib 0 "basic", "natbib_authoryear",
1280 # "natbib_numerical" or "jurabib"
1281 def convert_cite_engine(file):
1282 a = find_token(file.header, "\\use_natbib", 0)
1284 file.warning("Malformed lyx file: Missing '\\use_natbib'.")
1287 b = find_token(file.header, "\\use_numerical_citations", 0)
1288 if b == -1 or b != a+1:
1289 file.warning("Malformed lyx file: Missing '\\use_numerical_citations'.")
1292 c = find_token(file.header, "\\use_jurabib", 0)
1293 if c == -1 or c != b+1:
1294 file.warning("Malformed lyx file: Missing '\\use_jurabib'.")
1297 use_natbib = int(split(file.header[a])[1])
1298 use_numerical_citations = int(split(file.header[b])[1])
1299 use_jurabib = int(split(file.header[c])[1])
1301 cite_engine = "basic"
1303 if use_numerical_citations:
1304 cite_engine = "natbib_numerical"
1306 cite_engine = "natbib_authoryear"
1308 cite_engine = "jurabib"
1310 del file.header[a:c+1]
1311 file.header.insert(a, "\\cite_engine " + cite_engine)
1314 def revert_cite_engine(file):
1315 i = find_token(file.header, "\\cite_engine", 0)
1317 file.warning("Malformed lyx file: Missing '\\cite_engine'.")
1320 cite_engine = split(file.header[i])[1]
1325 if cite_engine == "natbib_numerical":
1328 elif cite_engine == "natbib_authoryear":
1330 elif cite_engine == "jurabib":
1334 file.header.insert(i, "\\use_jurabib " + use_jurabib)
1335 file.header.insert(i, "\\use_numerical_citations " + use_numerical)
1336 file.header.insert(i, "\\use_natbib " + use_natbib)
1342 def convert_paperpackage(file):
1343 i = find_token(file.header, "\\paperpackage", 0)
1345 file.warning("Malformed lyx file: Missing '\\paperpackage'.")
1348 packages = {'a4':'none', 'a4wide':'a4', 'widemarginsa4':'a4wide'}
1349 paperpackage = split(file.header[i])[1]
1350 file.header[i] = replace(file.header[i], paperpackage, packages[paperpackage])
1353 def revert_paperpackage(file):
1354 i = find_token(file.header, "\\paperpackage", 0)
1356 file.warning("Malformed lyx file: Missing '\\paperpackage'.")
1359 packages = {'none':'a4', 'a4':'a4wide', 'a4wide':'widemarginsa4',
1361 paperpackage = split(file.header[i])[1]
1362 file.header[i] = replace(file.header[i], paperpackage, packages[paperpackage])
1368 def convert_bullets(file):
1371 i = find_token(file.header, "\\bullet", i)
1374 if file.header[i][:12] == '\\bulletLaTeX':
1375 file.header[i] = file.header[i] + ' ' + strip(file.header[i+1])
1378 file.header[i] = file.header[i] + ' ' + strip(file.header[i+1]) +\
1379 ' ' + strip(file.header[i+2]) + ' ' + strip(file.header[i+3])
1381 del file.header[i+1:i + n]
1385 def revert_bullets(file):
1388 i = find_token(file.header, "\\bullet", i)
1391 if file.header[i][:12] == '\\bulletLaTeX':
1392 n = find(file.header[i], '"')
1394 file.warning("Malformed header.")
1397 file.header[i:i+1] = [file.header[i][:n-1],'\t' + file.header[i][n:], '\\end_bullet']
1400 frag = split(file.header[i])
1402 file.warning("Malformed header.")
1405 file.header[i:i+1] = [frag[0] + ' ' + frag[1],
1414 # \begin_header and \begin_document
1416 def add_begin_header(file):
1417 i = find_token(file.header, '\\lyxformat', 0)
1418 file.header.insert(i+1, '\\begin_header')
1419 file.header.insert(i+1, '\\begin_document')
1422 def remove_begin_header(file):
1423 i = find_token(file.header, "\\begin_document", 0)
1426 i = find_token(file.header, "\\begin_header", 0)
1432 # \begin_file.body and \end_file.body
1434 def add_begin_body(file):
1435 file.body.insert(0, '\\begin_body')
1436 file.body.insert(1, '')
1437 i = find_token(file.body, "\\end_document", 0)
1438 file.body.insert(i, '\\end_body')
1440 def remove_begin_body(file):
1441 i = find_token(file.body, "\\begin_body", 0)
1444 if not file.body[i]:
1446 i = find_token(file.body, "\\end_body", 0)
1454 def normalize_papersize(file):
1455 i = find_token(file.header, '\\papersize', 0)
1459 tmp = split(file.header[i])
1460 if tmp[1] == "Default":
1461 file.header[i] = '\\papersize default'
1463 if tmp[1] == "Custom":
1464 file.header[i] = '\\papersize custom'
1467 def denormalize_papersize(file):
1468 i = find_token(file.header, '\\papersize', 0)
1472 tmp = split(file.header[i])
1473 if tmp[1] == "custom":
1474 file.header[i] = '\\papersize Custom'
1478 # Strip spaces at end of command line
1480 def strip_end_space(file):
1481 for i in range(len(file.body)):
1482 if file.body[i][:1] == '\\':
1483 file.body[i] = strip(file.body[i])
1487 # Use boolean values for \use_geometry, \use_bibtopic and \tracking_changes
1489 def use_x_boolean(file):
1490 bin2bool = {'0': 'false', '1': 'true'}
1491 for use in '\\use_geometry', '\\use_bibtopic', '\\tracking_changes':
1492 i = find_token(file.header, use, 0)
1495 decompose = split(file.header[i])
1496 file.header[i] = decompose[0] + ' ' + bin2bool[decompose[1]]
1499 def use_x_binary(file):
1500 bool2bin = {'false': '0', 'true': '1'}
1501 for use in '\\use_geometry', '\\use_bibtopic', '\\tracking_changes':
1502 i = find_token(file.header, use, 0)
1505 decompose = split(file.header[i])
1506 file.header[i] = decompose[0] + ' ' + bool2bin[decompose[1]]
1512 table = { 223 : [insert_tracking_changes, add_end_header, remove_color_default,
1513 convert_spaces, convert_bibtex, remove_insetparent],
1514 224 : [convert_external, convert_comment],
1515 225 : [add_end_layout, layout2begin_layout, convert_end_document,
1516 convert_table_valignment_middle, convert_breaks],
1517 226 : [convert_note],
1518 227 : [convert_box],
1519 228 : [convert_collapsable, convert_ert],
1520 229 : [convert_minipage],
1521 230 : [convert_jurabib],
1522 231 : [convert_float],
1523 232 : [convert_bibtopic],
1524 233 : [convert_graphics, convert_names],
1525 234 : [convert_cite_engine],
1526 235 : [convert_paperpackage],
1527 236 : [convert_bullets, add_begin_header, add_begin_body,
1528 normalize_papersize, strip_end_space],
1529 237 : [use_x_boolean]}
1531 chain = table.keys()
1534 for version in chain:
1535 if file.format >= version:
1537 for convert in table[version]:
1539 file.format = version
1540 if file.end_format == file.format:
1545 table = { 236: [use_x_binary],
1546 235: [denormalize_papersize, remove_begin_body,remove_begin_header,
1548 234: [revert_paperpackage],
1549 233: [revert_cite_engine],
1550 232: [revert_names],
1551 231: [revert_bibtopic],
1552 230: [revert_float],
1553 229: [revert_jurabib],
1555 227: [revert_collapsable, revert_ert],
1556 226: [revert_box, revert_external_2],
1558 224: [rm_end_layout, begin_layout2layout, revert_end_document,
1559 revert_valignment_middle, convert_vspace, convert_frameless_box],
1560 223: [revert_external_2, revert_comment],
1561 221: [rm_end_header, revert_spaces, revert_bibtex,
1562 rm_tracking_changes, rm_body_changes]}
1564 chain = table.keys()
1568 for version in chain:
1569 if file.format <= version:
1571 for convert in table[version]:
1573 file.format = version
1574 if file.end_format == file.format:
1577 if __name__ == "__main__":