1 # This file is part of lyx2lyx
2 # -*- coding: iso-8859-1 -*-
3 # Copyright (C) 2002-2004 Dekel Tsur <dekel@lyx.org>, José Matos <jamatos@lyx.org>
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 def check_token(line, token):
23 if line[:len(token)] == token:
27 # We need to check that the char after the token is space, but I think
29 def find_token(lines, token, start, end = 0):
33 for i in xrange(start, end):
34 if lines[i][:m] == token:
38 def find_token2(lines, token, start, end = 0):
41 for i in xrange(start, end):
42 x = string.split(lines[i])
43 if len(x) > 0 and x[0] == token:
47 def find_tokens(lines, tokens, start, end = 0):
50 for i in xrange(start, end):
53 if line[:len(token)] == token:
57 def find_re(lines, rexp, start, end = 0):
60 for i in xrange(start, end):
61 if rexp.match(lines[i]):
65 def find_token_backwards(lines, token, start):
67 for i in xrange(start, -1, -1):
73 def find_tokens_backwards(lines, tokens, start):
74 for i in xrange(start, -1, -1):
77 if line[:len(token)] == token:
81 def get_value(lines, token, start, end = 0):
82 i = find_token2(lines, token, start, end)
85 if len(string.split(lines[i])) > 1:
86 return string.split(lines[i])[1]
90 def del_token(lines, token, i, j):
91 k = find_token2(lines, token, i, j)
98 # Finds the paragraph that contains line i.
99 def get_paragraph(lines, i):
101 i = find_tokens_backwards(lines, ["\\end_inset", "\\layout"], i)
102 if i == -1: return -1
103 if check_token(lines[i], "\\layout"):
105 i = find_beginning_of_inset(lines, i)
108 # Finds the paragraph after the paragraph that contains line i.
109 def get_next_paragraph(lines, i):
111 i = find_tokens(lines, ["\\begin_inset", "\\layout", "\\end_float", "\\the_end"], i)
112 if not check_token(lines[i], "\\begin_inset"):
114 i = find_end_of_inset(lines, i)
117 def find_end_of(lines, i, start_token, end_token):
121 i = find_tokens(lines, [end_token, start_token], i+1)
122 if check_token(lines[i], start_token):
130 # Finds the matching \end_inset
131 def find_beginning_of(lines, i, start_token, end_token):
134 i = find_tokens_backwards(lines, [start_token, end_token], i-1)
135 if check_token(lines[i], end_token):
143 # Finds the matching \end_inset
144 def find_end_of_inset(lines, i):
145 return find_end_of(lines, i, "\\begin_inset", "\\end_inset")
147 # Finds the matching \end_inset
148 def find_beginning_of_inset(lines, i):
149 return find_beginning_of(lines, i, "\\begin_inset", "\\end_inset")
151 def find_end_of_tabular(lines, i):
152 return find_end_of(lines, i, "<lyxtabular", "</lyxtabular")
154 def get_tabular_lines(lines, i):
157 j = find_end_of_tabular(lines, i)
162 if check_token(lines[i], "\\begin_inset"):
163 i = find_end_of_inset(lines, i)+1
169 def is_nonempty_line(line):
170 return line != " "*len(line)
172 def find_nonempty_line(lines, start, end = 0):
175 for i in xrange(start, end):
176 if is_nonempty_line(lines[i]):
181 # Tools for file reading
183 def read_file(header, body, opt):
184 """Reads a file into the header and body parts"""
188 line = opt.input.readline()
190 opt.error("Invalid LyX file.")
193 if check_token(line, '\\begin_preamble'):
195 if check_token(line, '\\end_preamble'):
199 line = string.strip(line)
201 if not line and not preamble:
207 line = opt.input.readline()
210 body.append(line[:-1])
212 def write_file(header, body, opt):
214 opt.output.write(line+"\n")
215 opt.output.write("\n")
217 opt.output.write(line+"\n")
222 original_version = re.compile(r"\#LyX (\S*)")
224 def read_version(header):
229 result = original_version.match(line)
231 return result.group(1)
234 def set_version(lines, version):
235 lines[0] = "#LyX %s created this file. For more info see http://www.lyx.org/" % version
236 if lines[1][0] == '#':
240 # file format version
242 format_re = re.compile(r"(\d)[\.,]?(\d\d)")
243 fileformat = re.compile(r"\\lyxformat\s*(\S*)")
244 lst_ft = [210, 215, 216, 217, 218, 220, 221, 223, 224, 225, 226, 227, 228, 229,
247 format_relation = [("0_10", [210], ["0.10.7","0.10"]),
248 ("0_12", [215], ["0.12","0.12.1","0.12"]),
249 ("1_0_0", [215], ["1.0.0","1.0"]),
250 ("1_0_1", [215], ["1.0.1","1.0.2","1.0.3","1.0.4", "1.1.2","1.1"]),
251 ("1_1_4", [215], ["1.1.4","1.1"]),
252 ("1_1_5", [216], ["1.1.5","1.1.5fix1","1.1.5fix2","1.1"]),
253 ("1_1_6", [217], ["1.1.6","1.1.6fix1","1.1.6fix2","1.1"]),
254 ("1_1_6fix3", [218], ["1.1.6fix3","1.1.6fix4","1.1"]),
255 ("1_2", [220], ["1.2.0","1.2.1","1.2.3","1.2.4","1.2"]),
256 ("1_3", [221], ["1.3.0","1.3.1","1.3.2","1.3.3","1.3.4","1.3"]),
257 ("1_4", [223,224,225,226,227,228,229,230,231,232,233], ["1.4.0cvs","1.4"])]
259 def lyxformat(format, opt):
260 result = format_re.match(format)
262 format = int(result.group(1) + result.group(2))
264 opt.error(str(format) + ": " + "Invalid LyX file.")
269 opt.error(str(format) + ": " + "Format no supported.")
272 def read_format(header, opt):
274 result = fileformat.match(line)
276 return lyxformat(result.group(1), opt)
278 opt.error("Invalid LyX File.")
281 def set_format(lines, number):
282 if int(number) <= 217:
283 number = float(number)/100
284 i = find_token(lines, "\\lyxformat", 0)
285 lines[i] = "\\lyxformat %s" % number
287 def get_end_format():
288 return format_relation[-1:][0][1][-1:][0]
290 def chain(opt, initial_version):
291 """ This is where all the decisions related with the convertion are taken"""
295 if opt.start != format:
296 opt.warning("%s: %s %s" % ("Proposed file format and input file formats do not match:", opt.start, format))
301 opt.end = get_end_format()
305 for rel in format_relation:
306 if initial_version in rel[2]:
308 initial_step = rel[0]
312 if not correct_version:
314 opt.warning("Version does not match file format, discarding it.")
315 for rel in format_relation:
317 initial_step = rel[0]
320 # This should not happen, really.
321 opt.error("Format not supported.")
323 # Find the final step
324 for rel in format_relation:
325 if opt.end in rel[1]:
329 opt.error("Format not supported.")
331 # Convertion mode, back or forth
333 if (initial_step, opt.start) < (final_step, opt.end):
336 for step in format_relation:
337 if initial_step <= step[0] <= final_step:
338 if first_step and len(step[1]) == 1:
341 steps.append(step[0])
344 relation_format = format_relation
345 relation_format.reverse()
348 for step in relation_format:
349 if final_step <= step[0] <= initial_step:
350 steps.append(step[0])
353 if last_step[1][-1] == opt.end: