]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_2_0.py
lyx_2_0.py: re-add comment
[lyx.git] / lib / lyx2lyx / lyx_2_0.py
1 # This file is part of lyx2lyx
2 # -*- coding: utf-8 -*-
3 # Copyright (C) 2008 José Matos  <jamatos@lyx.org>
4 #
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.
9 #
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.
14 #
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.
18
19 """ Convert files to the file format generated by lyx 2.0"""
20
21 import re, string
22 import unicodedata
23 import sys, os
24
25 from parser_tools import find_token, find_end_of, find_tokens, get_value, get_value_string
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
35 def add_to_preamble(document, text):
36     """ Add text to the preamble if it is not already there.
37     Only the first line is checked!"""
38
39     if find_token(document.preamble, text[0], 0) != -1:
40         return
41
42     document.preamble.extend(text)
43
44
45 def insert_to_preamble(index, document, text):
46     """ Insert text to the preamble at a given line"""
47
48     document.preamble.insert(index, text)
49
50
51 def read_unicodesymbols():
52     " Read the unicodesymbols list of unicode characters and corresponding commands."
53     pathname = os.path.abspath(os.path.dirname(sys.argv[0]))
54     fp = open(os.path.join(pathname.strip('lyx2lyx'), 'unicodesymbols'))
55     spec_chars = []
56     # Two backslashes, followed by some non-word character, and then a character
57     # in brackets. The idea is to check for constructs like: \"{u}, which is how
58     # they are written in the unicodesymbols file; but they can also be written
59     # as: \"u or even \" u.
60     r = re.compile(r'\\\\(\W)\{(\w)\}')
61     for line in fp.readlines():
62         if line[0] != '#' and line.strip() != "":
63             line=line.replace(' "',' ') # remove all quotation marks with spaces before
64             line=line.replace('" ',' ') # remove all quotation marks with spaces after
65             line=line.replace(r'\"','"') # replace \" by " (for characters with diaeresis)
66             try:
67                 [ucs4,command,dead] = line.split(None,2)
68                 if command[0:1] != "\\":
69                     continue
70                 spec_chars.append([command, unichr(eval(ucs4))])
71             except:
72                 continue
73             m = r.match(command)
74             if m != None:
75                 command = "\\\\"
76                 # If the character is a double-quote, then we need to escape it, too,
77                 # since it is done that way in the LyX file.
78                 if m.group(1) == "\"":
79                     command += "\\"
80                 commandbl = command
81                 command += m.group(1) + m.group(2)
82                 commandbl += m.group(1) + ' ' + m.group(2)
83                 spec_chars.append([command, unichr(eval(ucs4))])
84                 spec_chars.append([commandbl, unichr(eval(ucs4))])
85     fp.close()
86     return spec_chars
87
88
89 unicode_reps = read_unicodesymbols()
90
91
92 def put_cmd_in_ert(string):
93     for rep in unicode_reps:
94         string = string.replace(rep[1], rep[0].replace('\\\\', '\\'))
95     string = string.replace('\\', "\\backslash\n")
96     string = "\\begin_inset ERT\nstatus collapsed\n\\begin_layout Plain Layout\n" \
97       + string + "\n\\end_layout\n\\end_inset"
98     return string
99
100
101 def lyx2latex(document, lines):
102     'Convert some LyX stuff into corresponding LaTeX stuff, as best we can.'
103     # clean up multiline stuff
104     content = ""
105     ert_end = 0
106
107     for curline in range(len(lines)):
108       line = lines[curline]
109       if line.startswith("\\begin_inset ERT"):
110           # We don't want to replace things inside ERT, so figure out
111           # where the end of the inset is.
112           ert_end = find_end_of_inset(lines, curline + 1)
113           continue
114       elif line.startswith("\\begin_inset Formula"):
115           line = line[20:]
116       elif line.startswith("\\begin_inset Quotes"):
117           # For now, we do a very basic reversion. Someone who understands
118           # quotes is welcome to fix it up.
119           qtype = line[20:].strip()
120           # lang = qtype[0]
121           side = qtype[1]
122           dbls = qtype[2]
123           if side == "l":
124               if dbls == "d":
125                   line = "``"
126               else:
127                   line = "`"
128           else:
129               if dbls == "d":
130                   line = "''"
131               else:
132                   line = "'"
133       elif line.isspace() or \
134             line.startswith("\\begin_layout") or \
135             line.startswith("\\end_layout") or \
136             line.startswith("\\begin_inset") or \
137             line.startswith("\\end_inset") or \
138             line.startswith("\\lang") or \
139             line.strip() == "status collapsed" or \
140             line.strip() == "status open":
141           #skip all that stuff
142           continue
143
144       # this needs to be added to the preamble because of cases like
145       # \textmu, \textbackslash, etc.
146       add_to_preamble(document, ['% added by lyx2lyx for converted index entries',
147                                  '\\@ifundefined{textmu}',
148                                  ' {\\usepackage{textcomp}}{}'])
149       # a lossless reversion is not possible
150       # try at least to handle some common insets and settings
151       if ert_end >= curline:
152           line = line.replace(r'\backslash', r'\\')
153       else:
154           line = line.replace('&', '\\&{}')
155           line = line.replace('#', '\\#{}')
156           line = line.replace('^', '\\^{}')
157           line = line.replace('%', '\\%{}')
158           line = line.replace('_', '\\_{}')
159           line = line.replace('$', '\\${}')
160
161           # Do the LyX text --> LaTeX conversion
162           for rep in unicode_reps:
163             line = line.replace(rep[1], rep[0] + "{}")
164           line = line.replace(r'\backslash', r'\textbackslash{}')
165           line = line.replace(r'\series bold', r'\bfseries{}').replace(r'\series default', r'\mdseries{}')
166           line = line.replace(r'\shape italic', r'\itshape{}').replace(r'\shape smallcaps', r'\scshape{}')
167           line = line.replace(r'\shape slanted', r'\slshape{}').replace(r'\shape default', r'\upshape{}')
168           line = line.replace(r'\emph on', r'\em{}').replace(r'\emph default', r'\em{}')
169           line = line.replace(r'\noun on', r'\scshape{}').replace(r'\noun default', r'\upshape{}')
170           line = line.replace(r'\bar under', r'\underbar{').replace(r'\bar default', r'}')
171           line = line.replace(r'\family sans', r'\sffamily{}').replace(r'\family default', r'\normalfont{}')
172           line = line.replace(r'\family typewriter', r'\ttfamily{}').replace(r'\family roman', r'\rmfamily{}')
173           line = line.replace(r'\InsetSpace ', r'').replace(r'\SpecialChar ', r'')
174       content += line
175     return content
176
177
178 def latex_length(string):
179     'Convert lengths to their LaTeX representation.'
180     i = 0
181     percent = False
182     # the string has always the form
183     # ValueUnit+ValueUnit-ValueUnit
184     # the + and - lengths are optional
185     # the + is always before the -
186     i = string.find("text%")
187     if i > -1:
188         percent = True
189         minus = string.rfind("-", 0, i)
190         plus = string.rfind("+", 0, i)
191         if plus == -1 and minus == -1:
192             value = string[:i]
193             value = str(float(value)/100)
194             end = string[i+5:]
195             string = value + "\\textwidth" + end
196         if plus > minus:
197             value = string[plus+1:i]
198             value = str(float(value)/100)
199             begin = string[:plus+1]
200             end = string[i+5:]
201             string = begin + value + "\\textwidth" + end
202         if plus < minus:
203             value = string[minus+1:i]
204             value = str(float(value)/100)
205             begin = string[:minus+1]
206             string = begin + value + "\\textwidth"
207     i = string.find("col%")
208     if i > -1:
209         percent = True
210         minus = string.rfind("-", 0, i)
211         plus = string.rfind("+", 0, i)
212         if plus == -1 and minus == -1:
213             value = string[:i]
214             value = str(float(value)/100)
215             end = string[i+4:]
216             string = value + "\\columnwidth" + end
217         if plus > minus:
218             value = string[plus+1:i]
219             value = str(float(value)/100)
220             begin = string[:plus+1]
221             end = string[i+4:]
222             string = begin + value + "\\columnwidth" + end
223         if plus < minus:
224             value = string[minus+1:i]
225             value = str(float(value)/100)
226             begin = string[:minus+1]
227             string = begin + value + "\\columnwidth"
228     i = string.find("page%")
229     if i > -1:
230         percent = True
231         minus = string.rfind("-", 0, i)
232         plus = string.rfind("+", 0, i)
233         if plus == -1 and minus == -1:
234             value = string[:i]
235             value = str(float(value)/100)
236             end = string[i+5:]
237             string = value + "\\paperwidth" + end
238         if plus > minus:
239             value = string[plus+1:i]
240             value = str(float(value)/100)
241             begin = string[:plus+1]
242             end = string[i+5:]
243             string = begin + value + "\\paperwidth" + end
244         if plus < minus:
245             value = string[minus+1:i]
246             value = str(float(value)/100)
247             begin = string[:minus+1]
248             string = begin + value + "\\paperwidth"
249     i = string.find("line%")
250     if i > -1:
251         percent = True
252         minus = string.rfind("-", 0, i)
253         plus = string.rfind("+", 0, i)
254         if plus == -1 and minus == -1:
255             value = string[:i]
256             value = str(float(value)/100)
257             end = string[i+5:]
258             string = value + "\\linewidth" + end
259         if plus > minus:
260             value = string[plus+1:i]
261             value = str(float(value)/100)
262             begin = string[:plus+1]
263             end = string[i+5:]
264             string = begin + value + "\\linewidth" + end
265         if plus < minus:
266             value = string[minus+1:i]
267             value = str(float(value)/100)
268             begin = string[:minus+1]
269             string = begin + value + "\\linewidth"
270     i = string.find("theight%")
271     if i > -1:
272         percent = True
273         minus = string.rfind("-", 0, i)
274         plus = string.rfind("+", 0, i)
275         if plus == -1 and minus == -1:
276             value = string[:i]
277             value = str(float(value)/100)
278             end = string[i+8:]
279             string = value + "\\textheight" + end
280         if plus > minus:
281             value = string[plus+1:i]
282             value = str(float(value)/100)
283             begin = string[:plus+1]
284             end = string[i+8:]
285             string = begin + value + "\\textheight" + end
286         if plus < minus:
287             value = string[minus+1:i]
288             value = str(float(value)/100)
289             begin = string[:minus+1]
290             string = begin + value + "\\textheight"
291     i = string.find("pheight%")
292     if i > -1:
293         percent = True
294         minus = string.rfind("-", 0, i)
295         plus = string.rfind("+", 0, i)
296         if plus == -1 and minus == -1:
297             value = string[:i]
298             value = str(float(value)/100)
299             end = string[i+8:]
300             string = value + "\\paperheight" + end
301         if plus > minus:
302             value = string[plus+1:i]
303             value = str(float(value)/100)
304             begin = string[:plus+1]
305             end = string[i+8:]
306             string = begin + value + "\\paperheight" + end
307         if plus < minus:
308             value = string[minus+1:i]
309             value = str(float(value)/100)
310             begin = string[:minus+1]
311             string = begin + value + "\\paperheight"
312     if percent ==  False:
313         return "False," + string
314     else:
315         string = string.replace("+", " plus ")
316         string = string.replace("-", " minus ")
317         return "True," + string
318         
319
320 ####################################################################
321
322
323 def revert_swiss(document):
324     " Set language german-ch to ngerman "
325     i = 0
326     if document.language == "german-ch":
327         document.language = "ngerman"
328         i = find_token(document.header, "\\language", 0)
329         if i != -1:
330             document.header[i] = "\\language ngerman"
331     j = 0
332     while True:
333         j = find_token(document.body, "\\lang german-ch", j)
334         if j == -1:
335             return
336         document.body[j] = document.body[j].replace("\\lang german-ch", "\\lang ngerman")
337         j = j + 1
338
339
340 def revert_tabularvalign(document):
341    " Revert the tabular valign option "
342    i = 0
343    while True:
344        i = find_token(document.body, "\\begin_inset Tabular", i)
345        if i == -1:
346            return
347        j = find_end_of_inset(document.body, i)
348        if j == -1:
349            document.warning("Malformed LyX document: Could not find end of tabular.")
350            i = j
351            continue
352        # don't set a box for longtables, only delete tabularvalignment
353        # the alignment is 2 lines below \\begin_inset Tabular
354        p = document.body[i+2].find("islongtable")
355        if p > -1:
356            q = document.body[i+2].find("tabularvalignment")
357            if q > -1:
358                document.body[i+2] = document.body[i+2][:q-1]
359                document.body[i+2] = document.body[i+2] + '>'
360            i = i + 1
361
362        # when no longtable
363        if p == -1:
364          tabularvalignment = 'c'
365          # which valignment is specified?
366          m = document.body[i+2].find('tabularvalignment="top"')
367          if m > -1:
368              tabularvalignment = 't'
369          m = document.body[i+2].find('tabularvalignment="bottom"')
370          if m > -1:
371              tabularvalignment = 'b'
372          # delete tabularvalignment
373          q = document.body[i+2].find("tabularvalignment")
374          if q > -1:
375              document.body[i+2] = document.body[i+2][:q-1]
376              document.body[i+2] = document.body[i+2] + '>'
377
378          # don't add a box when centered
379          if tabularvalignment == 'c':
380              i = j
381              continue
382          subst = ['\\end_layout', '\\end_inset']
383          document.body[j+1:j+1] = subst # just inserts those lines
384          subst = ['\\begin_inset Box Frameless',
385              'position "' + tabularvalignment +'"',
386              'hor_pos "c"',
387              'has_inner_box 1',
388              'inner_pos "c"',
389              'use_parbox 0',
390              # we don't know the width, assume 50%
391              'width "50col%"',
392              'special "none"',
393              'height "1in"',
394              'height_special "totalheight"',
395              'status open',
396              '',
397              '\\begin_layout Plain Layout']
398          document.body[i:i] = subst # this just inserts the array at i
399          i += len(subst) + 2 # adjust i to save a few cycles
400
401
402 def revert_phantom(document):
403     " Reverts phantom to ERT "
404     i = 0
405     j = 0
406     while True:
407       i = find_token(document.body, "\\begin_inset Phantom Phantom", i)
408       if i == -1:
409           return
410       substi = document.body[i].replace('\\begin_inset Phantom Phantom', \
411                 '\\begin_inset ERT\nstatus collapsed\n\n' \
412                 '\\begin_layout Plain Layout\n\n\n\\backslash\n' \
413                 'phantom{\n\\end_layout\n\n\\end_inset\n')
414       substi = substi.split('\n')
415       document.body[i : i+4] = substi
416       i += len(substi)
417       j = find_token(document.body, "\\end_layout", i)
418       if j == -1:
419           document.warning("Malformed LyX document: Could not find end of Phantom inset.")
420           return
421       substj = document.body[j].replace('\\end_layout', \
422                 '\\size default\n\n\\begin_inset ERT\nstatus collapsed\n\n' \
423                 '\\begin_layout Plain Layout\n\n' \
424                 '}\n\\end_layout\n\n\\end_inset\n')
425       substj = substj.split('\n')
426       document.body[j : j+4] = substj
427       i += len(substj)
428
429
430 def revert_hphantom(document):
431     " Reverts hphantom to ERT "
432     i = 0
433     j = 0
434     while True:
435       i = find_token(document.body, "\\begin_inset Phantom HPhantom", i)
436       if i == -1:
437           return
438       substi = document.body[i].replace('\\begin_inset Phantom HPhantom', \
439                 '\\begin_inset ERT\nstatus collapsed\n\n' \
440                 '\\begin_layout Plain Layout\n\n\n\\backslash\n' \
441                 'hphantom{\n\\end_layout\n\n\\end_inset\n')
442       substi = substi.split('\n')
443       document.body[i : i+4] = substi
444       i += len(substi)
445       j = find_token(document.body, "\\end_layout", i)
446       if j == -1:
447           document.warning("Malformed LyX document: Could not find end of HPhantom inset.")
448           return
449       substj = document.body[j].replace('\\end_layout', \
450                 '\\size default\n\n\\begin_inset ERT\nstatus collapsed\n\n' \
451                 '\\begin_layout Plain Layout\n\n' \
452                 '}\n\\end_layout\n\n\\end_inset\n')
453       substj = substj.split('\n')
454       document.body[j : j+4] = substj
455       i += len(substj)
456
457
458 def revert_vphantom(document):
459     " Reverts vphantom to ERT "
460     i = 0
461     j = 0
462     while True:
463       i = find_token(document.body, "\\begin_inset Phantom VPhantom", i)
464       if i == -1:
465           return
466       substi = document.body[i].replace('\\begin_inset Phantom VPhantom', \
467                 '\\begin_inset ERT\nstatus collapsed\n\n' \
468                 '\\begin_layout Plain Layout\n\n\n\\backslash\n' \
469                 'vphantom{\n\\end_layout\n\n\\end_inset\n')
470       substi = substi.split('\n')
471       document.body[i : i+4] = substi
472       i += len(substi)
473       j = find_token(document.body, "\\end_layout", i)
474       if j == -1:
475           document.warning("Malformed LyX document: Could not find end of VPhantom inset.")
476           return
477       substj = document.body[j].replace('\\end_layout', \
478                 '\\size default\n\n\\begin_inset ERT\nstatus collapsed\n\n' \
479                 '\\begin_layout Plain Layout\n\n' \
480                 '}\n\\end_layout\n\n\\end_inset\n')
481       substj = substj.split('\n')
482       document.body[j : j+4] = substj
483       i += len(substj)
484
485
486 def revert_xetex(document):
487     " Reverts documents that use XeTeX "
488     i = find_token(document.header, '\\use_xetex', 0)
489     if i == -1:
490         document.warning("Malformed LyX document: Missing \\use_xetex.")
491         return
492     if get_value(document.header, "\\use_xetex", i) == 'false':
493         del document.header[i]
494         return
495     del document.header[i]
496     # 1.) set doc encoding to utf8-plain
497     i = find_token(document.header, "\\inputencoding", 0)
498     if i == -1:
499         document.warning("Malformed LyX document: Missing \\inputencoding.")
500     document.header[i] = "\\inputencoding utf8-plain"
501     # 2.) check font settings
502     l = find_token(document.header, "\\font_roman", 0)
503     if l == -1:
504         document.warning("Malformed LyX document: Missing \\font_roman.")
505     line = document.header[l]
506     l = re.compile(r'\\font_roman (.*)$')
507     m = l.match(line)
508     roman = m.group(1)
509     l = find_token(document.header, "\\font_sans", 0)
510     if l == -1:
511         document.warning("Malformed LyX document: Missing \\font_sans.")
512     line = document.header[l]
513     l = re.compile(r'\\font_sans (.*)$')
514     m = l.match(line)
515     sans = m.group(1)
516     l = find_token(document.header, "\\font_typewriter", 0)
517     if l == -1:
518         document.warning("Malformed LyX document: Missing \\font_typewriter.")
519     line = document.header[l]
520     l = re.compile(r'\\font_typewriter (.*)$')
521     m = l.match(line)
522     typewriter = m.group(1)
523     osf = get_value(document.header, '\\font_osf', 0) == "true"
524     sf_scale = float(get_value(document.header, '\\font_sf_scale', 0))
525     tt_scale = float(get_value(document.header, '\\font_tt_scale', 0))
526     # 3.) set preamble stuff
527     pretext = '%% This document must be processed with xelatex!\n'
528     pretext += '\\usepackage{fontspec}\n'
529     if roman != "default":
530         pretext += '\\setmainfont[Mapping=tex-text]{' + roman + '}\n'
531     if sans != "default":
532         pretext += '\\setsansfont['
533         if sf_scale != 100:
534             pretext += 'Scale=' + str(sf_scale / 100) + ','
535         pretext += 'Mapping=tex-text]{' + sans + '}\n'
536     if typewriter != "default":
537         pretext += '\\setmonofont'
538         if tt_scale != 100:
539             pretext += '[Scale=' + str(tt_scale / 100) + ']'
540         pretext += '{' + typewriter + '}\n'
541     if osf:
542         pretext += '\\defaultfontfeatures{Numbers=OldStyle}\n'
543     pretext += '\usepackage{xunicode}\n'
544     pretext += '\usepackage{xltxtra}\n'
545     insert_to_preamble(0, document, pretext)
546     # 4.) reset font settings
547     i = find_token(document.header, "\\font_roman", 0)
548     if i == -1:
549         document.warning("Malformed LyX document: Missing \\font_roman.")
550     document.header[i] = "\\font_roman default"
551     i = find_token(document.header, "\\font_sans", 0)
552     if i == -1:
553         document.warning("Malformed LyX document: Missing \\font_sans.")
554     document.header[i] = "\\font_sans default"
555     i = find_token(document.header, "\\font_typewriter", 0)
556     if i == -1:
557         document.warning("Malformed LyX document: Missing \\font_typewriter.")
558     document.header[i] = "\\font_typewriter default"
559     i = find_token(document.header, "\\font_osf", 0)
560     if i == -1:
561         document.warning("Malformed LyX document: Missing \\font_osf.")
562     document.header[i] = "\\font_osf false"
563     i = find_token(document.header, "\\font_sc", 0)
564     if i == -1:
565         document.warning("Malformed LyX document: Missing \\font_sc.")
566     document.header[i] = "\\font_sc false"
567     i = find_token(document.header, "\\font_sf_scale", 0)
568     if i == -1:
569         document.warning("Malformed LyX document: Missing \\font_sf_scale.")
570     document.header[i] = "\\font_sf_scale 100"
571     i = find_token(document.header, "\\font_tt_scale", 0)
572     if i == -1:
573         document.warning("Malformed LyX document: Missing \\font_tt_scale.")
574     document.header[i] = "\\font_tt_scale 100"
575
576
577 def revert_outputformat(document):
578     " Remove default output format param "
579     i = find_token(document.header, '\\default_output_format', 0)
580     if i == -1:
581         document.warning("Malformed LyX document: Missing \\default_output_format.")
582         return
583     del document.header[i]
584
585
586 def revert_backgroundcolor(document):
587     " Reverts background color to preamble code "
588     i = 0
589     colorcode = ""
590     while True:
591       i = find_token(document.header, "\\backgroundcolor", i)
592       if i == -1:
593           return
594       colorcode = get_value(document.header, '\\backgroundcolor', 0)
595       del document.header[i]
596       # don't clutter the preamble if backgroundcolor is not set
597       if colorcode == "#ffffff":
598           continue
599       # the color code is in the form #rrggbb where every character denotes a hex number
600       # convert the string to an int
601       red = string.atoi(colorcode[1:3],16)
602       # we want the output "0.5" for the value "127" therefore add here
603       if red != 0:
604           red = red + 1
605       redout = float(red) / 256
606       green = string.atoi(colorcode[3:5],16)
607       if green != 0:
608           green = green + 1
609       greenout = float(green) / 256
610       blue = string.atoi(colorcode[5:7],16)
611       if blue != 0:
612           blue = blue + 1
613       blueout = float(blue) / 256
614       # write the preamble
615       insert_to_preamble(0, document,
616                            '% Commands inserted by lyx2lyx to set the background color\n'
617                            + '\\@ifundefined{definecolor}{\\usepackage{color}}{}\n'
618                            + '\\definecolor{page_backgroundcolor}{rgb}{'
619                            + str(redout) + ', ' + str(greenout)
620                            + ', ' + str(blueout) + '}\n'
621                            + '\\pagecolor{page_backgroundcolor}\n')
622
623
624 def revert_splitindex(document):
625     " Reverts splitindex-aware documents "
626     i = find_token(document.header, '\\use_indices', 0)
627     if i == -1:
628         document.warning("Malformed LyX document: Missing \\use_indices.")
629         return
630     indices = get_value(document.header, "\\use_indices", i)
631     preamble = ""
632     if indices == "true":
633          preamble += "\\usepackage{splitidx}\n"
634     del document.header[i]
635     i = 0
636     while True:
637         i = find_token(document.header, "\\index", i)
638         if i == -1:
639             break
640         k = find_token(document.header, "\\end_index", i)
641         if k == -1:
642             document.warning("Malformed LyX document: Missing \\end_index.")
643             return
644         line = document.header[i]
645         l = re.compile(r'\\index (.*)$')
646         m = l.match(line)
647         iname = m.group(1)
648         ishortcut = get_value(document.header, '\\shortcut', i, k)
649         if ishortcut != "" and indices == "true":
650             preamble += "\\newindex[" + iname + "]{" + ishortcut + "}\n"
651         del document.header[i:k+1]
652         i = 0
653     if preamble != "":
654         insert_to_preamble(0, document, preamble)
655     i = 0
656     while True:
657         i = find_token(document.body, "\\begin_inset Index", i)
658         if i == -1:
659             break
660         line = document.body[i]
661         l = re.compile(r'\\begin_inset Index (.*)$')
662         m = l.match(line)
663         itype = m.group(1)
664         if itype == "idx" or indices == "false":
665             document.body[i] = "\\begin_inset Index"
666         else:
667             k = find_end_of_inset(document.body, i)
668             if k == -1:
669                  return
670             content = lyx2latex(document, document.body[i:k])
671             # escape quotes
672             content = content.replace('"', r'\"')
673             subst = [put_cmd_in_ert("\\sindex[" + itype + "]{" + content + "}")]
674             document.body[i:k+1] = subst
675         i = i + 1
676     i = 0
677     while True:
678         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
679         if i == -1:
680             return
681         k = find_end_of_inset(document.body, i)
682         ptype = get_value(document.body, 'type', i, k).strip('"')
683         if ptype == "idx":
684             j = find_token(document.body, "type", i, k)
685             del document.body[j]
686         elif indices == "false":
687             del document.body[i:k+1]
688         else:
689             subst = [put_cmd_in_ert("\\printindex[" + ptype + "]{}")]
690             document.body[i:k+1] = subst
691         i = i + 1
692
693
694 def convert_splitindex(document):
695     " Converts index and printindex insets to splitindex-aware format "
696     i = 0
697     while True:
698         i = find_token(document.body, "\\begin_inset Index", i)
699         if i == -1:
700             break
701         document.body[i] = document.body[i].replace("\\begin_inset Index",
702             "\\begin_inset Index idx")
703         i = i + 1
704     i = 0
705     while True:
706         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
707         if i == -1:
708             return
709         if document.body[i + 1].find('LatexCommand printindex') == -1:
710             document.warning("Malformed LyX document: Incomplete printindex inset.")
711             return
712         subst = ["LatexCommand printindex", 
713             "type \"idx\""]
714         document.body[i + 1:i + 2] = subst
715         i = i + 1
716
717
718 def revert_subindex(document):
719     " Reverts \\printsubindex CommandInset types "
720     i = find_token(document.header, '\\use_indices', 0)
721     if i == -1:
722         document.warning("Malformed LyX document: Missing \\use_indices.")
723         return
724     indices = get_value(document.header, "\\use_indices", i)
725     i = 0
726     while True:
727         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
728         if i == -1:
729             return
730         k = find_end_of_inset(document.body, i)
731         ctype = get_value(document.body, 'LatexCommand', i, k)
732         if ctype != "printsubindex":
733             i = i + 1
734             continue
735         ptype = get_value(document.body, 'type', i, k).strip('"')
736         if indices == "false":
737             del document.body[i:k+1]
738         else:
739             subst = [put_cmd_in_ert("\\printsubindex[" + ptype + "]{}")]
740             document.body[i:k+1] = subst
741         i = i + 1
742
743
744 def revert_printindexall(document):
745     " Reverts \\print[sub]index* CommandInset types "
746     i = find_token(document.header, '\\use_indices', 0)
747     if i == -1:
748         document.warning("Malformed LyX document: Missing \\use_indices.")
749         return
750     indices = get_value(document.header, "\\use_indices", i)
751     i = 0
752     while True:
753         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
754         if i == -1:
755             return
756         k = find_end_of_inset(document.body, i)
757         ctype = get_value(document.body, 'LatexCommand', i, k)
758         if ctype != "printindex*" and ctype != "printsubindex*":
759             i = i + 1
760             continue
761         if indices == "false":
762             del document.body[i:k+1]
763         else:
764             subst = [put_cmd_in_ert("\\" + ctype + "{}")]
765             document.body[i:k+1] = subst
766         i = i + 1
767
768
769 def revert_strikeout(document):
770     " Reverts \\strikeout character style "
771     while True:
772         i = find_token(document.body, '\\strikeout', 0)
773         if i == -1:
774             return
775         del document.body[i]
776
777
778 def revert_uulinewave(document):
779     " Reverts \\uuline, and \\uwave character styles "
780     while True:
781         i = find_token(document.body, '\\uuline', 0)
782         if i == -1:
783             break
784         del document.body[i]
785     while True:
786         i = find_token(document.body, '\\uwave', 0)
787         if i == -1:
788             return
789         del document.body[i]
790
791
792 def revert_ulinelatex(document):
793     " Reverts \\uline character style "
794     i = find_token(document.body, '\\bar under', 0)
795     if i == -1:
796         return
797     insert_to_preamble(0, document,
798             '% Commands inserted by lyx2lyx for proper underlining\n'
799             + '\\PassOptionsToPackage{normalem}{ulem}\n'
800             + '\\usepackage{ulem}\n'
801             + '\\let\\cite@rig\\cite\n'
802             + '\\newcommand{\\b@xcite}[2][\\%]{\\def\\def@pt{\\%}\\def\\pas@pt{#1}\n'
803             + '  \\mbox{\\ifx\\def@pt\\pas@pt\\cite@rig{#2}\\else\\cite@rig[#1]{#2}\\fi}}\n'
804             + '\\renewcommand{\\underbar}[1]{{\\let\\cite\\b@xcite\\uline{#1}}}\n')
805
806
807 def revert_custom_processors(document):
808     " Remove bibtex_command and index_command params "
809     i = find_token(document.header, '\\bibtex_command', 0)
810     if i == -1:
811         document.warning("Malformed LyX document: Missing \\bibtex_command.")
812         return
813     del document.header[i]
814     i = find_token(document.header, '\\index_command', 0)
815     if i == -1:
816         document.warning("Malformed LyX document: Missing \\index_command.")
817         return
818     del document.header[i]
819
820
821 def convert_nomencl_width(document):
822     " Add set_width param to nomencl_print "
823     i = 0
824     while True:
825       i = find_token(document.body, "\\begin_inset CommandInset nomencl_print", i)
826       if i == -1:
827         break
828       document.body.insert(i + 2, "set_width \"none\"")
829       i = i + 1
830
831
832 def revert_nomencl_width(document):
833     " Remove set_width param from nomencl_print "
834     i = 0
835     while True:
836       i = find_token(document.body, "\\begin_inset CommandInset nomencl_print", i)
837       if i == -1:
838         break
839       j = find_end_of_inset(document.body, i)
840       l = find_token(document.body, "set_width", i, j)
841       if l == -1:
842             document.warning("Can't find set_width option for nomencl_print!")
843             i = j
844             continue
845       del document.body[l]
846       i = i + 1
847
848
849 def revert_nomencl_cwidth(document):
850     " Remove width param from nomencl_print "
851     i = 0
852     while True:
853       i = find_token(document.body, "\\begin_inset CommandInset nomencl_print", i)
854       if i == -1:
855         break
856       j = find_end_of_inset(document.body, i)
857       l = find_token(document.body, "width", i, j)
858       if l == -1:
859             document.warning("Can't find width option for nomencl_print!")
860             i = j
861             continue
862       width = get_value(document.body, "width", i, j).strip('"')
863       del document.body[l]
864       add_to_preamble(document, ["\\setlength{\\nomlabelwidth}{" + width + "}"])
865       i = i + 1
866
867
868 def revert_applemac(document):
869     " Revert applemac encoding to auto "
870     i = 0
871     if document.encoding == "applemac":
872         document.encoding = "auto"
873         i = find_token(document.header, "\\encoding", 0)
874         if i != -1:
875             document.header[i] = "\\encoding auto"
876
877
878 def revert_longtable_align(document):
879     " Remove longtable alignment setting "
880     i = 0
881     j = 0
882     while True:
883       i = find_token(document.body, "\\begin_inset Tabular", i)
884       if i == -1:
885           break
886       # the alignment is 2 lines below \\begin_inset Tabular
887       j = document.body[i+2].find("longtabularalignment")
888       if j == -1:
889           break
890       document.body[i+2] = document.body[i+2][:j-1]
891       document.body[i+2] = document.body[i+2] + '>'
892       i = i + 1
893
894
895 def revert_branch_filename(document):
896     " Remove \\filename_suffix parameter from branches "
897     i = 0
898     while True:
899         i = find_token(document.header, "\\filename_suffix", i)
900         if i == -1:
901             return
902         del document.header[i]
903
904
905 def revert_paragraph_indentation(document):
906     " Revert custom paragraph indentation to preamble code "
907     i = 0
908     while True:
909       i = find_token(document.header, "\\paragraph_indentation", i)
910       if i == -1:
911           break
912       # only remove the preamble line when default
913       # otherwise also write the value to the preamble  
914       j = document.header[i].find("default")
915       if j > -1:
916           del document.header[i]
917           break
918       else:
919           # search for the beginning of the value via the space
920           j = document.header[i].find(" ")
921           length = document.header[i][j+1:]
922           # handle percent lengths
923           length = latex_length(length)
924           # latex_length returns "bool,length"
925           k = length.find(",")
926           length = length[k+1:]
927           add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
928           add_to_preamble(document, ["\\setlength{\\parindent}{" + length + "}"])
929           del document.header[i]
930       i = i + 1
931
932
933 def revert_percent_skip_lengths(document):
934     " Revert relative lengths for paragraph skip separation to preamble code "
935     i = 0
936     while True:
937       i = find_token(document.header, "\\defskip", i)
938       if i == -1:
939           break
940       # only revert when a custom length was set and when
941       # it used a percent length
942       j = document.header[i].find("smallskip")
943       k = document.header[i].find("medskip")
944       l = document.header[i].find("bigskip")
945       if (j > -1) or (k > -1) or (l > -1):
946           break
947       else:
948           # search for the beginning of the value via the space
949           j = document.header[i].find(" ")
950           length = document.header[i][j+1:]
951           # handle percent lengths
952           length = latex_length(length)
953           # latex_length returns "bool,length"
954           l = length.find(",")
955           percent = length[:l]
956           length = length[l+1:]
957           if percent == "True":
958               add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
959               add_to_preamble(document, ["\\setlength{\\parskip}{" + length + "}"])
960               # set defskip to medskip as default
961               document.header[i] = "\\defskip medskip"
962       i = i + 1
963
964
965 def revert_percent_vspace_lengths(document):
966     " Revert relative VSpace lengths to ERT "
967     i = 0
968     while True:
969       i = find_token(document.body, "\\begin_inset VSpace", i)
970       if i == -1:
971           break
972       # only revert when a custom length was set and when
973       # it used a percent length
974       j = document.body[i].find("defskip")
975       k = document.body[i].find("smallskip")
976       l = document.body[i].find("medskip")
977       m = document.body[i].find("bigskip")
978       n = document.body[i].find("vfill")
979       if (j > -1) or (k > -1) or (l > -1) or (m > -1) or (n > -1):
980           break
981       else:
982           # search for the beginning of the value via the last space
983           o = document.body[i].rfind(" ")
984           length = document.body[i][o+1:]
985           # check if the space has a star (protected space)
986           p = document.body[i].rfind("*")
987           if p > -1:
988               length = length[:-1]
989           # handle percent lengths
990           length = latex_length(length)
991           # latex_length returns "bool,length"
992           q = length.find(",")
993           percent = length[:q]
994           length = length[q+1:]
995           # revert the VSpace inset to ERT
996           if percent == "True":
997               if p > -1:
998                   subst = [put_cmd_in_ert("\\vspace*{" + length + "}")]
999               else:
1000                   subst = [put_cmd_in_ert("\\vspace{" + length + "}")]
1001               document.body[i:i+2] = subst
1002       i = i + 1
1003
1004
1005 def revert_percent_hspace_lengths(document):
1006     " Revert relative HSpace lengths to ERT "
1007     i = 0
1008     j = 0
1009     while True:
1010       i = find_token(document.body, "\\begin_inset space \hspace{}", i)
1011       if i == -1:
1012           j = find_token(document.body, "\\begin_inset space \hspace*{}", j)
1013           if j == -1:
1014               break
1015           else:
1016               star = True
1017               i = j
1018       else:
1019           star = False
1020       # only revert when a custom length was set and when
1021       # it used a percent length
1022       o = document.body[i+1].find("\\length")
1023       if o == -1:
1024           document.warning("Error: Cannot find lenght for \\hspace!")
1025           break
1026       # search for the beginning of the value via the space
1027       k = document.body[i+1].find(" ")
1028       length = document.body[i+1][k+1:]
1029       # handle percent lengths
1030       length = latex_length(length)
1031       # latex_length returns "bool,length"
1032       m = length.find(",")
1033       percent = length[:m]
1034       length = length[m+1:]
1035       # revert the HSpace inset to ERT
1036       if percent == "True":
1037           if star == True:
1038               subst = [put_cmd_in_ert("\\hspace*{" + length + "}")]
1039           else:
1040               subst = [put_cmd_in_ert("\\hspace{" + length + "}")]
1041           document.body[i:i+3] = subst
1042       i = i + 2
1043       j = i
1044
1045
1046 def revert_hspace_glue_lengths(document):
1047     " Revert HSpace glue lengths to ERT "
1048     i = 0
1049     j = 0
1050     while True:
1051       i = find_token(document.body, "\\begin_inset space \hspace{}", i)
1052       if i == -1:
1053           j = find_token(document.body, "\\begin_inset space \hspace*{}", j)
1054           if j == -1:
1055               break
1056           else:
1057               star = True
1058               i = j
1059       else:
1060           star = False
1061       o = document.body[i+1].find("\\length")
1062       if o == -1:
1063           document.warning("Error: Cannot find lenght for \\hspace!")
1064           break
1065       # search for the beginning of the value via the space
1066       k = document.body[i+1].find(" ")
1067       length = document.body[i+1][k+1:]
1068       # only revert when the length contains a plus or minus
1069       l = length.find("+")
1070       if l == -1:
1071           l = length.find("-")
1072           if l == -1:
1073               break
1074       # handle percent lengths
1075       length = latex_length(length)
1076       # latex_length returns "bool,length"
1077       m = length.find(",")
1078       length = length[m+1:]
1079       # revert the HSpace inset to ERT
1080       # allow leading -
1081       if length.rfind("-") <> 0 or (length.rfind("-") == 0 and length.rfind("+") > -1):
1082           if star == True:
1083               subst = [put_cmd_in_ert("\\hspace*{" + length + "}")]
1084           else:
1085               subst = [put_cmd_in_ert("\\hspace{" + length + "}")]
1086           document.body[i:i+3] = subst
1087       i = i + 2
1088       j = i
1089
1090
1091 ##
1092 # Conversion hub
1093 #
1094
1095 supported_versions = ["2.0.0","2.0"]
1096 convert = [[346, []],
1097            [347, []],
1098            [348, []],
1099            [349, []],
1100            [350, []],
1101            [351, []],
1102            [352, [convert_splitindex]],
1103            [353, []],
1104            [354, []],
1105            [355, []],
1106            [356, []],
1107            [357, []],
1108            [358, []],
1109            [359, [convert_nomencl_width]],
1110            [360, []],
1111            [361, []],
1112            [362, []],
1113            [363, []],
1114            [364, []],
1115            [365, []],
1116            [366, []],
1117            [367, []],
1118            [368, []]
1119           ]
1120
1121 revert =  [[367, [revert_hspace_glue_lengths]],
1122            [366, [revert_percent_vspace_lengths, revert_percent_hspace_lengths]],
1123            [365, [revert_percent_skip_lengths]],
1124            [364, [revert_paragraph_indentation]],
1125            [363, [revert_branch_filename]],
1126            [362, [revert_longtable_align]],
1127            [361, [revert_applemac]],
1128            [360, []],
1129            [359, [revert_nomencl_cwidth]],
1130            [358, [revert_nomencl_width]],
1131            [357, [revert_custom_processors]],
1132            [356, [revert_ulinelatex]],
1133            [355, [revert_uulinewave]],
1134            [354, [revert_strikeout]],
1135            [353, [revert_printindexall]],
1136            [352, [revert_subindex]],
1137            [351, [revert_splitindex]],
1138            [350, [revert_backgroundcolor]],
1139            [349, [revert_outputformat]],
1140            [348, [revert_xetex]],
1141            [347, [revert_phantom, revert_hphantom, revert_vphantom]],
1142            [346, [revert_tabularvalign]],
1143            [345, [revert_swiss]]
1144           ]
1145
1146
1147 if __name__ == "__main__":
1148     pass