]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_2_0.py
* lyx_2_0.py (revert_percent_hspace_lengths, revert_hspace_glue_lengths):
[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     # replace + and -, but only when the - is not the first character
313     string = string.replace("+", " plus ")
314     if string.find("-") == 0:
315         minusstring = string[1:]
316         minusstring = minusstring.replace("-", " minus ")
317         string = "-" + minusstring
318     else:
319         string = string.replace("-", " minus ")
320     # handle the case that "+-1mm" was used because LaTeX only understands
321     # "plus 1mm minus 1mm"
322     if string.find("plus  minus"):
323         lastvaluepos = string.rfind(" ")
324         lastvalue = string[lastvaluepos:]
325         string = string.replace("  ", lastvalue + " ")
326     if percent ==  False:
327         return "False," + string
328     else:
329         return "True," + string
330         
331
332 ####################################################################
333
334
335 def revert_swiss(document):
336     " Set language german-ch to ngerman "
337     i = 0
338     if document.language == "german-ch":
339         document.language = "ngerman"
340         i = find_token(document.header, "\\language", 0)
341         if i != -1:
342             document.header[i] = "\\language ngerman"
343     j = 0
344     while True:
345         j = find_token(document.body, "\\lang german-ch", j)
346         if j == -1:
347             return
348         document.body[j] = document.body[j].replace("\\lang german-ch", "\\lang ngerman")
349         j = j + 1
350
351
352 def revert_tabularvalign(document):
353    " Revert the tabular valign option "
354    i = 0
355    while True:
356        i = find_token(document.body, "\\begin_inset Tabular", i)
357        if i == -1:
358            return
359        j = find_end_of_inset(document.body, i)
360        if j == -1:
361            document.warning("Malformed LyX document: Could not find end of tabular.")
362            i = j
363            continue
364        # don't set a box for longtables, only delete tabularvalignment
365        # the alignment is 2 lines below \\begin_inset Tabular
366        p = document.body[i+2].find("islongtable")
367        if p > -1:
368            q = document.body[i+2].find("tabularvalignment")
369            if q > -1:
370                document.body[i+2] = document.body[i+2][:q-1]
371                document.body[i+2] = document.body[i+2] + '>'
372            i = i + 1
373
374        # when no longtable
375        if p == -1:
376          tabularvalignment = 'c'
377          # which valignment is specified?
378          m = document.body[i+2].find('tabularvalignment="top"')
379          if m > -1:
380              tabularvalignment = 't'
381          m = document.body[i+2].find('tabularvalignment="bottom"')
382          if m > -1:
383              tabularvalignment = 'b'
384          # delete tabularvalignment
385          q = document.body[i+2].find("tabularvalignment")
386          if q > -1:
387              document.body[i+2] = document.body[i+2][:q-1]
388              document.body[i+2] = document.body[i+2] + '>'
389
390          # don't add a box when centered
391          if tabularvalignment == 'c':
392              i = j
393              continue
394          subst = ['\\end_layout', '\\end_inset']
395          document.body[j+1:j+1] = subst # just inserts those lines
396          subst = ['\\begin_inset Box Frameless',
397              'position "' + tabularvalignment +'"',
398              'hor_pos "c"',
399              'has_inner_box 1',
400              'inner_pos "c"',
401              'use_parbox 0',
402              # we don't know the width, assume 50%
403              'width "50col%"',
404              'special "none"',
405              'height "1in"',
406              'height_special "totalheight"',
407              'status open',
408              '',
409              '\\begin_layout Plain Layout']
410          document.body[i:i] = subst # this just inserts the array at i
411          i += len(subst) + 2 # adjust i to save a few cycles
412
413
414 def revert_phantom(document):
415     " Reverts phantom to ERT "
416     i = 0
417     j = 0
418     while True:
419       i = find_token(document.body, "\\begin_inset Phantom Phantom", i)
420       if i == -1:
421           return
422       substi = document.body[i].replace('\\begin_inset Phantom Phantom', \
423                 '\\begin_inset ERT\nstatus collapsed\n\n' \
424                 '\\begin_layout Plain Layout\n\n\n\\backslash\n' \
425                 'phantom{\n\\end_layout\n\n\\end_inset\n')
426       substi = substi.split('\n')
427       document.body[i : i+4] = substi
428       i += len(substi)
429       j = find_token(document.body, "\\end_layout", i)
430       if j == -1:
431           document.warning("Malformed LyX document: Could not find end of Phantom inset.")
432           return
433       substj = document.body[j].replace('\\end_layout', \
434                 '\\size default\n\n\\begin_inset ERT\nstatus collapsed\n\n' \
435                 '\\begin_layout Plain Layout\n\n' \
436                 '}\n\\end_layout\n\n\\end_inset\n')
437       substj = substj.split('\n')
438       document.body[j : j+4] = substj
439       i += len(substj)
440
441
442 def revert_hphantom(document):
443     " Reverts hphantom to ERT "
444     i = 0
445     j = 0
446     while True:
447       i = find_token(document.body, "\\begin_inset Phantom HPhantom", i)
448       if i == -1:
449           return
450       substi = document.body[i].replace('\\begin_inset Phantom HPhantom', \
451                 '\\begin_inset ERT\nstatus collapsed\n\n' \
452                 '\\begin_layout Plain Layout\n\n\n\\backslash\n' \
453                 'hphantom{\n\\end_layout\n\n\\end_inset\n')
454       substi = substi.split('\n')
455       document.body[i : i+4] = substi
456       i += len(substi)
457       j = find_token(document.body, "\\end_layout", i)
458       if j == -1:
459           document.warning("Malformed LyX document: Could not find end of HPhantom inset.")
460           return
461       substj = document.body[j].replace('\\end_layout', \
462                 '\\size default\n\n\\begin_inset ERT\nstatus collapsed\n\n' \
463                 '\\begin_layout Plain Layout\n\n' \
464                 '}\n\\end_layout\n\n\\end_inset\n')
465       substj = substj.split('\n')
466       document.body[j : j+4] = substj
467       i += len(substj)
468
469
470 def revert_vphantom(document):
471     " Reverts vphantom to ERT "
472     i = 0
473     j = 0
474     while True:
475       i = find_token(document.body, "\\begin_inset Phantom VPhantom", i)
476       if i == -1:
477           return
478       substi = document.body[i].replace('\\begin_inset Phantom VPhantom', \
479                 '\\begin_inset ERT\nstatus collapsed\n\n' \
480                 '\\begin_layout Plain Layout\n\n\n\\backslash\n' \
481                 'vphantom{\n\\end_layout\n\n\\end_inset\n')
482       substi = substi.split('\n')
483       document.body[i : i+4] = substi
484       i += len(substi)
485       j = find_token(document.body, "\\end_layout", i)
486       if j == -1:
487           document.warning("Malformed LyX document: Could not find end of VPhantom inset.")
488           return
489       substj = document.body[j].replace('\\end_layout', \
490                 '\\size default\n\n\\begin_inset ERT\nstatus collapsed\n\n' \
491                 '\\begin_layout Plain Layout\n\n' \
492                 '}\n\\end_layout\n\n\\end_inset\n')
493       substj = substj.split('\n')
494       document.body[j : j+4] = substj
495       i += len(substj)
496
497
498 def revert_xetex(document):
499     " Reverts documents that use XeTeX "
500     i = find_token(document.header, '\\use_xetex', 0)
501     if i == -1:
502         document.warning("Malformed LyX document: Missing \\use_xetex.")
503         return
504     if get_value(document.header, "\\use_xetex", i) == 'false':
505         del document.header[i]
506         return
507     del document.header[i]
508     # 1.) set doc encoding to utf8-plain
509     i = find_token(document.header, "\\inputencoding", 0)
510     if i == -1:
511         document.warning("Malformed LyX document: Missing \\inputencoding.")
512     document.header[i] = "\\inputencoding utf8-plain"
513     # 2.) check font settings
514     l = find_token(document.header, "\\font_roman", 0)
515     if l == -1:
516         document.warning("Malformed LyX document: Missing \\font_roman.")
517     line = document.header[l]
518     l = re.compile(r'\\font_roman (.*)$')
519     m = l.match(line)
520     roman = m.group(1)
521     l = find_token(document.header, "\\font_sans", 0)
522     if l == -1:
523         document.warning("Malformed LyX document: Missing \\font_sans.")
524     line = document.header[l]
525     l = re.compile(r'\\font_sans (.*)$')
526     m = l.match(line)
527     sans = m.group(1)
528     l = find_token(document.header, "\\font_typewriter", 0)
529     if l == -1:
530         document.warning("Malformed LyX document: Missing \\font_typewriter.")
531     line = document.header[l]
532     l = re.compile(r'\\font_typewriter (.*)$')
533     m = l.match(line)
534     typewriter = m.group(1)
535     osf = get_value(document.header, '\\font_osf', 0) == "true"
536     sf_scale = float(get_value(document.header, '\\font_sf_scale', 0))
537     tt_scale = float(get_value(document.header, '\\font_tt_scale', 0))
538     # 3.) set preamble stuff
539     pretext = '%% This document must be processed with xelatex!\n'
540     pretext += '\\usepackage{fontspec}\n'
541     if roman != "default":
542         pretext += '\\setmainfont[Mapping=tex-text]{' + roman + '}\n'
543     if sans != "default":
544         pretext += '\\setsansfont['
545         if sf_scale != 100:
546             pretext += 'Scale=' + str(sf_scale / 100) + ','
547         pretext += 'Mapping=tex-text]{' + sans + '}\n'
548     if typewriter != "default":
549         pretext += '\\setmonofont'
550         if tt_scale != 100:
551             pretext += '[Scale=' + str(tt_scale / 100) + ']'
552         pretext += '{' + typewriter + '}\n'
553     if osf:
554         pretext += '\\defaultfontfeatures{Numbers=OldStyle}\n'
555     pretext += '\usepackage{xunicode}\n'
556     pretext += '\usepackage{xltxtra}\n'
557     insert_to_preamble(0, document, pretext)
558     # 4.) reset font settings
559     i = find_token(document.header, "\\font_roman", 0)
560     if i == -1:
561         document.warning("Malformed LyX document: Missing \\font_roman.")
562     document.header[i] = "\\font_roman default"
563     i = find_token(document.header, "\\font_sans", 0)
564     if i == -1:
565         document.warning("Malformed LyX document: Missing \\font_sans.")
566     document.header[i] = "\\font_sans default"
567     i = find_token(document.header, "\\font_typewriter", 0)
568     if i == -1:
569         document.warning("Malformed LyX document: Missing \\font_typewriter.")
570     document.header[i] = "\\font_typewriter default"
571     i = find_token(document.header, "\\font_osf", 0)
572     if i == -1:
573         document.warning("Malformed LyX document: Missing \\font_osf.")
574     document.header[i] = "\\font_osf false"
575     i = find_token(document.header, "\\font_sc", 0)
576     if i == -1:
577         document.warning("Malformed LyX document: Missing \\font_sc.")
578     document.header[i] = "\\font_sc false"
579     i = find_token(document.header, "\\font_sf_scale", 0)
580     if i == -1:
581         document.warning("Malformed LyX document: Missing \\font_sf_scale.")
582     document.header[i] = "\\font_sf_scale 100"
583     i = find_token(document.header, "\\font_tt_scale", 0)
584     if i == -1:
585         document.warning("Malformed LyX document: Missing \\font_tt_scale.")
586     document.header[i] = "\\font_tt_scale 100"
587
588
589 def revert_outputformat(document):
590     " Remove default output format param "
591     i = find_token(document.header, '\\default_output_format', 0)
592     if i == -1:
593         document.warning("Malformed LyX document: Missing \\default_output_format.")
594         return
595     del document.header[i]
596
597
598 def revert_backgroundcolor(document):
599     " Reverts background color to preamble code "
600     i = 0
601     colorcode = ""
602     while True:
603       i = find_token(document.header, "\\backgroundcolor", i)
604       if i == -1:
605           return
606       colorcode = get_value(document.header, '\\backgroundcolor', 0)
607       del document.header[i]
608       # don't clutter the preamble if backgroundcolor is not set
609       if colorcode == "#ffffff":
610           continue
611       # the color code is in the form #rrggbb where every character denotes a hex number
612       # convert the string to an int
613       red = string.atoi(colorcode[1:3],16)
614       # we want the output "0.5" for the value "127" therefore add here
615       if red != 0:
616           red = red + 1
617       redout = float(red) / 256
618       green = string.atoi(colorcode[3:5],16)
619       if green != 0:
620           green = green + 1
621       greenout = float(green) / 256
622       blue = string.atoi(colorcode[5:7],16)
623       if blue != 0:
624           blue = blue + 1
625       blueout = float(blue) / 256
626       # write the preamble
627       insert_to_preamble(0, document,
628                            '% Commands inserted by lyx2lyx to set the background color\n'
629                            + '\\@ifundefined{definecolor}{\\usepackage{color}}{}\n'
630                            + '\\definecolor{page_backgroundcolor}{rgb}{'
631                            + str(redout) + ', ' + str(greenout)
632                            + ', ' + str(blueout) + '}\n'
633                            + '\\pagecolor{page_backgroundcolor}\n')
634
635
636 def revert_splitindex(document):
637     " Reverts splitindex-aware documents "
638     i = find_token(document.header, '\\use_indices', 0)
639     if i == -1:
640         document.warning("Malformed LyX document: Missing \\use_indices.")
641         return
642     indices = get_value(document.header, "\\use_indices", i)
643     preamble = ""
644     if indices == "true":
645          preamble += "\\usepackage{splitidx}\n"
646     del document.header[i]
647     i = 0
648     while True:
649         i = find_token(document.header, "\\index", i)
650         if i == -1:
651             break
652         k = find_token(document.header, "\\end_index", i)
653         if k == -1:
654             document.warning("Malformed LyX document: Missing \\end_index.")
655             return
656         line = document.header[i]
657         l = re.compile(r'\\index (.*)$')
658         m = l.match(line)
659         iname = m.group(1)
660         ishortcut = get_value(document.header, '\\shortcut', i, k)
661         if ishortcut != "" and indices == "true":
662             preamble += "\\newindex[" + iname + "]{" + ishortcut + "}\n"
663         del document.header[i:k+1]
664         i = 0
665     if preamble != "":
666         insert_to_preamble(0, document, preamble)
667     i = 0
668     while True:
669         i = find_token(document.body, "\\begin_inset Index", i)
670         if i == -1:
671             break
672         line = document.body[i]
673         l = re.compile(r'\\begin_inset Index (.*)$')
674         m = l.match(line)
675         itype = m.group(1)
676         if itype == "idx" or indices == "false":
677             document.body[i] = "\\begin_inset Index"
678         else:
679             k = find_end_of_inset(document.body, i)
680             if k == -1:
681                  return
682             content = lyx2latex(document, document.body[i:k])
683             # escape quotes
684             content = content.replace('"', r'\"')
685             subst = [put_cmd_in_ert("\\sindex[" + itype + "]{" + content + "}")]
686             document.body[i:k+1] = subst
687         i = i + 1
688     i = 0
689     while True:
690         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
691         if i == -1:
692             return
693         k = find_end_of_inset(document.body, i)
694         ptype = get_value(document.body, 'type', i, k).strip('"')
695         if ptype == "idx":
696             j = find_token(document.body, "type", i, k)
697             del document.body[j]
698         elif indices == "false":
699             del document.body[i:k+1]
700         else:
701             subst = [put_cmd_in_ert("\\printindex[" + ptype + "]{}")]
702             document.body[i:k+1] = subst
703         i = i + 1
704
705
706 def convert_splitindex(document):
707     " Converts index and printindex insets to splitindex-aware format "
708     i = 0
709     while True:
710         i = find_token(document.body, "\\begin_inset Index", i)
711         if i == -1:
712             break
713         document.body[i] = document.body[i].replace("\\begin_inset Index",
714             "\\begin_inset Index idx")
715         i = i + 1
716     i = 0
717     while True:
718         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
719         if i == -1:
720             return
721         if document.body[i + 1].find('LatexCommand printindex') == -1:
722             document.warning("Malformed LyX document: Incomplete printindex inset.")
723             return
724         subst = ["LatexCommand printindex", 
725             "type \"idx\""]
726         document.body[i + 1:i + 2] = subst
727         i = i + 1
728
729
730 def revert_subindex(document):
731     " Reverts \\printsubindex CommandInset types "
732     i = find_token(document.header, '\\use_indices', 0)
733     if i == -1:
734         document.warning("Malformed LyX document: Missing \\use_indices.")
735         return
736     indices = get_value(document.header, "\\use_indices", i)
737     i = 0
738     while True:
739         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
740         if i == -1:
741             return
742         k = find_end_of_inset(document.body, i)
743         ctype = get_value(document.body, 'LatexCommand', i, k)
744         if ctype != "printsubindex":
745             i = i + 1
746             continue
747         ptype = get_value(document.body, 'type', i, k).strip('"')
748         if indices == "false":
749             del document.body[i:k+1]
750         else:
751             subst = [put_cmd_in_ert("\\printsubindex[" + ptype + "]{}")]
752             document.body[i:k+1] = subst
753         i = i + 1
754
755
756 def revert_printindexall(document):
757     " Reverts \\print[sub]index* CommandInset types "
758     i = find_token(document.header, '\\use_indices', 0)
759     if i == -1:
760         document.warning("Malformed LyX document: Missing \\use_indices.")
761         return
762     indices = get_value(document.header, "\\use_indices", i)
763     i = 0
764     while True:
765         i = find_token(document.body, "\\begin_inset CommandInset index_print", i)
766         if i == -1:
767             return
768         k = find_end_of_inset(document.body, i)
769         ctype = get_value(document.body, 'LatexCommand', i, k)
770         if ctype != "printindex*" and ctype != "printsubindex*":
771             i = i + 1
772             continue
773         if indices == "false":
774             del document.body[i:k+1]
775         else:
776             subst = [put_cmd_in_ert("\\" + ctype + "{}")]
777             document.body[i:k+1] = subst
778         i = i + 1
779
780
781 def revert_strikeout(document):
782     " Reverts \\strikeout character style "
783     while True:
784         i = find_token(document.body, '\\strikeout', 0)
785         if i == -1:
786             return
787         del document.body[i]
788
789
790 def revert_uulinewave(document):
791     " Reverts \\uuline, and \\uwave character styles "
792     while True:
793         i = find_token(document.body, '\\uuline', 0)
794         if i == -1:
795             break
796         del document.body[i]
797     while True:
798         i = find_token(document.body, '\\uwave', 0)
799         if i == -1:
800             return
801         del document.body[i]
802
803
804 def revert_ulinelatex(document):
805     " Reverts \\uline character style "
806     i = find_token(document.body, '\\bar under', 0)
807     if i == -1:
808         return
809     insert_to_preamble(0, document,
810             '% Commands inserted by lyx2lyx for proper underlining\n'
811             + '\\PassOptionsToPackage{normalem}{ulem}\n'
812             + '\\usepackage{ulem}\n'
813             + '\\let\\cite@rig\\cite\n'
814             + '\\newcommand{\\b@xcite}[2][\\%]{\\def\\def@pt{\\%}\\def\\pas@pt{#1}\n'
815             + '  \\mbox{\\ifx\\def@pt\\pas@pt\\cite@rig{#2}\\else\\cite@rig[#1]{#2}\\fi}}\n'
816             + '\\renewcommand{\\underbar}[1]{{\\let\\cite\\b@xcite\\uline{#1}}}\n')
817
818
819 def revert_custom_processors(document):
820     " Remove bibtex_command and index_command params "
821     i = find_token(document.header, '\\bibtex_command', 0)
822     if i == -1:
823         document.warning("Malformed LyX document: Missing \\bibtex_command.")
824         return
825     del document.header[i]
826     i = find_token(document.header, '\\index_command', 0)
827     if i == -1:
828         document.warning("Malformed LyX document: Missing \\index_command.")
829         return
830     del document.header[i]
831
832
833 def convert_nomencl_width(document):
834     " Add set_width param to nomencl_print "
835     i = 0
836     while True:
837       i = find_token(document.body, "\\begin_inset CommandInset nomencl_print", i)
838       if i == -1:
839         break
840       document.body.insert(i + 2, "set_width \"none\"")
841       i = i + 1
842
843
844 def revert_nomencl_width(document):
845     " Remove set_width param from nomencl_print "
846     i = 0
847     while True:
848       i = find_token(document.body, "\\begin_inset CommandInset nomencl_print", i)
849       if i == -1:
850         break
851       j = find_end_of_inset(document.body, i)
852       l = find_token(document.body, "set_width", i, j)
853       if l == -1:
854             document.warning("Can't find set_width option for nomencl_print!")
855             i = j
856             continue
857       del document.body[l]
858       i = i + 1
859
860
861 def revert_nomencl_cwidth(document):
862     " Remove width param from nomencl_print "
863     i = 0
864     while True:
865       i = find_token(document.body, "\\begin_inset CommandInset nomencl_print", i)
866       if i == -1:
867         break
868       j = find_end_of_inset(document.body, i)
869       l = find_token(document.body, "width", i, j)
870       if l == -1:
871             document.warning("Can't find width option for nomencl_print!")
872             i = j
873             continue
874       width = get_value(document.body, "width", i, j).strip('"')
875       del document.body[l]
876       add_to_preamble(document, ["\\setlength{\\nomlabelwidth}{" + width + "}"])
877       i = i + 1
878
879
880 def revert_applemac(document):
881     " Revert applemac encoding to auto "
882     i = 0
883     if document.encoding == "applemac":
884         document.encoding = "auto"
885         i = find_token(document.header, "\\encoding", 0)
886         if i != -1:
887             document.header[i] = "\\encoding auto"
888
889
890 def revert_longtable_align(document):
891     " Remove longtable alignment setting "
892     i = 0
893     j = 0
894     while True:
895       i = find_token(document.body, "\\begin_inset Tabular", i)
896       if i == -1:
897           break
898       # the alignment is 2 lines below \\begin_inset Tabular
899       j = document.body[i+2].find("longtabularalignment")
900       if j == -1:
901           break
902       document.body[i+2] = document.body[i+2][:j-1]
903       document.body[i+2] = document.body[i+2] + '>'
904       i = i + 1
905
906
907 def revert_branch_filename(document):
908     " Remove \\filename_suffix parameter from branches "
909     i = 0
910     while True:
911         i = find_token(document.header, "\\filename_suffix", i)
912         if i == -1:
913             return
914         del document.header[i]
915
916
917 def revert_paragraph_indentation(document):
918     " Revert custom paragraph indentation to preamble code "
919     i = 0
920     while True:
921       i = find_token(document.header, "\\paragraph_indentation", i)
922       if i == -1:
923           break
924       # only remove the preamble line when default
925       # otherwise also write the value to the preamble  
926       j = document.header[i].find("default")
927       if j > -1:
928           del document.header[i]
929           break
930       else:
931           # search for the beginning of the value via the space
932           j = document.header[i].find(" ")
933           length = document.header[i][j+1:]
934           # handle percent lengths
935           length = latex_length(length)
936           # latex_length returns "bool,length"
937           k = length.find(",")
938           length = length[k+1:]
939           add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
940           add_to_preamble(document, ["\\setlength{\\parindent}{" + length + "}"])
941           del document.header[i]
942       i = i + 1
943
944
945 def revert_percent_skip_lengths(document):
946     " Revert relative lengths for paragraph skip separation to preamble code "
947     i = 0
948     while True:
949       i = find_token(document.header, "\\defskip", i)
950       if i == -1:
951           break
952       # only revert when a custom length was set and when
953       # it used a percent length
954       j = document.header[i].find("smallskip")
955       k = document.header[i].find("medskip")
956       l = document.header[i].find("bigskip")
957       if (j > -1) or (k > -1) or (l > -1):
958           break
959       else:
960           # search for the beginning of the value via the space
961           j = document.header[i].find(" ")
962           length = document.header[i][j+1:]
963           # handle percent lengths
964           length = latex_length(length)
965           # latex_length returns "bool,length"
966           l = length.find(",")
967           percent = length[:l]
968           length = length[l+1:]
969           if percent == "True":
970               add_to_preamble(document, ["% this command was inserted by lyx2lyx"])
971               add_to_preamble(document, ["\\setlength{\\parskip}{" + length + "}"])
972               # set defskip to medskip as default
973               document.header[i] = "\\defskip medskip"
974       i = i + 1
975
976
977 def revert_percent_vspace_lengths(document):
978     " Revert relative VSpace lengths to ERT "
979     i = 0
980     while True:
981       i = find_token(document.body, "\\begin_inset VSpace", i)
982       if i == -1:
983           break
984       # only revert when a custom length was set and when
985       # it used a percent length
986       j = document.body[i].find("defskip")
987       k = document.body[i].find("smallskip")
988       l = document.body[i].find("medskip")
989       m = document.body[i].find("bigskip")
990       n = document.body[i].find("vfill")
991       if (j > -1) or (k > -1) or (l > -1) or (m > -1) or (n > -1):
992           break
993       else:
994           # search for the beginning of the value via the last space
995           o = document.body[i].rfind(" ")
996           length = document.body[i][o+1:]
997           # check if the space has a star (protected space)
998           p = document.body[i].rfind("*")
999           if p > -1:
1000               length = length[:-1]
1001           # handle percent lengths
1002           length = latex_length(length)
1003           # latex_length returns "bool,length"
1004           q = length.find(",")
1005           percent = length[:q]
1006           length = length[q+1:]
1007           # revert the VSpace inset to ERT
1008           if percent == "True":
1009               if p > -1:
1010                   subst = [put_cmd_in_ert("\\vspace*{" + length + "}")]
1011               else:
1012                   subst = [put_cmd_in_ert("\\vspace{" + length + "}")]
1013               document.body[i:i+2] = subst
1014       i = i + 1
1015
1016
1017 def revert_percent_hspace_lengths(document):
1018     " Revert relative HSpace lengths to ERT "
1019     i = 0
1020     while True:
1021       i = find_token(document.body, "\\begin_inset space \\hspace", i)
1022       if i == -1:
1023           break
1024       star = (document.body[i].find("\\hspace*{}") != -1)
1025       # only revert when a custom length was set and when
1026       # it used a percent length
1027       length = get_value(document.body, '\\length', i+1)
1028       if length == '':
1029           document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
1030           return
1031       # handle percent lengths
1032       length = latex_length(length)
1033       # latex_length returns "bool,length"
1034       m = length.find(",")
1035       percent = length[:m]
1036       length = length[m+1:]
1037       # revert the HSpace inset to ERT
1038       if percent == "True":
1039           if star == True:
1040               subst = [put_cmd_in_ert("\\hspace*{" + length + "}")]
1041           else:
1042               subst = [put_cmd_in_ert("\\hspace{" + length + "}")]
1043           document.body[i:i+3] = subst
1044       i = i + 2
1045
1046
1047 def revert_hspace_glue_lengths(document):
1048     " Revert HSpace glue lengths to ERT "
1049     i = 0
1050     while True:
1051       i = find_token(document.body, "\\begin_inset space \\hspace", i)
1052       if i == -1:
1053           break
1054       star = (document.body[i].find("\\hspace*{}") != -1)
1055       length = get_value(document.body, '\\length', i+1)
1056       if length == '':
1057           document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
1058           return
1059       # only revert when the length contains a plus or minus
1060       l = length.find("+")
1061       if l == -1:
1062           l = length.find("-")
1063           if l == -1:
1064               break
1065       # handle percent lengths
1066       length = latex_length(length)
1067       # latex_length returns "bool,length"
1068       m = length.find(",")
1069       length = length[m+1:]
1070       # revert the HSpace inset to ERT
1071       # allow leading -
1072       n = length.find("-")
1073       if n != 0 or (n == 0 and (length.rfind("plus") > -1 or length.rfind("minus") > -1)):
1074           if star == True:
1075               subst = [put_cmd_in_ert("\\hspace*{" + length + "}")]
1076           else:
1077               subst = [put_cmd_in_ert("\\hspace{" + length + "}")]
1078           document.body[i:i+3] = subst
1079       i = i + 2
1080
1081
1082 ##
1083 # Conversion hub
1084 #
1085
1086 supported_versions = ["2.0.0","2.0"]
1087 convert = [[346, []],
1088            [347, []],
1089            [348, []],
1090            [349, []],
1091            [350, []],
1092            [351, []],
1093            [352, [convert_splitindex]],
1094            [353, []],
1095            [354, []],
1096            [355, []],
1097            [356, []],
1098            [357, []],
1099            [358, []],
1100            [359, [convert_nomencl_width]],
1101            [360, []],
1102            [361, []],
1103            [362, []],
1104            [363, []],
1105            [364, []],
1106            [365, []],
1107            [366, []],
1108            [367, []],
1109            [368, []]
1110           ]
1111
1112 revert =  [[367, [revert_hspace_glue_lengths]],
1113            [366, [revert_percent_vspace_lengths, revert_percent_hspace_lengths]],
1114            [365, [revert_percent_skip_lengths]],
1115            [364, [revert_paragraph_indentation]],
1116            [363, [revert_branch_filename]],
1117            [362, [revert_longtable_align]],
1118            [361, [revert_applemac]],
1119            [360, []],
1120            [359, [revert_nomencl_cwidth]],
1121            [358, [revert_nomencl_width]],
1122            [357, [revert_custom_processors]],
1123            [356, [revert_ulinelatex]],
1124            [355, [revert_uulinewave]],
1125            [354, [revert_strikeout]],
1126            [353, [revert_printindexall]],
1127            [352, [revert_subindex]],
1128            [351, [revert_splitindex]],
1129            [350, [revert_backgroundcolor]],
1130            [349, [revert_outputformat]],
1131            [348, [revert_xetex]],
1132            [347, [revert_phantom, revert_hphantom, revert_vphantom]],
1133            [346, [revert_tabularvalign]],
1134            [345, [revert_swiss]]
1135           ]
1136
1137
1138 if __name__ == "__main__":
1139     pass