]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_1_5.py
68f71cc18b355773c279564ad60cd090c94682fb
[lyx.git] / lib / lyx2lyx / lyx_1_5.py
1 # This file is part of lyx2lyx
2 # -*- coding: utf-8 -*-
3 # Copyright (C) 2006 José Matos <jamatos@lyx.org>
4 # Copyright (C) 2004-2006 Georg Baum <Georg.Baum@post.rwth-aachen.de>
5 #
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.
10 #
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.
15 #
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.
19
20 """ Convert files to the file format generated by lyx 1.5"""
21
22 import re
23 from parser_tools import find_token, find_token_backwards, find_token_exact, find_tokens, find_end_of, get_value
24 from LyX import get_encoding
25
26
27 ####################################################################
28 # Private helper functions
29
30 def find_end_of_inset(lines, i):
31     " Find end of inset, where lines[i] is included."
32     return find_end_of(lines, i, "\\begin_inset", "\\end_inset")
33
34 def find_end_of_layout(lines, i):
35     " Find end of layout, where lines[i] is included."
36     return find_end_of(lines, i, "\\begin_layout", "\\end_layout")
37
38 # End of helper functions
39 ####################################################################
40
41
42 ##
43 #  Notes: Framed/Shaded
44 #
45
46 def revert_framed(document):
47     "Revert framed notes. "
48     i = 0
49     while 1:
50         i = find_tokens(document.body, ["\\begin_inset Note Framed", "\\begin_inset Note Shaded"], i)
51
52         if i == -1:
53             return
54         document.body[i] = "\\begin_inset Note"
55         i = i + 1
56
57
58 ##
59 #  Fonts
60 #
61
62 roman_fonts      = {'default' : 'default', 'ae'       : 'ae',
63                     'times'   : 'times',   'palatino' : 'palatino',
64                     'helvet'  : 'default', 'avant'    : 'default',
65                     'newcent' : 'newcent', 'bookman'  : 'bookman',
66                     'pslatex' : 'times'}
67 sans_fonts       = {'default' : 'default', 'ae'       : 'default',
68                     'times'   : 'default', 'palatino' : 'default',
69                     'helvet'  : 'helvet',  'avant'    : 'avant',
70                     'newcent' : 'default', 'bookman'  : 'default',
71                     'pslatex' : 'helvet'}
72 typewriter_fonts = {'default' : 'default', 'ae'       : 'default',
73                     'times'   : 'default', 'palatino' : 'default',
74                     'helvet'  : 'default', 'avant'    : 'default',
75                     'newcent' : 'default', 'bookman'  : 'default',
76                     'pslatex' : 'courier'}
77
78 def convert_font_settings(document):
79     " Convert font settings. "
80     i = 0
81     i = find_token_exact(document.header, "\\fontscheme", i)
82     if i == -1:
83         document.warning("Malformed LyX document: Missing `\\fontscheme'.")
84         return
85     font_scheme = get_value(document.header, "\\fontscheme", i, i + 1)
86     if font_scheme == '':
87         document.warning("Malformed LyX document: Empty `\\fontscheme'.")
88         font_scheme = 'default'
89     if not font_scheme in roman_fonts.keys():
90         document.warning("Malformed LyX document: Unknown `\\fontscheme' `%s'." % font_scheme)
91         font_scheme = 'default'
92     document.header[i:i+1] = ['\\font_roman %s' % roman_fonts[font_scheme],
93                           '\\font_sans %s' % sans_fonts[font_scheme],
94                           '\\font_typewriter %s' % typewriter_fonts[font_scheme],
95                           '\\font_default_family default',
96                           '\\font_sc false',
97                           '\\font_osf false',
98                           '\\font_sf_scale 100',
99                           '\\font_tt_scale 100']
100
101
102 def revert_font_settings(document):
103     " Revert font settings. "
104     i = 0
105     insert_line = -1
106     fonts = {'roman' : 'default', 'sans' : 'default', 'typewriter' : 'default'}
107     for family in 'roman', 'sans', 'typewriter':
108         name = '\\font_%s' % family
109         i = find_token_exact(document.header, name, i)
110         if i == -1:
111             document.warning("Malformed LyX document: Missing `%s'." % name)
112             i = 0
113         else:
114             if (insert_line < 0):
115                 insert_line = i
116             fonts[family] = get_value(document.header, name, i, i + 1)
117             del document.header[i]
118     i = find_token_exact(document.header, '\\font_default_family', i)
119     if i == -1:
120         document.warning("Malformed LyX document: Missing `\\font_default_family'.")
121         font_default_family = 'default'
122     else:
123         font_default_family = get_value(document.header, "\\font_default_family", i, i + 1)
124         del document.header[i]
125     i = find_token_exact(document.header, '\\font_sc', i)
126     if i == -1:
127         document.warning("Malformed LyX document: Missing `\\font_sc'.")
128         font_sc = 'false'
129     else:
130         font_sc = get_value(document.header, '\\font_sc', i, i + 1)
131         del document.header[i]
132     if font_sc != 'false':
133         document.warning("Conversion of '\\font_sc' not yet implemented.")
134     i = find_token_exact(document.header, '\\font_osf', i)
135     if i == -1:
136         document.warning("Malformed LyX document: Missing `\\font_osf'.")
137         font_osf = 'false'
138     else:
139         font_osf = get_value(document.header, '\\font_osf', i, i + 1)
140         del document.header[i]
141     i = find_token_exact(document.header, '\\font_sf_scale', i)
142     if i == -1:
143         document.warning("Malformed LyX document: Missing `\\font_sf_scale'.")
144         font_sf_scale = '100'
145     else:
146         font_sf_scale = get_value(document.header, '\\font_sf_scale', i, i + 1)
147         del document.header[i]
148     if font_sf_scale != '100':
149         document.warning("Conversion of '\\font_sf_scale' not yet implemented.")
150     i = find_token_exact(document.header, '\\font_tt_scale', i)
151     if i == -1:
152         document.warning("Malformed LyX document: Missing `\\font_tt_scale'.")
153         font_tt_scale = '100'
154     else:
155         font_tt_scale = get_value(document.header, '\\font_tt_scale', i, i + 1)
156         del document.header[i]
157     if font_tt_scale != '100':
158         document.warning("Conversion of '\\font_tt_scale' not yet implemented.")
159     for font_scheme in roman_fonts.keys():
160         if (roman_fonts[font_scheme] == fonts['roman'] and
161             sans_fonts[font_scheme] == fonts['sans'] and
162             typewriter_fonts[font_scheme] == fonts['typewriter']):
163             document.header.insert(insert_line, '\\fontscheme %s' % font_scheme)
164             if font_default_family != 'default':
165                 document.preamble.append('\\renewcommand{\\familydefault}{\\%s}' % font_default_family)
166             if font_osf == 'true':
167                 document.warning("Ignoring `\\font_osf = true'")
168             return
169     font_scheme = 'default'
170     document.header.insert(insert_line, '\\fontscheme %s' % font_scheme)
171     if fonts['roman'] == 'cmr':
172         document.preamble.append('\\renewcommand{\\rmdefault}{cmr}')
173         if font_osf == 'true':
174             document.preamble.append('\\usepackage{eco}')
175             font_osf = 'false'
176     for font in 'lmodern', 'charter', 'utopia', 'beraserif', 'ccfonts', 'chancery':
177         if fonts['roman'] == font:
178             document.preamble.append('\\usepackage{%s}' % font)
179     for font in 'cmss', 'lmss', 'cmbr':
180         if fonts['sans'] == font:
181             document.preamble.append('\\renewcommand{\\sfdefault}{%s}' % font)
182     for font in 'berasans':
183         if fonts['sans'] == font:
184             document.preamble.append('\\usepackage{%s}' % font)
185     for font in 'cmtt', 'lmtt', 'cmtl':
186         if fonts['typewriter'] == font:
187             document.preamble.append('\\renewcommand{\\ttdefault}{%s}' % font)
188     for font in 'courier', 'beramono', 'luximono':
189         if fonts['typewriter'] == font:
190             document.preamble.append('\\usepackage{%s}' % font)
191     if font_default_family != 'default':
192         document.preamble.append('\\renewcommand{\\familydefault}{\\%s}' % font_default_family)
193     if font_osf == 'true':
194         document.warning("Ignoring `\\font_osf = true'")
195
196
197 def revert_booktabs(document):
198     " We remove the booktabs flag or everything else will become a mess. "
199     re_row = re.compile(r'^<row.*space="[^"]+".*>$')
200     re_tspace = re.compile(r'\s+topspace="[^"]+"')
201     re_bspace = re.compile(r'\s+bottomspace="[^"]+"')
202     re_ispace = re.compile(r'\s+interlinespace="[^"]+"')
203     i = 0
204     while 1:
205         i = find_token(document.body, "\\begin_inset Tabular", i)
206         if i == -1:
207             return
208         j = find_end_of_inset(document.body, i + 1)
209         if j == -1:
210             document.warning("Malformed LyX document: Could not find end of tabular.")
211             continue
212         for k in range(i, j):
213             if re.search('^<features.* booktabs="true".*>$', document.body[k]):
214                 document.warning("Converting 'booktabs' table to normal table.")
215                 document.body[k] = document.body[k].replace(' booktabs="true"', '')
216             if re.search(re_row, document.body[k]):
217                 document.warning("Removing extra row space.")
218                 document.body[k] = re_tspace.sub('', document.body[k])
219                 document.body[k] = re_bspace.sub('', document.body[k])
220                 document.body[k] = re_ispace.sub('', document.body[k])
221         i = i + 1
222
223
224 def convert_multiencoding(document, forward):
225     """ Fix files with multiple encodings.
226 Files with an inputencoding of "auto" or "default" and multiple languages
227 where at least two languages have different default encodings are encoded
228 in multiple encodings for file formats < 249. These files are incorrectly
229 read and written (as if the whole file was in the encoding of the main
230 language).
231
232 This function
233 - converts from fake unicode values to true unicode if forward is true, and
234 - converts from true unicode values to fake unicode if forward is false.
235 document.encoding must be set to the old value (format 248) in both cases.
236
237 We do this here and not in LyX.py because it is far easier to do the
238 necessary parsing in modern formats than in ancient ones.
239 """
240     encoding_stack = [document.encoding]
241     lang_re = re.compile(r"^\\lang\s(\S+)")
242     if document.inputencoding == "auto" or document.inputencoding == "default":
243         for i in range(len(document.body)):
244             result = lang_re.match(document.body[i])
245             if result:
246                 language = result.group(1)
247                 if language == "default":
248                     document.warning("Resetting encoding from %s to %s." % (encoding_stack[-1], document.encoding))
249                     encoding_stack[-1] = document.encoding
250                 else:
251                     from lyx2lyx_lang import lang
252                     document.warning("Setting encoding from %s to %s." % (encoding_stack[-1], lang[language][3]))
253                     encoding_stack[-1] = lang[language][3]
254             elif find_token(document.body, "\\begin_layout", i, i + 1) == i:
255                 document.warning("Adding nested encoding %s." % encoding_stack[-1])
256                 encoding_stack.append(encoding_stack[-1])
257             elif find_token(document.body, "\\end_layout", i, i + 1) == i:
258                 document.warning("Removing nested encoding %s." % encoding_stack[-1])
259                 del encoding_stack[-1]
260             if encoding_stack[-1] != document.encoding:
261                 if forward:
262                     # This line has been incorrectly interpreted as if it was
263                     # encoded in 'encoding'.
264                     # Convert back to the 8bit string that was in the file.
265                     orig = document.body[i].encode(document.encoding)
266                     # Convert the 8bit string that was in the file to unicode
267                     # with the correct encoding.
268                     document.body[i] = orig.decode(encoding_stack[-1])
269                 else:
270                     # Convert unicode to the 8bit string that will be written
271                     # to the file with the correct encoding.
272                     orig = document.body[i].encode(encoding_stack[-1])
273                     # Convert the 8bit string that will be written to the
274                     # file to fake unicode with the encoding that will later
275                     # be used when writing to the file.
276                     document.body[i] = orig.decode(document.encoding)
277
278
279 def convert_utf8(document):
280     " Set document encoding to UTF-8. "
281     convert_multiencoding(document, True)
282     document.encoding = "utf8"
283
284
285 def revert_utf8(document):
286     " Set document encoding to the value corresponding to inputencoding. "
287     i = find_token(document.header, "\\inputencoding", 0)
288     if i == -1:
289         document.header.append("\\inputencoding auto")
290     elif get_value(document.header, "\\inputencoding", i) == "utf8":
291         document.header[i] = "\\inputencoding auto"
292     document.inputencoding = get_value(document.header, "\\inputencoding", 0)
293     document.encoding = get_encoding(document.language, document.inputencoding, 248)
294     convert_multiencoding(document, False)
295
296
297 def revert_cs_label(document):
298     " Remove status flag of charstyle label. "
299     i = 0
300     while 1:
301         i = find_token(document.body, "\\begin_inset CharStyle", i)
302         if i == -1:
303             return
304         # Seach for a line starting 'show_label'
305         # If it is not there, break with a warning message
306         i = i + 1
307         while 1:
308             if (document.body[i][:10] == "show_label"):
309                 del document.body[i]
310                 break
311             elif (document.body[i][:13] == "\\begin_layout"):
312                 document.warning("Malformed LyX document: Missing 'show_label'.")
313                 break
314             i = i + 1
315
316         i = i + 1
317
318
319 def convert_bibitem(document):
320     """ Convert
321 \bibitem [option]{argument}
322
323 to
324
325 \begin_inset LatexCommand bibitem
326 label "option"
327 key "argument"
328
329 \end_inset
330
331 This must be called after convert_commandparams.
332 """
333     regex = re.compile(r'\S+\s*(\[[^\[\{]*\])?(\{[^}]*\})')
334     i = 0
335     while 1:
336         i = find_token(document.body, "\\bibitem", i)
337         if i == -1:
338             break
339         match = re.match(regex, document.body[i])
340         option = match.group(1)
341         argument = match.group(2)
342         lines = ['\\begin_inset LatexCommand bibitem']
343         if option != None:
344             lines.append('label "%s"' % option[1:-1].replace('"', '\\"'))
345         lines.append('key "%s"' % argument[1:-1].replace('"', '\\"'))
346         lines.append('')
347         lines.append('\\end_inset')
348         document.body[i:i+1] = lines
349         i = i + 1
350
351
352 commandparams_info = {
353     # command : [option1, option2, argument]
354     "bibitem" : ["label", "", "key"],
355     "bibtex" : ["options", "btprint", "bibfiles"],
356     "cite"        : ["after", "before", "key"],
357     "citet"       : ["after", "before", "key"],
358     "citep"       : ["after", "before", "key"],
359     "citealt"     : ["after", "before", "key"],
360     "citealp"     : ["after", "before", "key"],
361     "citeauthor"  : ["after", "before", "key"],
362     "citeyear"    : ["after", "before", "key"],
363     "citeyearpar" : ["after", "before", "key"],
364     "citet*"      : ["after", "before", "key"],
365     "citep*"      : ["after", "before", "key"],
366     "citealt*"    : ["after", "before", "key"],
367     "citealp*"    : ["after", "before", "key"],
368     "citeauthor*" : ["after", "before", "key"],
369     "Citet"       : ["after", "before", "key"],
370     "Citep"       : ["after", "before", "key"],
371     "Citealt"     : ["after", "before", "key"],
372     "Citealp"     : ["after", "before", "key"],
373     "Citeauthor"  : ["after", "before", "key"],
374     "Citet*"      : ["after", "before", "key"],
375     "Citep*"      : ["after", "before", "key"],
376     "Citealt*"    : ["after", "before", "key"],
377     "Citealp*"    : ["after", "before", "key"],
378     "Citeauthor*" : ["after", "before", "key"],
379     "citefield"   : ["after", "before", "key"],
380     "citetitle"   : ["after", "before", "key"],
381     "cite*"       : ["after", "before", "key"],
382     "hfill" : ["", "", ""],
383     "index"      : ["", "", "name"],
384     "printindex" : ["", "", "name"],
385     "label" : ["", "", "name"],
386     "eqref"     : ["name", "", "reference"],
387     "pageref"   : ["name", "", "reference"],
388     "prettyref" : ["name", "", "reference"],
389     "ref"       : ["name", "", "reference"],
390     "vpageref"  : ["name", "", "reference"],
391     "vref"      : ["name", "", "reference"],
392     "tableofcontents" : ["", "", "type"],
393     "htmlurl" : ["name", "", "target"],
394     "url"     : ["name", "", "target"]}
395
396
397 def convert_commandparams(document):
398     """ Convert
399
400  \begin_inset LatexCommand \cmdname[opt1][opt2]{arg}
401  \end_inset
402
403  to
404
405  \begin_inset LatexCommand cmdname
406  name1 "opt1"
407  name2 "opt2"
408  name3 "arg"
409  \end_inset
410
411  name1, name2 and name3 can be different for each command.
412 """
413     # \begin_inset LatexCommand bibitem was not the official version (see
414     # convert_bibitem()), but could be read in, so we convert it here, too.
415
416     i = 0
417     while 1:
418         i = find_token(document.body, "\\begin_inset LatexCommand", i)
419         if i == -1:
420             break
421         command = document.body[i][26:].strip()
422         if command == "":
423             document.warning("Malformed LyX document: Missing LatexCommand name.")
424             i = i + 1
425             continue
426
427         # The following parser is taken from the original InsetCommandParams::scanCommand
428         name = ""
429         option1 = ""
430         option2 = ""
431         argument = ""
432         state = "WS"
433         # Used to handle things like \command[foo[bar]]{foo{bar}}
434         nestdepth = 0
435         b = 0
436         for c in command:
437             if ((state == "CMDNAME" and c == ' ') or
438                 (state == "CMDNAME" and c == '[') or
439                 (state == "CMDNAME" and c == '{')):
440                 state = "WS"
441             if ((state == "OPTION" and c == ']') or
442                 (state == "SECOPTION" and c == ']') or
443                 (state == "CONTENT" and c == '}')):
444                 if nestdepth == 0:
445                     state = "WS"
446                 else:
447                     nestdepth = nestdepth - 1
448             if ((state == "OPTION" and c == '[') or
449                 (state == "SECOPTION" and c == '[') or
450                 (state == "CONTENT" and c == '{')):
451                 nestdepth = nestdepth + 1
452             if state == "CMDNAME":
453                     name += c
454             elif state == "OPTION":
455                     option1 += c
456             elif state == "SECOPTION":
457                     option2 += c
458             elif state == "CONTENT":
459                     argument += c
460             elif state == "WS":
461                 if c == '\\':
462                     state = "CMDNAME"
463                 elif c == '[' and b != ']':
464                     state = "OPTION"
465                     nestdepth = 0 # Just to be sure
466                 elif c == '[' and b == ']':
467                     state = "SECOPTION"
468                     nestdepth = 0 # Just to be sure
469                 elif c == '{':
470                     state = "CONTENT"
471                     nestdepth = 0 # Just to be sure
472             b = c
473
474         # Now we have parsed the command, output the parameters
475         lines = ["\\begin_inset LatexCommand %s" % name]
476         if option1 != "":
477             if commandparams_info[name][0] == "":
478                 document.warning("Ignoring invalid option `%s' of command `%s'." % (option1, name))
479             else:
480                 lines.append('%s "%s"' % (commandparams_info[name][0], option1.replace('"', '\\"')))
481         if option2 != "":
482             if commandparams_info[name][1] == "":
483                 document.warning("Ignoring invalid second option `%s' of command `%s'." % (option2, name))
484             else:
485                 lines.append('%s "%s"' % (commandparams_info[name][1], option2.replace('"', '\\"')))
486         if argument != "":
487             if commandparams_info[name][2] == "":
488                 document.warning("Ignoring invalid argument `%s' of command `%s'." % (argument, name))
489             else:
490                 lines.append('%s "%s"' % (commandparams_info[name][2], argument.replace('"', '\\"')))
491         document.body[i:i+1] = lines
492         i = i + 1
493
494
495 def revert_commandparams(document):
496     regex = re.compile(r'(\S+)\s+(.+)')
497     i = 0
498     while 1:
499         i = find_token(document.body, "\\begin_inset LatexCommand", i)
500         if i == -1:
501             break
502         name = document.body[i].split()[2]
503         j = find_end_of_inset(document.body, i + 1)
504         preview_line = ""
505         option1 = ""
506         option2 = ""
507         argument = ""
508         for k in range(i + 1, j):
509             match = re.match(regex, document.body[k])
510             if match:
511                 pname = match.group(1)
512                 pvalue = match.group(2)
513                 if pname == "preview":
514                     preview_line = document.body[k]
515                 elif (commandparams_info[name][0] != "" and
516                       pname == commandparams_info[name][0]):
517                     option1 = pvalue.strip('"').replace('\\"', '"')
518                 elif (commandparams_info[name][1] != "" and
519                       pname == commandparams_info[name][1]):
520                     option2 = pvalue.strip('"').replace('\\"', '"')
521                 elif (commandparams_info[name][2] != "" and
522                       pname == commandparams_info[name][2]):
523                     argument = pvalue.strip('"').replace('\\"', '"')
524             elif document.body[k].strip() != "":
525                 document.warning("Ignoring unknown contents `%s' in command inset %s." % (document.body[k], name))
526         if name == "bibitem":
527             if option1 == "":
528                 lines = ["\\bibitem {%s}" % argument]
529             else:
530                 lines = ["\\bibitem [%s]{%s}" % (option1, argument)]
531         else:
532             if option1 == "":
533                 if option2 == "":
534                     lines = ["\\begin_inset LatexCommand \\%s{%s}" % (name, argument)]
535                 else:
536                     lines = ["\\begin_inset LatexCommand \\%s[][%s]{%s}" % (name, option2, argument)]
537             else:
538                 if option2 == "":
539                     lines = ["\\begin_inset LatexCommand \\%s[%s]{%s}" % (name, option1, argument)]
540                 else:
541                     lines = ["\\begin_inset LatexCommand \\%s[%s][%s]{%s}" % (name, option1, option2, argument)]
542         if name != "bibitem":
543             if preview_line != "":
544                 lines.append(preview_line)
545             lines.append('')
546             lines.append('\\end_inset')
547         document.body[i:j+1] = lines
548         i = j + 1
549
550
551 def revert_nomenclature(document):
552     " Convert nomenclature entry to ERT. "
553     regex = re.compile(r'(\S+)\s+(.+)')
554     i = 0
555     use_nomencl = 0
556     while 1:
557         i = find_token(document.body, "\\begin_inset LatexCommand nomenclature", i)
558         if i == -1:
559             break
560         use_nomencl = 1
561         j = find_end_of_inset(document.body, i + 1)
562         preview_line = ""
563         symbol = ""
564         description = ""
565         prefix = ""
566         for k in range(i + 1, j):
567             match = re.match(regex, document.body[k])
568             if match:
569                 name = match.group(1)
570                 value = match.group(2)
571                 if name == "preview":
572                     preview_line = document.body[k]
573                 elif name == "symbol":
574                     symbol = value.strip('"').replace('\\"', '"')
575                 elif name == "description":
576                     description = value.strip('"').replace('\\"', '"')
577                 elif name == "prefix":
578                     prefix = value.strip('"').replace('\\"', '"')
579             elif document.body[k].strip() != "":
580                 document.warning("Ignoring unknown contents `%s' in nomenclature inset." % document.body[k])
581         if prefix == "":
582             command = 'nomenclature{%s}{%s}' % (symbol, description)
583         else:
584             command = 'nomenclature[%s]{%s}{%s}' % (prefix, symbol, description)
585         document.body[i:j+1] = ['\\begin_inset ERT',
586                                 'status collapsed',
587                                 '',
588                                 '\\begin_layout %s' % document.default_layout,
589                                 '',
590                                 '',
591                                 '\\backslash',
592                                 command,
593                                 '\\end_layout',
594                                 '',
595                                 '\\end_inset']
596         i = i + 11
597     if use_nomencl and find_token(document.preamble, '\\usepackage{nomencl}[2005/09/22]', 0) == -1:
598         document.preamble.append('\\usepackage{nomencl}[2005/09/22]')
599         document.preamble.append('\\makenomenclature')
600
601
602 def revert_printnomenclature(document):
603     " Convert printnomenclature to ERT. "
604     regex = re.compile(r'(\S+)\s+(.+)')
605     i = 0
606     use_nomencl = 0
607     while 1:
608         i = find_token(document.body, "\\begin_inset LatexCommand printnomenclature", i)
609         if i == -1:
610             break
611         use_nomencl = 1
612         j = find_end_of_inset(document.body, i + 1)
613         preview_line = ""
614         labelwidth = ""
615         for k in range(i + 1, j):
616             match = re.match(regex, document.body[k])
617             if match:
618                 name = match.group(1)
619                 value = match.group(2)
620                 if name == "preview":
621                     preview_line = document.body[k]
622                 elif name == "labelwidth":
623                     labelwidth = value.strip('"').replace('\\"', '"')
624             elif document.body[k].strip() != "":
625                 document.warning("Ignoring unknown contents `%s' in printnomenclature inset." % document.body[k])
626         if labelwidth == "":
627             command = 'nomenclature{}'
628         else:
629             command = 'nomenclature[%s]' % labelwidth
630         document.body[i:j+1] = ['\\begin_inset ERT',
631                                 'status collapsed',
632                                 '',
633                                 '\\begin_layout %s' % document.default_layout,
634                                 '',
635                                 '',
636                                 '\\backslash',
637                                 command,
638                                 '\\end_layout',
639                                 '',
640                                 '\\end_inset']
641         i = i + 11
642     if use_nomencl and find_token(document.preamble, '\\usepackage{nomencl}[2005/09/22]', 0) == -1:
643         document.preamble.append('\\usepackage{nomencl}[2005/09/22]')
644         document.preamble.append('\\makenomenclature')
645
646
647 def convert_esint(document):
648     " Add \\use_esint setting to header. "
649     i = find_token(document.header, "\\cite_engine", 0)
650     if i == -1:
651         document.warning("Malformed LyX document: Missing `\\cite_engine'.")
652         return
653     # 0 is off, 1 is auto, 2 is on.
654     document.header.insert(i, '\\use_esint 0')
655
656
657 def revert_esint(document):
658     " Remove \\use_esint setting from header. "
659     i = find_token(document.header, "\\use_esint", 0)
660     if i == -1:
661         document.warning("Malformed LyX document: Missing `\\use_esint'.")
662         return
663     use_esint = document.header[i].split()[1]
664     del document.header[i]
665     # 0 is off, 1 is auto, 2 is on.
666     if (use_esint == 2):
667         document.preamble.append('\\usepackage{esint}')
668
669
670 def revert_clearpage(document):
671     " clearpage -> ERT "
672     i = 0
673     while 1:
674         i = find_token(document.body, "\\clearpage", i)
675         if i == -1:
676             break
677         document.body[i:i+1] =  ['\\begin_inset ERT',
678                                 'status collapsed',
679                                 '',
680                                 '\\begin_layout %s' % document.default_layout,
681                                 '',
682                                 '',
683                                 '\\backslash',
684                                 'clearpage',
685                                 '\\end_layout',
686                                 '',
687                                 '\\end_inset']
688     i = i + 1
689
690
691 def revert_cleardoublepage(document):
692     " cleardoublepage -> ERT "
693     i = 0
694     while 1:
695         i = find_token(document.body, "\\cleardoublepage", i)
696         if i == -1:
697             break
698         document.body[i:i+1] =  ['\\begin_inset ERT',
699                                 'status collapsed',
700                                 '',
701                                 '\\begin_layout %s' % document.default_layout,
702                                 '',
703                                 '',
704                                 '\\backslash',
705                                 'cleardoublepage',
706                                 '\\end_layout',
707                                 '',
708                                 '\\end_inset']
709     i = i + 1
710
711
712 def convert_lyxline(document):
713     " remove fontsize commands for \lyxline "
714     # The problematic is: The old \lyxline definition doesn't handle the fontsize
715     # to change the line thickness. The new definiton does this so that imported
716     # \lyxlines would have a different line thickness. The eventual fontsize command
717     # before \lyxline is therefore removed to get the same output.
718     fontsizes = ["tiny", "scriptsize", "footnotesize", "small", "normalsize",
719                  "large", "Large", "LARGE", "huge", "Huge"]
720     for n in range(0, len(fontsizes)):
721         i = 0
722         k = 0
723         while i < len(document.body):
724             i = find_token(document.body, "\\size " + fontsizes[n], i)
725             k = find_token(document.body, "\\lyxline",i)
726             # the corresponding fontsize command is always 2 lines before the \lyxline
727             if (i != -1 and k == i+2):
728                 document.body[i:i+1] = []
729             else:
730                 break
731         i = i + 1
732
733
734 def revert_encodings(document):
735     " Set new encodings to auto. "
736     encodings = ["8859-6", "8859-8", "cp437", "cp437de", "cp850", "cp852",
737                  "cp855", "cp858", "cp862", "cp865", "cp866", "cp1250",
738                  "cp1252", "cp1256", "cp1257", "latin10", "pt254", "tis620-0"]
739     i = find_token(document.header, "\\inputencoding", 0)
740     if i == -1:
741         document.header.append("\\inputencoding auto")
742     else:
743         inputenc = get_value(document.header, "\\inputencoding", i)
744         if inputenc in encodings:
745             document.header[i] = "\\inputencoding auto"
746     document.inputencoding = get_value(document.header, "\\inputencoding", 0)
747
748
749 def convert_caption(document):
750     " Convert caption layouts to caption insets. "
751     i = 0
752     while 1:
753         i = find_token(document.body, "\\begin_layout Caption", i)
754         if i == -1:
755             return
756         j = find_end_of_layout(document.body, i)
757         if j == -1:
758             document.warning("Malformed LyX document: Missing `\\end_layout'.")
759             return
760
761         document.body[j:j] = ["\\end_layout", "", "\\end_inset", "", ""]
762         document.body[i:i+1] = ["\\begin_layout %s" % document.default_layout,
763                             "\\begin_inset Caption", "",
764                             "\\begin_layout %s" % document.default_layout]
765         i = i + 1
766
767
768 def revert_caption(document):
769     " Convert caption insets to caption layouts. "
770     " This assumes that the text class has a caption style. "
771     i = 0
772     while 1:
773         i = find_token(document.body, "\\begin_inset Caption", i)
774         if i == -1:
775             return
776
777         # We either need to delete the previous \begin_layout line, or we
778         # need to end the previous layout if this inset is not in the first
779         # position of the paragraph.
780         layout_before = find_token_backwards(document.body, "\\begin_layout", i)
781         if layout_before == -1:
782             document.warning("Malformed LyX document: Missing `\\begin_layout'.")
783             return
784         layout_line = document.body[layout_before]
785         del_layout_before = True
786         l = layout_before + 1
787         while l < i:
788             if document.body[l] != "":
789                 del_layout_before = False
790                 break
791             l = l + 1
792         if del_layout_before:
793             del document.body[layout_before:i]
794             i = layout_before
795         else:
796             document.body[i:i] = ["\\end_layout", ""]
797             i = i + 2
798
799         # Find start of layout in the inset and end of inset
800         j = find_token(document.body, "\\begin_layout", i)
801         if j == -1:
802             document.warning("Malformed LyX document: Missing `\\begin_layout'.")
803             return
804         k = find_end_of_inset(document.body, i)
805         if k == -1:
806             document.warning("Malformed LyX document: Missing `\\end_inset'.")
807             return
808
809         # We either need to delete the following \end_layout line, or we need
810         # to restart the old layout if this inset is not at the paragraph end.
811         layout_after = find_token(document.body, "\\end_layout", k)
812         if layout_after == -1:
813             document.warning("Malformed LyX document: Missing `\\end_layout'.")
814             return
815         del_layout_after = True
816         l = k + 1
817         while l < layout_after:
818             if document.body[l] != "":
819                 del_layout_after = False
820                 break
821             l = l + 1
822         if del_layout_after:
823             del document.body[k+1:layout_after+1]
824         else:
825             document.body[k+1:k+1] = [layout_line, ""]
826
827         # delete \begin_layout and \end_inset and replace \begin_inset with
828         # "\begin_layout Caption". This works because we can only have one
829         # paragraph in the caption inset: The old \end_layout will be recycled.
830         del document.body[k]
831         if document.body[k] == "":
832             del document.body[k]
833         del document.body[j]
834         if document.body[j] == "":
835             del document.body[j]
836         document.body[i] = "\\begin_layout Caption"
837         if document.body[i+1] == "":
838             del document.body[i+1]
839         i = i + 1
840
841
842 ##
843 # Conversion hub
844 #
845
846 supported_versions = ["1.5.0","1.5"]
847 convert = [[246, []],
848            [247, [convert_font_settings]],
849            [248, []],
850            [249, [convert_utf8]],
851            [250, []],
852            [251, []],
853            [252, [convert_commandparams, convert_bibitem]],
854            [253, []],
855            [254, [convert_esint]],
856            [255, []],
857            [256, []],
858            [257, [convert_caption]],
859            [258, [convert_lyxline]]]
860
861 revert =  [[257, []],
862            [256, [revert_caption]],
863            [255, [revert_encodings]],
864            [254, [revert_clearpage, revert_cleardoublepage]],
865            [253, [revert_esint]],
866            [252, [revert_nomenclature, revert_printnomenclature]],
867            [251, [revert_commandparams]],
868            [250, [revert_cs_label]],
869            [249, []],
870            [248, [revert_utf8]],
871            [247, [revert_booktabs]],
872            [246, [revert_font_settings]],
873            [245, [revert_framed]]]
874
875
876 if __name__ == "__main__":
877     pass
878
879