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>
5 # Copyright (C) 2004-2005 Georg Baum <Georg.Baum@post.rwth-aachen.de>
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # as published by the Free Software Foundation; either version 2
10 # of the License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 from os import access, F_OK
24 from parser_tools import find_token, find_end_of_inset, get_next_paragraph, \
25 get_paragraph, get_value, del_token, is_nonempty_line,\
26 find_tokens, find_end_of, find_token2, find_re
28 from string import replace, split, find, strip, join
30 from lyx_0_12 import update_latexaccents
33 # Remove \color default
35 def remove_color_default(file):
38 i = find_token(file.body, "\\color default", i)
41 file.body[i] = replace(file.body[i], "\\color default",
48 def add_end_header(file):
49 file.header.append("\\end_header");
52 def rm_end_header(file):
53 i = find_token(file.header, "\\end_header", 0)
60 # \SpecialChar ~ -> \InsetSpace ~
62 def convert_spaces(file):
63 for i in range(len(file.body)):
64 file.body[i] = replace(file.body[i],"\\SpecialChar ~","\\InsetSpace ~")
67 def revert_spaces(file):
68 for i in range(len(file.body)):
69 file.body[i] = replace(file.body[i],"\\InsetSpace ~", "\\SpecialChar ~")
73 # equivalent to lyx::support::escape()
75 def lyx_support_escape(lab):
76 hexdigit = ['0', '1', '2', '3', '4', '5', '6', '7',
77 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
81 if o >= 128 or c == '=' or c == '%':
83 enc = enc + hexdigit[o >> 4]
84 enc = enc + hexdigit[o & 15]
91 # \begin_inset LatexCommand \eqref -> ERT
93 def revert_eqref(file):
94 regexp = re.compile(r'^\\begin_inset\s+LatexCommand\s+\\eqref')
97 i = find_re(file.body, regexp, i)
100 eqref = lyx_support_escape(regexp.sub("", file.body[i]))
101 file.body[i:i+1] = ["\\begin_inset ERT", "status Collapsed", "",
102 "\\layout Standard", "", "\\backslash ",
110 def convert_bibtex(file):
111 for i in range(len(file.body)):
112 file.body[i] = replace(file.body[i],"\\begin_inset LatexCommand \\BibTeX",
113 "\\begin_inset LatexCommand \\bibtex")
116 def revert_bibtex(file):
117 for i in range(len(file.body)):
118 file.body[i] = replace(file.body[i], "\\begin_inset LatexCommand \\bibtex",
119 "\\begin_inset LatexCommand \\BibTeX")
125 def remove_insetparent(file):
128 i = find_token(file.body, "\\begin_inset LatexCommand \\lyxparent", i)
137 def convert_external(file):
138 external_rexp = re.compile(r'\\begin_inset External ([^,]*),"([^"]*)",')
139 external_header = "\\begin_inset External"
142 i = find_token(file.body, external_header, i)
145 look = external_rexp.search(file.body[i])
148 args[0] = look.group(1)
149 args[1] = look.group(2)
150 #FIXME: if the previous search fails then warn
152 if args[0] == "RasterImage":
153 # Convert a RasterImage External Inset to a Graphics Inset.
154 top = "\\begin_inset Graphics"
156 filename = "\tfilename " + args[1]
157 file.body[i:i+1] = [top, filename]
160 # Convert the old External Inset format to the new.
161 top = external_header
162 template = "\ttemplate " + args[0]
164 filename = "\tfilename " + args[1]
165 file.body[i:i+1] = [top, template, filename]
168 file.body[i:i+1] = [top, template]
172 def revert_external_1(file):
173 external_header = "\\begin_inset External"
176 i = find_token(file.body, external_header, i)
180 template = split(file.body[i+1])
184 filename = split(file.body[i+1])
188 params = split(file.body[i+1])
190 if file.body[i+1]: del file.body[i+1]
192 file.body[i] = file.body[i] + " " + template[0]+ ', "' + filename[0] + '", " '+ join(params[1:]) + '"'
196 def revert_external_2(file):
197 draft_token = '\tdraft'
200 i = find_token(file.body, '\\begin_inset External', i)
203 j = find_end_of_inset(file.body, i + 1)
205 #this should not happen
207 k = find_token(file.body, draft_token, i+1, j-1)
208 if (k != -1 and len(draft_token) == len(file.body[k])):
216 def convert_comment(file):
218 comment = "\\layout Comment"
220 i = find_token(file.body, comment, i)
224 file.body[i:i+1] = ["\\layout Standard","","",
225 "\\begin_inset Comment",
232 i = find_token(file.body, "\\layout", i)
234 i = len(file.body) - 1
235 file.body[i:i] = ["\\end_inset","",""]
238 j = find_token(file.body, '\\begin_deeper', old_i, i)
239 if j == -1: j = i + 1
240 k = find_token(file.body, '\\begin_inset', old_i, i)
241 if k == -1: k = i + 1
246 i = find_end_of( file.body, i, "\\begin_deeper","\\end_deeper")
248 #This case should not happen
249 #but if this happens deal with it greacefully adding
250 #the missing \end_deeper.
251 i = len(file.body) - 1
252 file.body[i:i] = ["\end_deeper",""]
260 i = find_end_of( file.body, i, "\\begin_inset","\\end_inset")
262 #This case should not happen
263 #but if this happens deal with it greacefully adding
264 #the missing \end_inset.
265 i = len(file.body) - 1
266 file.body[i:i] = ["\\end_inset","","","\\end_inset","",""]
272 if find(file.body[i], comment) == -1:
273 file.body[i:i] = ["\\end_inset"]
276 file.body[i:i+1] = ["\\layout Standard"]
280 def revert_comment(file):
283 i = find_tokens(file.body, ["\\begin_inset Comment", "\\begin_inset Greyedout"], i)
287 file.body[i] = "\\begin_inset Note"
294 def add_end_layout(file):
295 i = find_token(file.body, '\\layout', 0)
301 struct_stack = ["\\layout"]
304 i = find_tokens(file.body, ["\\begin_inset", "\\end_inset", "\\layout",
305 "\\begin_deeper", "\\end_deeper", "\\the_end"], i)
308 token = split(file.body[i])[0]
310 file.warning("Truncated file.")
312 file.body.insert(i, '\\the_end')
315 if token == "\\begin_inset":
316 struct_stack.append(token)
320 if token == "\\end_inset":
321 tail = struct_stack.pop()
322 if tail == "\\layout":
323 file.body.insert(i,"")
324 file.body.insert(i,"\\end_layout")
326 #Check if it is the correct tag
331 if token == "\\layout":
332 tail = struct_stack.pop()
334 file.body.insert(i,"")
335 file.body.insert(i,"\\end_layout")
338 struct_stack.append(tail)
340 struct_stack.append(token)
343 if token == "\\begin_deeper":
344 file.body.insert(i,"")
345 file.body.insert(i,"\\end_layout")
347 struct_stack.append(token)
350 if token == "\\end_deeper":
351 if struct_stack[-1] == '\\layout':
352 file.body.insert(i, '\\end_layout')
359 file.body.insert(i, "")
360 file.body.insert(i, "\\end_layout")
364 def rm_end_layout(file):
367 i = find_token(file.body, '\\end_layout', i)
376 # Handle change tracking keywords
378 def insert_tracking_changes(file):
379 i = find_token(file.header, "\\tracking_changes", 0)
381 file.header.append("\\tracking_changes 0")
384 def rm_tracking_changes(file):
385 i = find_token(file.header, "\\author", 0)
389 i = find_token(file.header, "\\tracking_changes", 0)
395 def rm_body_changes(file):
398 i = find_token(file.body, "\\change_", i)
406 # \layout -> \begin_layout
408 def layout2begin_layout(file):
411 i = find_token(file.body, '\\layout', i)
415 file.body[i] = replace(file.body[i], '\\layout', '\\begin_layout')
419 def begin_layout2layout(file):
422 i = find_token(file.body, '\\begin_layout', i)
426 file.body[i] = replace(file.body[i], '\\begin_layout', '\\layout')
431 # valignment="center" -> valignment="middle"
433 def convert_valignment_middle(body, start, end):
434 for i in range(start, end):
435 if re.search('^<(column|cell) .*valignment="center".*>$', body[i]):
436 body[i] = replace(body[i], 'valignment="center"', 'valignment="middle"')
439 def convert_table_valignment_middle(file):
440 regexp = re.compile(r'^\\begin_inset\s+Tabular')
443 i = find_re(file.body, regexp, i)
446 j = find_end_of_inset(file.body, i + 1)
448 #this should not happen
449 convert_valignment_middle(file.body, i + 1, len(file.body))
451 convert_valignment_middle(file.body, i + 1, j)
455 def revert_table_valignment_middle(body, start, end):
456 for i in range(start, end):
457 if re.search('^<(column|cell) .*valignment="middle".*>$', body[i]):
458 body[i] = replace(body[i], 'valignment="middle"', 'valignment="center"')
461 def revert_valignment_middle(file):
462 regexp = re.compile(r'^\\begin_inset\s+Tabular')
465 i = find_re(file.body, regexp, i)
468 j = find_end_of_inset(file.body, i + 1)
470 #this should not happen
471 revert_table_valignment_middle(file.body, i + 1, len(file.body))
473 revert_table_valignment_middle(file.body, i + 1, j)
478 # \the_end -> \end_document
480 def convert_end_document(file):
481 i = find_token(file.body, "\\the_end", 0)
483 file.body.append("\\end_document")
485 file.body[i] = "\\end_document"
488 def revert_end_document(file):
489 i = find_token(file.body, "\\end_document", 0)
491 file.body.append("\\the_end")
493 file.body[i] = "\\the_end"
497 # Convert line and page breaks
500 #\line_top \line_bottom \pagebreak_top \pagebreak_bottom \added_space_top xxx \added_space_bottom yyy
504 #\begin layout Standard
509 #\begin_inset VSpace xxx
513 #\begin_layout Standard
517 #\begin_layout Standard
519 #\begin_inset VSpace xxx
526 def convert_breaks(file):
527 par_params = ('added_space_bottom', 'added_space_top', 'align',
528 'labelwidthstring', 'line_bottom', 'line_top', 'noindent',
529 'pagebreak_bottom', 'pagebreak_top', 'paragraph_spacing',
533 i = find_token(file.body, "\\begin_layout", i)
538 # Merge all paragraph parameters into a single line
539 # We cannot check for '\\' only because paragraphs may start e.g.
541 while file.body[i + 1][:1] == '\\' and split(file.body[i + 1][1:])[0] in par_params:
542 file.body[i] = file.body[i + 1] + ' ' + file.body[i]
545 line_top = find(file.body[i],"\\line_top")
546 line_bot = find(file.body[i],"\\line_bottom")
547 pb_top = find(file.body[i],"\\pagebreak_top")
548 pb_bot = find(file.body[i],"\\pagebreak_bottom")
549 vspace_top = find(file.body[i],"\\added_space_top")
550 vspace_bot = find(file.body[i],"\\added_space_bottom")
552 if line_top == -1 and line_bot == -1 and pb_bot == -1 and pb_top == -1 and vspace_top == -1 and vspace_bot == -1:
555 for tag in "\\line_top", "\\line_bottom", "\\pagebreak_top", "\\pagebreak_bottom":
556 file.body[i] = replace(file.body[i], tag, "")
559 # the position could be change because of the removal of other
560 # paragraph properties above
561 vspace_top = find(file.body[i],"\\added_space_top")
562 tmp_list = split(file.body[i][vspace_top:])
563 vspace_top_value = tmp_list[1]
564 file.body[i] = file.body[i][:vspace_top] + join(tmp_list[2:])
567 # the position could be change because of the removal of other
568 # paragraph properties above
569 vspace_bot = find(file.body[i],"\\added_space_bottom")
570 tmp_list = split(file.body[i][vspace_bot:])
571 vspace_bot_value = tmp_list[1]
572 file.body[i] = file.body[i][:vspace_bot] + join(tmp_list[2:])
574 file.body[i] = strip(file.body[i])
577 # Create an empty paragraph for line and page break that belong
578 # above the paragraph
579 if pb_top !=-1 or line_top != -1 or vspace_top != -1:
581 paragraph_above = ['','\\begin_layout Standard','','']
584 paragraph_above.extend(['\\newpage ',''])
587 paragraph_above.extend(['\\begin_inset VSpace ' + vspace_top_value,'\\end_inset','',''])
590 paragraph_above.extend(['\\lyxline ',''])
592 paragraph_above.extend(['\\end_layout',''])
594 #inset new paragraph above the current paragraph
595 file.body[i-2:i-2] = paragraph_above
596 i = i + len(paragraph_above)
598 # Ensure that nested style are converted later.
599 k = find_end_of(file.body, i, "\\begin_layout", "\\end_layout")
604 if pb_bot !=-1 or line_bot != -1 or vspace_bot != -1:
606 paragraph_below = ['','\\begin_layout Standard','','']
609 paragraph_below.extend(['\\lyxline ',''])
612 paragraph_below.extend(['\\begin_inset VSpace ' + vspace_bot_value,'\\end_inset','',''])
615 paragraph_below.extend(['\\newpage ',''])
617 paragraph_below.extend(['\\end_layout',''])
619 #inset new paragraph above the current paragraph
620 file.body[k + 1: k + 1] = paragraph_below
626 def convert_note(file):
629 i = find_tokens(file.body, ["\\begin_inset Note",
630 "\\begin_inset Comment",
631 "\\begin_inset Greyedout"], i)
635 file.body[i] = file.body[i][0:13] + 'Note ' + file.body[i][13:]
639 def revert_note(file):
640 note_header = "\\begin_inset Note "
643 i = find_token(file.body, note_header, i)
647 file.body[i] = "\\begin_inset " + file.body[i][len(note_header):]
654 def convert_box(file):
657 i = find_tokens(file.body, ["\\begin_inset Boxed",
658 "\\begin_inset Doublebox",
659 "\\begin_inset Frameless",
660 "\\begin_inset ovalbox",
661 "\\begin_inset Ovalbox",
662 "\\begin_inset Shadowbox"], i)
666 file.body[i] = file.body[i][0:13] + 'Box ' + file.body[i][13:]
670 def revert_box(file):
671 box_header = "\\begin_inset Box "
674 i = find_token(file.body, box_header, i)
678 file.body[i] = "\\begin_inset " + file.body[i][len(box_header):]
685 def convert_collapsable(file):
688 i = find_tokens(file.body, ["\\begin_inset Box",
689 "\\begin_inset Branch",
690 "\\begin_inset CharStyle",
691 "\\begin_inset Float",
692 "\\begin_inset Foot",
693 "\\begin_inset Marginal",
694 "\\begin_inset Note",
695 "\\begin_inset OptArg",
696 "\\begin_inset Wrap"], i)
700 # Seach for a line starting 'collapsed'
701 # If, however, we find a line starting '\begin_layout'
702 # (_always_ present) then break with a warning message
705 if (file.body[i] == "collapsed false"):
706 file.body[i] = "status open"
708 elif (file.body[i] == "collapsed true"):
709 file.body[i] = "status collapsed"
711 elif (file.body[i][:13] == "\\begin_layout"):
712 file.warning("Malformed LyX file: Missing 'collapsed'.")
719 def revert_collapsable(file):
722 i = find_tokens(file.body, ["\\begin_inset Box",
723 "\\begin_inset Branch",
724 "\\begin_inset CharStyle",
725 "\\begin_inset Float",
726 "\\begin_inset Foot",
727 "\\begin_inset Marginal",
728 "\\begin_inset Note",
729 "\\begin_inset OptArg",
730 "\\begin_inset Wrap"], i)
734 # Seach for a line starting 'status'
735 # If, however, we find a line starting '\begin_layout'
736 # (_always_ present) then break with a warning message
739 if (file.body[i] == "status open"):
740 file.body[i] = "collapsed false"
742 elif (file.body[i] == "status collapsed" or
743 file.body[i] == "status inlined"):
744 file.body[i] = "collapsed true"
746 elif (file.body[i][:13] == "\\begin_layout"):
747 file.warning("Malformed LyX file: Missing 'status'.")
757 def convert_ert(file):
760 i = find_token(file.body, "\\begin_inset ERT", i)
764 # Seach for a line starting 'status'
765 # If, however, we find a line starting '\begin_layout'
766 # (_always_ present) then break with a warning message
769 if (file.body[i] == "status Open"):
770 file.body[i] = "status open"
772 elif (file.body[i] == "status Collapsed"):
773 file.body[i] = "status collapsed"
775 elif (file.body[i] == "status Inlined"):
776 file.body[i] = "status inlined"
778 elif (file.body[i][:13] == "\\begin_layout"):
779 file.warning("Malformed LyX file: Missing 'status'.")
786 def revert_ert(file):
789 i = find_token(file.body, "\\begin_inset ERT", i)
793 # Seach for a line starting 'status'
794 # If, however, we find a line starting '\begin_layout'
795 # (_always_ present) then break with a warning message
798 if (file.body[i] == "status open"):
799 file.body[i] = "status Open"
801 elif (file.body[i] == "status collapsed"):
802 file.body[i] = "status Collapsed"
804 elif (file.body[i] == "status inlined"):
805 file.body[i] = "status Inlined"
807 elif (file.body[i][:13] == "\\begin_layout"):
808 file.warning("Malformed LyX file : Missing 'status'.")
818 def convert_minipage(file):
819 """ Convert minipages to the box inset.
820 We try to use the same order of arguments as lyx does.
823 inner_pos = ["c","t","b","s"]
827 i = find_token(file.body, "\\begin_inset Minipage", i)
831 file.body[i] = "\\begin_inset Box Frameless"
834 # convert old to new position using the pos list
835 if file.body[i][:8] == "position":
836 file.body[i] = 'position "%s"' % pos[int(file.body[i][9])]
838 file.body.insert(i, 'position "%s"' % pos[0])
841 file.body.insert(i, 'hor_pos "c"')
843 file.body.insert(i, 'has_inner_box 1')
846 # convert the inner_position
847 if file.body[i][:14] == "inner_position":
848 file.body[i] = 'inner_pos "%s"' % inner_pos[int(file.body[i][15])]
850 file.body.insert('inner_pos "%s"' % inner_pos[0])
853 # We need this since the new file format has a height and width
854 # in a different order.
855 if file.body[i][:6] == "height":
856 height = file.body[i][6:]
857 # test for default value of 221 and convert it accordingly
858 if height == ' "0pt"' or height == ' "0"':
864 if file.body[i][:5] == "width":
865 width = file.body[i][5:]
870 if file.body[i][:9] == "collapsed":
871 if file.body[i][9:] == "true":
879 file.body.insert(i, 'use_parbox 0')
881 file.body.insert(i, 'width' + width)
883 file.body.insert(i, 'special "none"')
885 file.body.insert(i, 'height' + height)
887 file.body.insert(i, 'height_special "totalheight"')
889 file.body.insert(i, 'status ' + status)
893 # -------------------------------------------------------------------------------------------
894 # Convert backslashes and '\n' into valid ERT code, append the converted
895 # text to body[i] and return the (maybe incremented) line index i
896 def convert_ertbackslash(body, i, ert):
899 body[i] = body[i] + '\\backslash '
903 body[i+1:i+1] = ['\\newline ', '']
906 body[i] = body[i] + c
910 # get all paragraph parameters. They can be all on one line or on several lines.
911 # lines[i] must be the first parameter line
912 def get_par_params(lines, i):
913 par_params = ('added_space_bottom', 'added_space_top', 'align',
914 'labelwidthstring', 'line_bottom', 'line_top', 'noindent',
915 'pagebreak_bottom', 'pagebreak_top', 'paragraph_spacing',
917 # We cannot check for '\\' only because paragraphs may start e.g.
920 while lines[i][:1] == '\\' and split(lines[i][1:])[0] in par_params:
921 params = params + ' ' + strip(lines[i])
926 # Change vspace insets, page breaks and lyxlines to paragraph options
927 # (if possible) or ERT
928 def revert_breaks(file):
930 # Get default spaceamount
931 i = find_token(file.header, '\\defskip', 0)
933 defskipamount = 'medskip'
935 defskipamount = split(file.header[i])[1]
937 keys = {"\\begin_inset" : "vspace", "\\lyxline" : "lyxline",
938 "\\newpage" : "newpage"}
939 keywords_top = {"vspace" : "\\added_space_top", "lyxline" : "\\line_top",
940 "newpage" : "\\pagebreak_top"}
941 keywords_bot = {"vspace" : "\\added_space_bottom", "lyxline" : "\\line_bottom",
942 "newpage" : "\\pagebreak_bottom"}
943 tokens = ["\\begin_inset VSpace", "\\lyxline", "\\newpage"]
948 i = find_tokens(file.body, tokens, i)
953 lines.append(split(file.body[i]))
954 insets.append(keys[lines[0][0]])
956 # Are we at the beginning of a paragraph?
958 start = get_paragraph(file.body, i) + 1
959 params = get_par_params(file.body, start)
960 # Paragraph parameters may be on one or more lines.
961 # Find the start of the real paragraph text.
962 while file.body[start][:1] == '\\' and split(file.body[start])[0] in params:
964 for k in range(start, i):
965 if is_nonempty_line(file.body[k]):
969 # Are we at the end of a paragraph?
971 if insets[0] == "vspace":
972 j = find_end_of_inset(file.body, i)
974 file.warning("Malformed lyx file: Missing '\\end_inset'.")
979 end = get_next_paragraph(file.body, i)
980 for k in range(j + 1, end):
981 if is_nonempty_line(file.body[k]):
985 # Detect paragraphs created by convert_breaks()
989 # Make a copy of the paragraph, since we need to strip empty lines
991 for k in range(start, end):
992 if is_nonempty_line(file.body[k]):
993 if (len(paragraph) > 0 and
994 find_token(file.body, "\\end_inset", k) == k):
995 paragraph[len(paragraph) - 1] = paragraph[len(paragraph) - 1] + ' ' + file.body[k]
997 paragraph.append(file.body[k])
999 # Do we have a second inset to convert in this paragraph?
1000 if ((len(paragraph) == 2 or len(paragraph) == 3) and
1001 find_tokens(paragraph, tokens, 1) == 1):
1002 lines.append(split(paragraph[1]))
1003 insets.append(keys[lines[1][0]])
1005 # Do we have a third inset to convert in this paragraph?
1006 if (len(paragraph) == 2 and
1007 find_tokens(paragraph, tokens, 1) == 1):
1008 lines.append(split(paragraph[1]))
1009 insets.append(keys[lines[2][0]])
1012 if len(paragraph) == 1:
1013 # We have only insets that need to be converted in this paragraph
1014 if ((len(insets) == 3 and insets[0] == "newpage" and
1015 insets[1] == "vspace" and insets[2] == "lyxline") or
1016 (len(insets) == 2 and
1017 ((insets[0] == "newpage" and insets[1] == "vspace") or
1018 (insets[0] == "newpage" and insets[1] == "lyxline") or
1019 (insets[0] == "vspace" and insets[1] == "lyxline")))):
1020 # This paragraph has been created before a paragraph by
1025 if ((len(insets) == 3 and insets[0] == "lyxline" and
1026 insets[1] == "vspace" and insets[2] == "newpage") or
1027 (len(insets) == 2 and
1028 ((insets[0] == "lyxline" and insets[1] == "vspace") or
1029 (insets[0] == "lyxline" and insets[1] == "newpage") or
1030 (insets[0] == "vspace" and insets[1] == "newpage")))):
1031 # This paragraph has been created after a paragraph by
1037 # Determine space amount for vspace insets
1038 spaceamount = list()
1040 for k in range(len(lines)):
1041 if insets[k] == "vspace":
1042 spaceamount.append(lines[k][2])
1043 arguments.append(' ' + spaceamount[k] + ' ')
1045 spaceamount.append('')
1046 arguments.append(' ')
1048 if paragraph_start and paragraph_end:
1049 # We are in a paragraph of our own.
1050 # We must not delete this paragraph if it has parameters
1052 # First try to merge with the previous paragraph.
1053 # We try the previous paragraph first because we would
1054 # otherwise need ERT for two subsequent vspaces.
1055 prev_par = get_paragraph(file.body, start - 2) + 1
1056 if prev_par > 0 and not before:
1057 prev_params = get_par_params(file.body, prev_par + 1)
1061 if keywords_bot[insets[k]] in prev_params:
1065 for k in range(len(insets)):
1066 file.body.insert(prev_par + 1,
1067 keywords_bot[insets[k]] + arguments[k])
1068 del file.body[start-1+n:end+n]
1071 # Then try next paragraph
1073 if next_par > 0 and not after:
1074 next_params = get_par_params(file.body, next_par + 1)
1078 if keywords_top[insets[k]] in next_params:
1082 for k in range(len(insets)):
1083 file.body.insert(next_par + 1,
1084 keywords_top[insets[k]] + arguments[k])
1085 del file.body[start-1:end]
1089 # Convert to paragraph formatting if we are at the beginning or end
1090 # of a paragraph and the resulting paragraph would not be empty
1091 # The order is important: del and insert invalidate some indices
1096 file.body.insert(start, keywords_top[insets[0]] + arguments[0])
1102 file.body.insert(start, keywords_bot[insets[0]] + arguments[0])
1105 # Convert the first inset to ERT.
1106 # The others are converted in the next loop runs (if they exist)
1107 if insets[0] == "vspace":
1108 file.body[i:i+1] = ['\\begin_inset ERT', 'status Collapsed', '',
1109 '\\layout Standard', '', '\\backslash ']
1111 if spaceamount[0][-1] == '*':
1112 spaceamount[0] = spaceamount[0][:-1]
1117 # Replace defskip by the actual value
1118 if spaceamount[0] == 'defskip':
1119 spaceamount[0] = defskipamount
1121 # LaTeX does not know \\smallskip* etc
1123 if spaceamount[0] == 'smallskip':
1124 spaceamount[0] = '\\smallskipamount'
1125 elif spaceamount[0] == 'medskip':
1126 spaceamount[0] = '\\medskipamount'
1127 elif spaceamount[0] == 'bigskip':
1128 spaceamount[0] = '\\bigskipamount'
1129 elif spaceamount[0] == 'vfill':
1130 spaceamount[0] = '\\fill'
1132 # Finally output the LaTeX code
1133 if (spaceamount[0] == 'smallskip' or spaceamount[0] == 'medskip' or
1134 spaceamount[0] == 'bigskip' or spaceamount[0] == 'vfill'):
1135 file.body.insert(i, spaceamount[0] + '{}')
1138 file.body.insert(i, 'vspace*{')
1140 file.body.insert(i, 'vspace{')
1141 i = convert_ertbackslash(file.body, i, spaceamount[0])
1142 file.body[i] = file.body[i] + '}'
1144 elif insets[0] == "lyxline":
1145 file.body[i:i+1] = ['\\begin_inset ERT', 'status Collapsed', '',
1146 '\\layout Standard', '', '\\backslash',
1147 'lyxline{}', '', '\\end_inset', '']
1149 # We use \providecommand so that we don't get an error if native
1150 # lyxlines are used (LyX writes first its own preamble and then
1151 # the user specified one)
1152 add_to_preamble(file,
1153 ['% Commands inserted by lyx2lyx for lyxlines',
1154 '\\providecommand{\\lyxline}[1]{',
1155 ' {#1 \\vspace{1ex} \\hrule width \\columnwidth \\vspace{1ex}}'
1157 elif insets[0] == "newpage":
1158 file.body[i:i+1] = ['\\begin_inset ERT', 'status Collapsed', '',
1159 '\\layout Standard', '', '\\backslash',
1160 'newpage{}', '', '\\end_inset', '']
1164 # Convert a LyX length into a LaTeX length
1165 def convert_len(len, special):
1166 units = {"text%":"\\textwidth", "col%":"\\columnwidth",
1167 "page%":"\\pagewidth", "line%":"\\linewidth",
1168 "theight%":"\\textheight", "pheight%":"\\pageheight"}
1170 # Convert special lengths
1171 if special != 'none':
1172 len = '%f\\' % len2value(len) + special
1174 # Convert LyX units to LaTeX units
1175 for unit in units.keys():
1176 if find(len, unit) != -1:
1177 len = '%f' % (len2value(len) / 100) + units[unit]
1183 # Convert a LyX length into valid ERT code and append it to body[i]
1184 # Return the (maybe incremented) line index i
1185 def convert_ertlen(body, i, len, special):
1186 # Convert backslashes and insert the converted length into body
1187 return convert_ertbackslash(body, i, convert_len(len, special))
1190 # Return the value of len without the unit in numerical form
1192 result = re.search('([+-]?[0-9.]+)', len)
1194 return float(result.group(1))
1195 # No number means 1.0
1199 # Convert text to ERT and insert it at body[i]
1200 # Return the index of the line after the inserted ERT
1201 def insert_ert(body, i, status, text):
1202 body[i:i] = ['\\begin_inset ERT', 'status ' + status, '',
1203 '\\layout Standard', '']
1205 i = convert_ertbackslash(body, i, text) + 1
1206 body[i:i] = ['', '\\end_inset', '']
1211 # Add text to the preamble if it is not already there.
1212 # Only the first line is checked!
1213 def add_to_preamble(file, text):
1214 if find_token(file.preamble, text[0], 0) != -1:
1217 file.preamble.extend(text)
1220 def convert_frameless_box(file):
1221 pos = ['t', 'c', 'b']
1222 inner_pos = ['c', 't', 'b', 's']
1225 i = find_token(file.body, '\\begin_inset Frameless', i)
1228 j = find_end_of_inset(file.body, i)
1230 file.warning("Malformed LyX file: Missing '\\end_inset'.")
1237 params = {'position':0, 'hor_pos':'c', 'has_inner_box':'1',
1238 'inner_pos':1, 'use_parbox':'0', 'width':'100col%',
1239 'special':'none', 'height':'1in',
1240 'height_special':'totalheight', 'collapsed':'false'}
1241 for key in params.keys():
1242 value = replace(get_value(file.body, key, i, j), '"', '')
1244 if key == 'position':
1245 # convert new to old position: 'position "t"' -> 0
1246 value = find_token(pos, value, 0)
1249 elif key == 'inner_pos':
1250 # convert inner position
1251 value = find_token(inner_pos, value, 0)
1256 j = del_token(file.body, key, i, j)
1259 # Convert to minipage or ERT?
1260 # Note that the inner_position and height parameters of a minipage
1261 # inset are ignored and not accessible for the user, although they
1262 # are present in the file format and correctly read in and written.
1263 # Therefore we convert to ERT if they do not have their LaTeX
1264 # defaults. These are:
1265 # - the value of "position" for "inner_pos"
1266 # - "\totalheight" for "height"
1267 if (params['use_parbox'] != '0' or
1268 params['has_inner_box'] != '1' or
1269 params['special'] != 'none' or
1270 params['height_special'] != 'totalheight' or
1271 len2value(params['height']) != 1.0):
1273 # Here we know that this box is not supported in file format 224.
1274 # Therefore we need to convert it to ERT. We can't simply convert
1275 # the beginning and end of the box to ERT, because the
1276 # box inset may contain layouts that are different from the
1277 # surrounding layout. After the conversion the contents of the
1278 # box inset is on the same level as the surrounding text, and
1279 # paragraph layouts and align parameters can get mixed up.
1281 # A possible solution for this problem:
1282 # Convert the box to a minipage and redefine the minipage
1283 # environment in ERT so that the original box is simulated.
1284 # For minipages we could do this in a way that the width and
1285 # position can still be set from LyX, but this did not work well.
1286 # This is not possible for parboxes either, so we convert the
1287 # original box to ERT, put the minipage inset inside the box
1288 # and redefine the minipage environment to be empty.
1290 # Commands that are independant of a particular box can go to
1292 # We need to define lyxtolyxrealminipage with 3 optional
1293 # arguments although LyX 1.3 uses only the first one.
1294 # Otherwise we will get LaTeX errors if this document is
1295 # converted to format 225 or above again (LyX 1.4 uses all
1296 # optional arguments).
1297 add_to_preamble(file,
1298 ['% Commands inserted by lyx2lyx for frameless boxes',
1299 '% Save the original minipage environment',
1300 '\\let\\lyxtolyxrealminipage\\minipage',
1301 '\\let\\endlyxtolyxrealminipage\\endminipage',
1302 '% Define an empty lyxtolyximinipage environment',
1303 '% with 3 optional arguments',
1304 '\\newenvironment{lyxtolyxiiiminipage}[4]{}{}',
1305 '\\newenvironment{lyxtolyxiiminipage}[2][\\lyxtolyxargi]%',
1306 ' {\\begin{lyxtolyxiiiminipage}{\\lyxtolyxargi}{\\lyxtolyxargii}{#1}{#2}}%',
1307 ' {\\end{lyxtolyxiiiminipage}}',
1308 '\\newenvironment{lyxtolyximinipage}[1][\\totalheight]%',
1309 ' {\\def\\lyxtolyxargii{{#1}}\\begin{lyxtolyxiiminipage}}%',
1310 ' {\\end{lyxtolyxiiminipage}}',
1311 '\\newenvironment{lyxtolyxminipage}[1][c]%',
1312 ' {\\def\\lyxtolyxargi{{#1}}\\begin{lyxtolyximinipage}}',
1313 ' {\\end{lyxtolyximinipage}}'])
1315 if params['use_parbox'] != '0':
1318 ert = '\\begin{lyxtolyxrealminipage}'
1320 # convert optional arguments only if not latex default
1321 if (pos[params['position']] != 'c' or
1322 inner_pos[params['inner_pos']] != pos[params['position']] or
1323 params['height_special'] != 'totalheight' or
1324 len2value(params['height']) != 1.0):
1325 ert = ert + '[' + pos[params['position']] + ']'
1326 if (inner_pos[params['inner_pos']] != pos[params['position']] or
1327 params['height_special'] != 'totalheight' or
1328 len2value(params['height']) != 1.0):
1329 ert = ert + '[' + convert_len(params['height'],
1330 params['height_special']) + ']'
1331 if inner_pos[params['inner_pos']] != pos[params['position']]:
1332 ert = ert + '[' + inner_pos[params['inner_pos']] + ']'
1334 ert = ert + '{' + convert_len(params['width'],
1335 params['special']) + '}'
1337 if params['use_parbox'] != '0':
1339 ert = ert + '\\let\\minipage\\lyxtolyxminipage%\n'
1340 ert = ert + '\\let\\endminipage\\endlyxtolyxminipage%\n'
1343 i = insert_ert(file.body, i, 'Collapsed', ert)
1344 j = j + i - old_i - 1
1346 file.body[i:i] = ['\\begin_inset Minipage',
1347 'position %d' % params['position'],
1350 'width "' + params['width'] + '"',
1351 'collapsed ' + params['collapsed']]
1355 # Restore the original minipage environment since we may have
1356 # minipages inside this box.
1357 # Start a new paragraph because the following may be nonstandard
1358 file.body[i:i] = ['\\layout Standard', '', '']
1361 ert = '\\let\\minipage\\lyxtolyxrealminipage%\n'
1362 ert = ert + '\\let\\endminipage\\lyxtolyxrealendminipage%'
1364 i = insert_ert(file.body, i, 'Collapsed', ert)
1365 j = j + i - old_i - 1
1367 # Redefine the minipage end before the inset end.
1368 # Start a new paragraph because the previous may be nonstandard
1369 file.body[j:j] = ['\\layout Standard', '', '']
1371 ert = '\\let\\endminipage\\endlyxtolyxminipage'
1372 j = insert_ert(file.body, j, 'Collapsed', ert)
1374 file.body.insert(j, '')
1377 # LyX writes '%\n' after each box. Therefore we need to end our
1378 # ERT with '%\n', too, since this may swallow a following space.
1379 if params['use_parbox'] != '0':
1382 ert = '\\end{lyxtolyxrealminipage}%\n'
1383 j = insert_ert(file.body, j, 'Collapsed', ert)
1385 # We don't need to restore the original minipage after the inset
1386 # end because the scope of the redefinition is the original box.
1390 # Convert to minipage
1391 file.body[i:i] = ['\\begin_inset Minipage',
1392 'position %d' % params['position'],
1393 'inner_position %d' % params['inner_pos'],
1394 'height "' + params['height'] + '"',
1395 'width "' + params['width'] + '"',
1396 'collapsed ' + params['collapsed']]
1403 def convert_jurabib(file):
1404 i = find_token(file.header, '\\use_numerical_citations', 0)
1406 file.warning("Malformed lyx file: Missing '\\use_numerical_citations'.")
1408 file.header.insert(i + 1, '\\use_jurabib 0')
1411 def revert_jurabib(file):
1412 i = find_token(file.header, '\\use_jurabib', 0)
1414 file.warning("Malformed lyx file: Missing '\\use_jurabib'.")
1416 if get_value(file.header, '\\use_jurabib', 0) != "0":
1417 file.warning("Conversion of '\\use_jurabib = 1' not yet implemented.")
1418 # Don't remove '\\use_jurabib' so that people will get warnings by lyx
1426 def convert_bibtopic(file):
1427 i = find_token(file.header, '\\use_jurabib', 0)
1429 file.warning("Malformed lyx file: Missing '\\use_jurabib'.")
1431 file.header.insert(i + 1, '\\use_bibtopic 0')
1434 def revert_bibtopic(file):
1435 i = find_token(file.header, '\\use_bibtopic', 0)
1437 file.warning("Malformed lyx file: Missing '\\use_bibtopic'.")
1439 if get_value(file.header, '\\use_bibtopic', 0) != "0":
1440 file.warning("Conversion of '\\use_bibtopic = 1' not yet implemented.")
1441 # Don't remove '\\use_jurabib' so that people will get warnings by lyx
1448 def convert_float(file):
1451 i = find_token(file.body, '\\begin_inset Float', i)
1454 # Seach for a line starting 'wide'
1455 # If, however, we find a line starting '\begin_layout'
1456 # (_always_ present) then break with a warning message
1459 if (file.body[i][:4] == "wide"):
1460 file.body.insert(i + 1, 'sideways false')
1462 elif (file.body[i][:13] == "\\begin_layout"):
1463 file.warning("Malformed lyx file: Missing 'wide'.")
1469 def revert_float(file):
1472 i = find_token(file.body, '\\begin_inset Float', i)
1475 j = find_end_of_inset(file.body, i)
1477 file.warning("Malformed lyx file: Missing '\\end_inset'.")
1480 if get_value(file.body, 'sideways', i, j) != "false":
1481 file.warning("Conversion of 'sideways true' not yet implemented.")
1482 # Don't remove 'sideways' so that people will get warnings by lyx
1485 del_token(file.body, 'sideways', i, j)
1489 def convert_graphics(file):
1490 """ Add extension to filenames of insetgraphics if necessary.
1494 i = find_token(file.body, "\\begin_inset Graphics", i)
1498 j = find_token2(file.body, "filename", i)
1502 filename = split(file.body[j])[1]
1503 absname = os.path.normpath(os.path.join(file.dir, filename))
1504 if file.input == stdin and not os.path.isabs(filename):
1505 # We don't know the directory and cannot check the file.
1506 # We could use a heuristic and take the current directory,
1507 # and we could try to find out if filename has an extension,
1508 # but that would be just guesses and could be wrong.
1509 file.warning("""Warning: Can not determine whether file
1511 needs an extension when reading from standard input.
1512 You may need to correct the file manually or run
1513 lyx2lyx again with the .lyx file as commandline argument.""" % filename)
1515 # This needs to be the same algorithm as in pre 233 insetgraphics
1516 if access(absname, F_OK):
1518 if access(absname + ".ps", F_OK):
1519 file.body[j] = replace(file.body[j], filename, filename + ".ps")
1521 if access(absname + ".eps", F_OK):
1522 file.body[j] = replace(file.body[j], filename, filename + ".eps")
1526 # Convert firstname and surname from styles -> char styles
1528 def convert_names(file):
1529 """ Convert in the docbook backend from firstname and surname style
1532 if file.backend != "docbook":
1538 i = find_token(file.body, "\\begin_layout Author", i)
1543 while file.body[i] == "":
1546 if file.body[i][:11] != "\\end_layout" or file.body[i+2][:13] != "\\begin_deeper":
1551 i = find_end_of( file.body, i+3, "\\begin_deeper","\\end_deeper")
1553 # something is really wrong, abort
1554 file.warning("Missing \\end_deeper, after style Author.")
1555 file.warning("Aborted attempt to parse FirstName and Surname.")
1557 firstname, surname = "", ""
1559 name = file.body[k:i]
1561 j = find_token(name, "\\begin_layout FirstName", 0)
1564 while(name[j] != "\\end_layout"):
1565 firstname = firstname + name[j]
1568 j = find_token(name, "\\begin_layout Surname", 0)
1571 while(name[j] != "\\end_layout"):
1572 surname = surname + name[j]
1576 del file.body[k+2:i+1]
1578 file.body[k-1:k-1] = ["", "",
1579 "\\begin_inset CharStyle Firstname",
1582 "\\begin_layout Standard",
1590 "\\begin_inset CharStyle Surname",
1593 "\\begin_layout Standard",
1602 def revert_names(file):
1603 """ Revert in the docbook backend from firstname and surname char style
1606 if file.backend != "docbook":
1611 # \use_natbib 1 \cite_engine <style>
1612 # \use_numerical_citations 0 -> where <style> is one of
1613 # \use_jurabib 0 "basic", "natbib_authoryear",
1614 # "natbib_numerical" or "jurabib"
1615 def convert_cite_engine(file):
1616 a = find_token(file.header, "\\use_natbib", 0)
1618 file.warning("Malformed lyx file: Missing '\\use_natbib'.")
1621 b = find_token(file.header, "\\use_numerical_citations", 0)
1622 if b == -1 or b != a+1:
1623 file.warning("Malformed lyx file: Missing '\\use_numerical_citations'.")
1626 c = find_token(file.header, "\\use_jurabib", 0)
1627 if c == -1 or c != b+1:
1628 file.warning("Malformed lyx file: Missing '\\use_jurabib'.")
1631 use_natbib = int(split(file.header[a])[1])
1632 use_numerical_citations = int(split(file.header[b])[1])
1633 use_jurabib = int(split(file.header[c])[1])
1635 cite_engine = "basic"
1637 if use_numerical_citations:
1638 cite_engine = "natbib_numerical"
1640 cite_engine = "natbib_authoryear"
1642 cite_engine = "jurabib"
1644 del file.header[a:c+1]
1645 file.header.insert(a, "\\cite_engine " + cite_engine)
1648 def revert_cite_engine(file):
1649 i = find_token(file.header, "\\cite_engine", 0)
1651 file.warning("Malformed lyx file: Missing '\\cite_engine'.")
1654 cite_engine = split(file.header[i])[1]
1659 if cite_engine == "natbib_numerical":
1662 elif cite_engine == "natbib_authoryear":
1664 elif cite_engine == "jurabib":
1668 file.header.insert(i, "\\use_jurabib " + use_jurabib)
1669 file.header.insert(i, "\\use_numerical_citations " + use_numerical)
1670 file.header.insert(i, "\\use_natbib " + use_natbib)
1676 def convert_paperpackage(file):
1677 i = find_token(file.header, "\\paperpackage", 0)
1681 packages = {'default':'none','a4':'none', 'a4wide':'a4', 'widemarginsa4':'a4wide'}
1682 if len(split(file.header[i])) > 1:
1683 paperpackage = split(file.header[i])[1]
1684 file.header[i] = replace(file.header[i], paperpackage, packages[paperpackage])
1686 file.header[i] = file.header[i] + ' widemarginsa4'
1689 def revert_paperpackage(file):
1690 i = find_token(file.header, "\\paperpackage", 0)
1694 packages = {'none':'a4', 'a4':'a4wide', 'a4wide':'widemarginsa4',
1695 'widemarginsa4':'', 'default': 'default'}
1696 if len(split(file.header[i])) > 1:
1697 paperpackage = split(file.header[i])[1]
1699 paperpackage = 'default'
1700 file.header[i] = replace(file.header[i], paperpackage, packages[paperpackage])
1706 def convert_bullets(file):
1709 i = find_token(file.header, "\\bullet", i)
1712 if file.header[i][:12] == '\\bulletLaTeX':
1713 file.header[i] = file.header[i] + ' ' + strip(file.header[i+1])
1716 file.header[i] = file.header[i] + ' ' + strip(file.header[i+1]) +\
1717 ' ' + strip(file.header[i+2]) + ' ' + strip(file.header[i+3])
1719 del file.header[i+1:i + n]
1723 def revert_bullets(file):
1726 i = find_token(file.header, "\\bullet", i)
1729 if file.header[i][:12] == '\\bulletLaTeX':
1730 n = find(file.header[i], '"')
1732 file.warning("Malformed header.")
1735 file.header[i:i+1] = [file.header[i][:n-1],'\t' + file.header[i][n:], '\\end_bullet']
1738 frag = split(file.header[i])
1740 file.warning("Malformed header.")
1743 file.header[i:i+1] = [frag[0] + ' ' + frag[1],
1752 # \begin_header and \begin_document
1754 def add_begin_header(file):
1755 i = find_token(file.header, '\\lyxformat', 0)
1756 file.header.insert(i+1, '\\begin_header')
1757 file.header.insert(i+1, '\\begin_document')
1760 def remove_begin_header(file):
1761 i = find_token(file.header, "\\begin_document", 0)
1764 i = find_token(file.header, "\\begin_header", 0)
1770 # \begin_file.body and \end_file.body
1772 def add_begin_body(file):
1773 file.body.insert(0, '\\begin_body')
1774 file.body.insert(1, '')
1775 i = find_token(file.body, "\\end_document", 0)
1776 file.body.insert(i, '\\end_body')
1778 def remove_begin_body(file):
1779 i = find_token(file.body, "\\begin_body", 0)
1782 if not file.body[i]:
1784 i = find_token(file.body, "\\end_body", 0)
1792 def normalize_papersize(file):
1793 i = find_token(file.header, '\\papersize', 0)
1797 tmp = split(file.header[i])
1798 if tmp[1] == "Default":
1799 file.header[i] = '\\papersize default'
1801 if tmp[1] == "Custom":
1802 file.header[i] = '\\papersize custom'
1805 def denormalize_papersize(file):
1806 i = find_token(file.header, '\\papersize', 0)
1810 tmp = split(file.header[i])
1811 if tmp[1] == "custom":
1812 file.header[i] = '\\papersize Custom'
1816 # Strip spaces at end of command line
1818 def strip_end_space(file):
1819 for i in range(len(file.body)):
1820 if file.body[i][:1] == '\\':
1821 file.body[i] = strip(file.body[i])
1825 # Use boolean values for \use_geometry, \use_bibtopic and \tracking_changes
1827 def use_x_boolean(file):
1828 bin2bool = {'0': 'false', '1': 'true'}
1829 for use in '\\use_geometry', '\\use_bibtopic', '\\tracking_changes':
1830 i = find_token(file.header, use, 0)
1833 decompose = split(file.header[i])
1834 file.header[i] = decompose[0] + ' ' + bin2bool[decompose[1]]
1837 def use_x_binary(file):
1838 bool2bin = {'false': '0', 'true': '1'}
1839 for use in '\\use_geometry', '\\use_bibtopic', '\\tracking_changes':
1840 i = find_token(file.header, use, 0)
1843 decompose = split(file.header[i])
1844 file.header[i] = decompose[0] + ' ' + bool2bin[decompose[1]]
1847 # Place all the paragraph parameters in their own line
1849 def normalize_paragraph_params(file):
1851 allowed_parameters = '\\paragraph_spacing', '\\noindent', '\\align', '\\labelwidthstring', "\\start_of_appendix", "\\leftindent"
1855 i = find_token(file.body, '\\begin_layout', i)
1861 if strip(body[i]) and split(body[i])[0] not in allowed_parameters:
1864 j = find(body[i],'\\', 1)
1867 body[i:i+1] = [strip(body[i][:j]), body[i][j:]]
1873 # Add/remove output_changes parameter
1875 def convert_output_changes (file):
1876 i = find_token(file.header, '\\tracking_changes', 0)
1878 file.warning("Malformed lyx file: Missing '\\tracking_changes'.")
1880 file.header.insert(i+1, '\\output_changes true')
1883 def revert_output_changes (file):
1884 i = find_token(file.header, '\\output_changes', 0)
1891 # Convert paragraph breaks and sanitize paragraphs
1893 def convert_ert_paragraphs(file):
1894 forbidden_settings = [
1895 # paragraph parameters
1896 '\\paragraph_spacing', '\\labelwidthstring',
1897 '\\start_of_appendix', '\\noindent',
1898 '\\leftindent', '\\align',
1900 '\\family', '\\series', '\\shape', '\\size',
1901 '\\emph', '\\numeric', '\\bar', '\\noun',
1902 '\\color', '\\lang']
1905 i = find_token(file.body, '\\begin_inset ERT', i)
1908 j = find_end_of_inset(file.body, i)
1910 file.warning("Malformed lyx file: Missing '\\end_inset'.")
1914 # convert non-standard paragraphs to standard
1917 k = find_token(file.body, "\\begin_layout", k, j)
1920 file.body[k] = "\\begin_layout Standard"
1923 # remove all paragraph parameters and font settings
1926 if (strip(file.body[k]) and
1927 split(file.body[k])[0] in forbidden_settings):
1933 # insert an empty paragraph before each paragraph but the first
1937 k = find_token(file.body, "\\begin_layout Standard", k, j)
1944 file.body[k:k] = ["\\begin_layout Standard", "",
1949 # convert \\newline to new paragraph
1952 k = find_token(file.body, "\\newline", k, j)
1955 file.body[k:k+1] = ["\\end_layout", "", "\\begin_layout Standard"]
1962 # Remove double paragraph breaks
1964 def revert_ert_paragraphs(file):
1967 i = find_token(file.body, '\\begin_inset ERT', i)
1970 j = find_end_of_inset(file.body, i)
1972 file.warning("Malformed lyx file: Missing '\\end_inset'.")
1976 # replace paragraph breaks with \newline
1979 k = find_token(file.body, "\\end_layout", k, j)
1980 l = find_token(file.body, "\\begin_layout", k, j)
1981 if k == -1 or l == -1:
1983 file.body[k:l+1] = ["\\newline"]
1987 # replace double \newlines with paragraph breaks
1990 k = find_token(file.body, "\\newline", k, j)
1994 while file.body[l] == "":
1996 if strip(file.body[l]) and split(file.body[l])[0] == "\\newline":
1997 file.body[k:l+1] = ["\\end_layout", "",
1998 "\\begin_layout Standard"]
2006 def convert_french(file):
2007 regexp = re.compile(r'^\\language\s+frenchb')
2008 i = find_re(file.header, regexp, 0)
2010 file.header[i] = "\\language french"
2012 # Change language in the document body
2013 regexp = re.compile(r'^\\lang\s+frenchb')
2016 i = find_re(file.body, regexp, i)
2019 file.body[i] = "\\lang french"
2023 def remove_paperpackage(file):
2024 i = find_token(file.header, '\\paperpackage', 0)
2029 paperpackage = split(file.header[i])[1]
2031 if paperpackage in ("a4", "a4wide", "widemarginsa4"):
2032 conv = {"a4":"\\usepackage{a4}","a4wide": "\\usepackage{a4wide}",
2033 "widemarginsa4": "\\usepackage[widemargins]{a4}"}
2034 # for compatibility we ensure it is the first entry in preamble
2035 file.preamble[0:0] = [conv[paperpackage]]
2039 i = find_token(file.header, '\\papersize', 0)
2041 file.header[i] = "\\papersize default"
2048 convert = [[222, [insert_tracking_changes, add_end_header]],
2049 [223, [remove_color_default, convert_spaces, convert_bibtex, remove_insetparent]],
2050 [224, [convert_external, convert_comment]],
2051 [225, [add_end_layout, layout2begin_layout, convert_end_document,
2052 convert_table_valignment_middle, convert_breaks]],
2053 [226, [convert_note]],
2054 [227, [convert_box]],
2055 [228, [convert_collapsable, convert_ert]],
2056 [229, [convert_minipage]],
2057 [230, [convert_jurabib]],
2058 [231, [convert_float]],
2059 [232, [convert_bibtopic]],
2060 [233, [convert_graphics, convert_names]],
2061 [234, [convert_cite_engine]],
2062 [235, [convert_paperpackage]],
2063 [236, [convert_bullets, add_begin_header, add_begin_body,
2064 normalize_papersize, strip_end_space]],
2065 [237, [use_x_boolean]],
2066 [238, [update_latexaccents]],
2067 [239, [normalize_paragraph_params]],
2068 [240, [convert_output_changes]],
2069 [241, [convert_ert_paragraphs]],
2070 [242, [convert_french]],
2071 [243, [remove_paperpackage]]]
2073 revert = [[242, []],
2075 [240, [revert_ert_paragraphs]],
2076 [239, [revert_output_changes]],
2079 [236, [use_x_binary]],
2080 [235, [denormalize_papersize, remove_begin_body,remove_begin_header,
2082 [234, [revert_paperpackage]],
2083 [233, [revert_cite_engine]],
2084 [232, [revert_names]],
2085 [231, [revert_bibtopic]],
2086 [230, [revert_float]],
2087 [229, [revert_jurabib]],
2089 [227, [revert_collapsable, revert_ert]],
2090 [226, [revert_box, revert_external_2]],
2091 [225, [revert_note]],
2092 [224, [rm_end_layout, begin_layout2layout, revert_end_document,
2093 revert_valignment_middle, revert_breaks, convert_frameless_box]],
2094 [223, [revert_external_2, revert_comment, revert_eqref]],
2095 [222, [revert_spaces, revert_bibtex]],
2096 [221, [rm_end_header, rm_tracking_changes, rm_body_changes]]]
2099 if __name__ == "__main__":