]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_1_6.py
* lyx2lyx/lyx_1_6.py:
[lyx.git] / lib / lyx2lyx / lyx_1_6.py
1 # This file is part of lyx2lyx
2 # -*- coding: utf-8 -*-
3 # Copyright (C) 2007-2008 The LyX Team <lyx-devel@lists.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 1.6"""
20
21 import re
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 # WARNING!
35 # DO NOT do this:
36 #   document.body[i] = wrap_insert_ert(...)
37 # wrap_into_ert may returns a multiline string, which should NOT appear
38 # in document.body. Insetad, do something like this:
39 #   subst = wrap_inset_ert(...)
40 #   subst = subst.split('\n')
41 #   document.body[i:i+1] = subst
42 #   i+= len(subst) - 1
43 # where the last statement resets the counter to accord with the added
44 # lines.
45 def wrap_into_ert(string, src, dst):
46     '''Within string, replace occurrences of src with dst, wrapped into ERT
47        E.g.: wrap_into_ert('sch\"on', "\\", "\\backslash") is:
48        sch<ERT>\\backslash</ERT>"on'''
49     return string.replace(src, '\n\\begin_inset ERT\nstatus collapsed\n\\begin_layout Standard\n'
50       + dst + '\n\\end_layout\n\\end_inset\n')
51
52 def put_cmd_in_ert(string):
53     string = string.replace('\\', "\\backslash\n")
54     string = "\\begin_inset ERT\nstatus collapsed\n\\begin_layout Standard\n" \
55       + string + "\n\\end_layout\n\\end_inset"
56     return string
57
58 def add_to_preamble(document, text):
59     """ Add text to the preamble if it is not already there.
60     Only the first line is checked!"""
61
62     if find_token(document.preamble, text[0], 0) != -1:
63         return
64
65     document.preamble.extend(text)
66
67 def insert_to_preamble(index, document, text):
68     """ Insert text to the preamble at a given line"""
69
70     document.preamble.insert(index, text)
71
72 # Convert a LyX length into a LaTeX length
73 def convert_len(len):
74     units = {"text%":"\\backslash\ntextwidth", "col%":"\\backslash\ncolumnwidth",
75              "page%":"\\backslash\npagewidth", "line%":"\\backslash\nlinewidth",
76              "theight%":"\\backslash\ntextheight", "pheight%":"\\backslash\npageheight"}
77
78     # Convert LyX units to LaTeX units
79     for unit in units.keys():
80         if len.find(unit) != -1:
81             len = '%f' % (len2value(len) / 100)
82             len = len.strip('0') + units[unit]
83             break
84
85     return len
86
87 # Return the value of len without the unit in numerical form.
88 def len2value(len):
89     result = re.search('([+-]?[0-9.]+)', len)
90     if result:
91         return float(result.group(1))
92     # No number means 1.0
93     return 1.0
94
95 # Unfortunately, this doesn't really work, since Standard isn't always default.
96 # But it's as good as we can do right now.
97 def find_default_layout(document, start, end):
98     l = find_token(document.body, "\\begin_layout Standard", start, end)
99     if l == -1:
100         l = find_token(document.body, "\\begin_layout PlainLayout", start, end)
101     if l == -1:
102         l = find_token(document.body, "\\begin_layout Plain Layout", start, end)
103     return l
104
105 def get_option(document, m, option, default):
106     l = document.body[m].find(option)
107     val = default
108     if l != -1:
109         val = document.body[m][l:].split('"')[1]
110     return val
111
112 def remove_option(document, m, option):
113     l = document.body[m].find(option)
114     if l != -1:
115         val = document.body[m][l:].split('"')[1]
116         document.body[m] = document.body[m][:l-1] + document.body[m][l+len(option + '="' + val + '"'):]
117     return l
118
119 def set_option(document, m, option, value):
120     l = document.body[m].find(option)
121     if l != -1:
122         oldval = document.body[m][l:].split('"')[1]
123         l = l + len(option + '="')
124         document.body[m] = document.body[m][:l] + value + document.body[m][l+len(oldval):]
125     else:
126         document.body[m] = document.body[m][:-1] + ' ' + option + '="' + value + '">'
127     return l
128
129
130 def read_unicodesymbols():
131     " Read the unicodesymbols list of unicode characters and corresponding commands."
132     pathname = os.path.abspath(os.path.dirname(sys.argv[0]))
133     fp = open(os.path.join(pathname.strip('lyx2lyx'), 'unicodesymbols'))
134     spec_chars = []
135     # Two backslashes, followed by some non-word character, and then a character
136     # in brackets. The idea is to check for constructs like: \"{u}, which is how
137     # they are written in the unicodesymbols file; but they can also be written
138     # as: \"u.
139     r = re.compile(r'\\\\(\W)\{(\w)\}')
140     for line in fp.readlines():
141         if line[0] != '#' and line.strip() != "":
142             line=line.replace(' "',' ') # remove all quotation marks with spaces before
143             line=line.replace('" ',' ') # remove all quotation marks with spaces after
144             line=line.replace(r'\"','"') # replace \" by " (for characters with diaeresis)
145             try:
146                 [ucs4,command,dead] = line.split(None,2)
147                 if command[0:1] != "\\":
148                     continue
149                 spec_chars.append([command, unichr(eval(ucs4))])
150             except:
151                 continue
152             m = r.match(command)
153             if m != None:
154                 command = "\\\\"
155                 # If the character is a double-quote, then we need to escape it, too,
156                 # since it is done that way in the LyX file.
157                 if m.group(1) == "\"":
158                     command += "\\"
159                 command += m.group(1) + m.group(2)
160                 spec_chars.append([command, unichr(eval(ucs4))])
161     fp.close()
162     return spec_chars
163
164
165 def extract_argument(line):
166     'Extracts a LaTeX argument from the start of line. Returns (arg, rest).'
167
168     if not line:
169         return (None, "")
170
171     bracere = re.compile("(\s*)(.*)")
172     n = bracere.match(line)
173     whitespace = n.group(1)
174     stuff = n.group(2)
175     brace = stuff[:1]
176     if brace != "[" and brace != "{":
177         return (None, line)
178
179     # find closing brace
180     remain = stuff[1:]
181     pos = 0
182     num = 1
183     term = "}"
184     if brace == "[":
185         term = "]"
186     skip = False
187     for c in remain:
188         if skip:
189             skip = False
190         elif c == "\\":
191             skip = True
192         elif c == brace:
193             num += 1
194         elif c == term:
195             num -= 1
196         if c == 0:
197             break
198         pos += 1
199     if num != 0:
200         # We never found the matching brace
201         # So, to be on the safe side, let's just return everything
202         # which will then get wrapped as ERT
203         return (line, "")
204     return (line[:pos + 1], line[pos + 1:])
205
206
207 def latex2ert(line):
208     '''Converts LaTeX commands into ERT. line may well be a multi-line
209        string when it is returned.'''
210     if not line:
211         return line
212
213     retval = ""
214     ## FIXME Escaped \ ??
215     # This regex looks for a LaTeX command---i.e., something of the form
216     # "\alPhaStuFF", or "\X", where X is any character---where the command
217     # may also be preceded by an additional backslash, which is how it would
218     # appear (e.g.) in an InsetIndex.
219     labelre = re.compile(r'(.*?)\\?(\\(?:[a-zA-Z]+|.))(.*)')
220
221     m = labelre.match(line)
222     while m != None:
223         retval += m.group(1)
224         cmd = m.group(2)
225         end = m.group(3)
226
227         while True:
228             (arg, rest) = extract_argument(end)
229             if arg == None:
230                 break
231             cmd += arg
232             end = rest
233         # If we wanted to put labels into an InsetLabel, for example, then we
234         # would just need to test here for cmd == "label" and then take some
235         # appropriate action, i.e., to use arg to get the content and then
236         # wrap it appropriately.
237         cmd = put_cmd_in_ert(cmd)
238         retval += "\n" + cmd + "\n"
239         line = end
240         m = labelre.match(line)
241     retval += line
242     return retval
243
244
245 #Bug 5022....
246 #Might should do latex2ert first, then deal with stuff that DOESN'T
247 #end up inside ERT. That routine could be modified so that it returned
248 #a list of lines, and we could then skip ERT bits and only deal with
249 #the other bits.
250 def latex2lyx(data):
251     '''Takes a string, possibly multi-line, and returns the result of
252     converting LaTeX constructs into LyX constructs. Returns a list of
253     lines, suitable for insertion into document.body.'''
254
255     retval = []
256
257     # Convert LaTeX to Unicode
258     reps = read_unicodesymbols()
259     # Commands of this sort need to be checked to make sure they are
260     # followed by a non-alpha character, lest we replace too much.
261     hardone = re.compile(r'^\\\\[a-zA-Z]+$')
262
263     for rep in reps:
264         if hardone.match(rep[0]):
265             pos = 0
266             while True:
267                 pos = data.find(rep[0], pos)
268                 if pos == -1:
269                     break
270                 nextpos = pos + len(rep[0])
271                 if nextpos < len(data) and data[nextpos].isalpha():
272                     # not the end of that command
273                     pos = nextpos
274                     continue
275                 data = data[:pos] + rep[1] + data[nextpos:]
276                 pos = nextpos
277         else:
278             data = data.replace(rep[0], rep[1])
279
280     # Generic, \" -> ":
281     data = wrap_into_ert(data, r'\"', '"')
282
283     # Math:
284     mathre = re.compile('^(.*?)(\$.*?\$)(.*)')
285     lines = data.split('\n')
286     for line in lines:
287         #document.warning("LINE: " + line)
288         #document.warning(str(i) + ":" + document.body[i])
289         #document.warning("LAST: " + document.body[-1])
290         g = line
291         m = mathre.match(g)
292         while m != None:
293             s = m.group(1)
294             f = m.group(2).replace('\\\\', '\\')
295             g = m.group(3)
296             if s:
297                 # this is non-math!
298                 s = latex2ert(s)
299                 subst = s.split('\n')
300                 retval += subst
301             retval.append("\\begin_inset Formula " + f)
302             retval.append("\\end_inset")
303             m = mathre.match(g)
304         # Handle whatever is left, which is just text
305         g = latex2ert(g)
306         subst = g.split('\n')
307         retval += subst
308     return retval
309
310
311 def lyx2latex(document, lines):
312     'Convert some LyX stuff into corresponding LaTeX stuff, as best we can.'
313     # clean up multiline stuff
314     content = ""
315     ert_end = 0
316     reps = read_unicodesymbols()
317
318     for curline in range(len(lines)):
319       line = lines[curline]
320       if line.startswith("\\begin_inset ERT"):
321           # We don't want to replace things inside ERT, so figure out
322           # where the end of the inset is.
323           ert_end = find_end_of_inset(lines, curline + 1)
324           continue
325       elif line.startswith("\\begin_inset Formula"):
326           line = line[20:]
327       elif line.startswith("\\begin_inset Quotes"):
328           # For now, we do a very basic reversion. Someone who understands
329           # quotes is welcome to fix it up.
330           qtype = line[20:].strip()
331           # lang = qtype[0]
332           side = qtype[1]
333           dbls = qtype[2]
334           if side == "l":
335               if dbls == "d":
336                   line = "``"
337               else:
338                   line = "`"
339           else:
340               if dbls == "d":
341                   line = "''"
342               else:
343                   line = "'"
344       elif line.isspace() or \
345             line.startswith("\\begin_layout") or \
346             line.startswith("\\end_layout") or \
347             line.startswith("\\begin_inset") or \
348             line.startswith("\\end_inset") or \
349             line.startswith("\\lang") or \
350             line.strip() == "status collapsed" or \
351             line.strip() == "status open":
352           #skip all that stuff
353           continue
354
355       # this needs to be added to the preamble because of cases like
356       # \textmu, \textbackslash, etc.
357       add_to_preamble(document, ['% added by lyx2lyx for converted index entries',
358                                  '\\@ifundefined{textmu}',
359                                  ' {\\usepackage{textcomp}}{}'])
360       # a lossless reversion is not possible
361       # try at least to handle some common insets and settings
362       if ert_end >= curline:
363           line = line.replace(r'\backslash', r'\\')
364       else:
365           line = line.replace('&', '\\&{}')
366           line = line.replace('#', '\\#{}')
367           line = line.replace('^', '\\^{}')
368           line = line.replace('%', '\\%{}')
369           line = line.replace('_', '\\_{}')
370           line = line.replace('$', '\\${}')
371
372           # Do the LyX text --> LaTeX conversion
373           for rep in reps:
374             line = line.replace(rep[1], rep[0] + "{}")
375           line = line.replace(r'\backslash', r'\textbackslash{}')
376           line = line.replace(r'\series bold', r'\bfseries{}').replace(r'\series default', r'\mdseries{}')
377           line = line.replace(r'\shape italic', r'\itshape{}').replace(r'\shape smallcaps', r'\scshape{}')
378           line = line.replace(r'\shape slanted', r'\slshape{}').replace(r'\shape default', r'\upshape{}')
379           line = line.replace(r'\emph on', r'\em{}').replace(r'\emph default', r'\em{}')
380           line = line.replace(r'\noun on', r'\scshape{}').replace(r'\noun default', r'\upshape{}')
381           line = line.replace(r'\bar under', r'\underbar{').replace(r'\bar default', r'}')
382           line = line.replace(r'\family sans', r'\sffamily{}').replace(r'\family default', r'\normalfont{}')
383           line = line.replace(r'\family typewriter', r'\ttfamily{}').replace(r'\family roman', r'\rmfamily{}')
384           line = line.replace(r'\InsetSpace ', r'').replace(r'\SpecialChar ', r'')
385       content += line
386     return content
387
388
389 ####################################################################
390
391 def convert_ltcaption(document):
392     i = 0
393     while True:
394         i = find_token(document.body, "\\begin_inset Tabular", i)
395         if i == -1:
396             return
397         j = find_end_of_inset(document.body, i + 1)
398         if j == -1:
399             document.warning("Malformed LyX document: Could not find end of tabular.")
400             continue
401
402         nrows = int(document.body[i+1].split('"')[3])
403         ncols = int(document.body[i+1].split('"')[5])
404
405         m = i + 1
406         for k in range(nrows):
407             m = find_token(document.body, "<row", m)
408             r = m
409             caption = 'false'
410             for k in range(ncols):
411                 m = find_token(document.body, "<cell", m)
412                 if (k == 0):
413                     mend = find_token(document.body, "</cell>", m + 1)
414                     # first look for caption insets
415                     mcap = find_token(document.body, "\\begin_inset Caption", m + 1, mend)
416                     # then look for ERT captions
417                     if mcap == -1:
418                         mcap = find_token(document.body, "caption", m + 1, mend)
419                         if mcap > -1:
420                             mcap = find_token(document.body, "\\backslash", mcap - 1, mcap)
421                     if mcap > -1:
422                         caption = 'true'
423                 if caption == 'true':
424                     if (k == 0):
425                         set_option(document, r, 'caption', 'true')
426                         set_option(document, m, 'multicolumn', '1')
427                         set_option(document, m, 'bottomline', 'false')
428                         set_option(document, m, 'topline', 'false')
429                         set_option(document, m, 'rightline', 'false')
430                         set_option(document, m, 'leftline', 'false')
431                         #j = find_end_of_inset(document.body, j + 1)
432                     else:
433                         set_option(document, m, 'multicolumn', '2')
434                 m = m + 1
435             m = m + 1
436
437         i = j + 1
438
439
440 #FIXME Use of wrap_into_ert can confuse lyx2lyx
441 def revert_ltcaption(document):
442     i = 0
443     while True:
444         i = find_token(document.body, "\\begin_inset Tabular", i)
445         if i == -1:
446             return
447         j = find_end_of_inset(document.body, i + 1)
448         if j == -1:
449             document.warning("Malformed LyX document: Could not find end of tabular.")
450             continue
451
452         m = i + 1
453         nrows = int(document.body[i+1].split('"')[3])
454         ncols = int(document.body[i+1].split('"')[5])
455
456         for k in range(nrows):
457             m = find_token(document.body, "<row", m)
458             caption = get_option(document, m, 'caption', 'false')
459             if caption == 'true':
460                 remove_option(document, m, 'caption')
461                 for k in range(ncols):
462                     m = find_token(document.body, "<cell", m)
463                     remove_option(document, m, 'multicolumn')
464                     if k == 0:
465                         m = find_token(document.body, "\\begin_inset Caption", m)
466                         if m == -1:
467                             return
468                         m = find_end_of_inset(document.body, m + 1)
469                         document.body[m] += wrap_into_ert("","","\\backslash\n\\backslash\n%")
470                     m = m + 1
471             m = m + 1
472         i = j + 1
473
474
475 def convert_tablines(document):
476     i = 0
477     while True:
478         i = find_token(document.body, "\\begin_inset Tabular", i)
479         if i == -1:
480             # LyX 1.3 inserted an extra space between \begin_inset
481             # and Tabular so let us try if this is the case and fix it.
482             i = find_token(document.body, "\\begin_inset  Tabular", i)
483             if i == -1:
484                 return
485             else:
486                 document.body[i] = "\\begin_inset Tabular"
487         j = find_end_of_inset(document.body, i + 1)
488         if j == -1:
489             document.warning("Malformed LyX document: Could not find end of tabular.")
490             continue
491
492         m = i + 1
493         nrows = int(document.body[i+1].split('"')[3])
494         ncols = int(document.body[i+1].split('"')[5])
495
496         col_info = []
497         for k in range(ncols):
498             m = find_token(document.body, "<column", m)
499             left = get_option(document, m, 'leftline', 'false')
500             right = get_option(document, m, 'rightline', 'false')
501             col_info.append([left, right])
502             remove_option(document, m, 'leftline')
503             remove_option(document, m, 'rightline')
504             m = m + 1
505
506         row_info = []
507         for k in range(nrows):
508             m = find_token(document.body, "<row", m)
509             top = get_option(document, m, 'topline', 'false')
510             bottom = get_option(document, m, 'bottomline', 'false')
511             row_info.append([top, bottom])
512             remove_option(document, m, 'topline')
513             remove_option(document, m, 'bottomline')
514             m = m + 1
515
516         m = i + 1
517         mc_info = []
518         for k in range(nrows*ncols):
519             m = find_token(document.body, "<cell", m)
520             mc_info.append(get_option(document, m, 'multicolumn', '0'))
521             m = m + 1
522         m = i + 1
523         for l in range(nrows):
524             for k in range(ncols):
525                 m = find_token(document.body, '<cell', m)
526                 if mc_info[l*ncols + k] == '0':
527                     r = set_option(document, m, 'topline', row_info[l][0])
528                     r = set_option(document, m, 'bottomline', row_info[l][1])
529                     r = set_option(document, m, 'leftline', col_info[k][0])
530                     r = set_option(document, m, 'rightline', col_info[k][1])
531                 elif mc_info[l*ncols + k] == '1':
532                     s = k + 1
533                     while s < ncols and mc_info[l*ncols + s] == '2':
534                         s = s + 1
535                     if s < ncols and mc_info[l*ncols + s] != '1':
536                         r = set_option(document, m, 'rightline', col_info[k][1])
537                     if k > 0 and mc_info[l*ncols + k - 1] == '0':
538                         r = set_option(document, m, 'leftline', col_info[k][0])
539                 m = m + 1
540         i = j + 1
541
542
543 def revert_tablines(document):
544     i = 0
545     while True:
546         i = find_token(document.body, "\\begin_inset Tabular", i)
547         if i == -1:
548             return
549         j = find_end_of_inset(document.body, i + 1)
550         if j == -1:
551             document.warning("Malformed LyX document: Could not find end of tabular.")
552             continue
553
554         m = i + 1
555         nrows = int(document.body[i+1].split('"')[3])
556         ncols = int(document.body[i+1].split('"')[5])
557
558         lines = []
559         for k in range(nrows*ncols):
560             m = find_token(document.body, "<cell", m)
561             top = get_option(document, m, 'topline', 'false')
562             bottom = get_option(document, m, 'bottomline', 'false')
563             left = get_option(document, m, 'leftline', 'false')
564             right = get_option(document, m, 'rightline', 'false')
565             lines.append([top, bottom, left, right])
566             m = m + 1
567
568         # we will want to ignore longtable captions
569         m = i + 1
570         caption_info = []
571         for k in range(nrows):
572             m = find_token(document.body, "<row", m)
573             caption = get_option(document, m, 'caption', 'false')
574             caption_info.append([caption])
575             m = m + 1
576
577         m = i + 1
578         col_info = []
579         for k in range(ncols):
580             m = find_token(document.body, "<column", m)
581             left = 'true'
582             for l in range(nrows):
583                 left = lines[l*ncols + k][2]
584                 if left == 'false' and caption_info[l] == 'false':
585                     break
586             set_option(document, m, 'leftline', left)
587             right = 'true'
588             for l in range(nrows):
589                 right = lines[l*ncols + k][3]
590                 if right == 'false' and caption_info[l] == 'false':
591                     break
592             set_option(document, m, 'rightline', right)
593             m = m + 1
594
595         row_info = []
596         for k in range(nrows):
597             m = find_token(document.body, "<row", m)
598             top = 'true'
599             for l in range(ncols):
600                 top = lines[k*ncols + l][0]
601                 if top == 'false':
602                     break
603             if caption_info[k] == 'false':
604                 top = 'false'
605             set_option(document, m, 'topline', top)
606             bottom = 'true'
607             for l in range(ncols):
608                 bottom = lines[k*ncols + l][1]
609                 if bottom == 'false':
610                     break
611             if caption_info[k] == 'false':
612                 bottom = 'false'
613             set_option(document, m, 'bottomline', bottom)
614             m = m + 1
615
616         i = j + 1
617
618
619 def fix_wrong_tables(document):
620     i = 0
621     while True:
622         i = find_token(document.body, "\\begin_inset Tabular", i)
623         if i == -1:
624             return
625         j = find_end_of_inset(document.body, i + 1)
626         if j == -1:
627             document.warning("Malformed LyX document: Could not find end of tabular.")
628             continue
629
630         m = i + 1
631         nrows = int(document.body[i+1].split('"')[3])
632         ncols = int(document.body[i+1].split('"')[5])
633
634         for l in range(nrows):
635             prev_multicolumn = 0
636             for k in range(ncols):
637                 m = find_token(document.body, '<cell', m)
638
639                 if document.body[m].find('multicolumn') != -1:
640                     multicol_cont = int(document.body[m].split('"')[1])
641
642                     if multicol_cont == 2 and (k == 0 or prev_multicolumn == 0):
643                         document.body[m] = document.body[m][:5] + document.body[m][21:]
644                         prev_multicolumn = 0
645                     else:
646                         prev_multicolumn = multicol_cont
647                 else:
648                     prev_multicolumn = 0
649
650         i = j + 1
651
652
653 def close_begin_deeper(document):
654     i = 0
655     depth = 0
656     while True:
657         i = find_tokens(document.body, ["\\begin_deeper", "\\end_deeper"], i)
658
659         if i == -1:
660             break
661
662         if document.body[i][:13] == "\\begin_deeper":
663             depth += 1
664         else:
665             depth -= 1
666
667         i += 1
668
669     document.body[-2:-2] = ['\\end_deeper' for i in range(depth)]
670
671
672 def long_charstyle_names(document):
673     i = 0
674     while True:
675         i = find_token(document.body, "\\begin_inset CharStyle", i)
676         if i == -1:
677             return
678         document.body[i] = document.body[i].replace("CharStyle ", "CharStyle CharStyle:")
679         i += 1
680
681 def revert_long_charstyle_names(document):
682     i = 0
683     while True:
684         i = find_token(document.body, "\\begin_inset CharStyle", i)
685         if i == -1:
686             return
687         document.body[i] = document.body[i].replace("CharStyle CharStyle:", "CharStyle ")
688         i += 1
689
690
691 def axe_show_label(document):
692     i = 0
693     while True:
694         i = find_token(document.body, "\\begin_inset CharStyle", i)
695         if i == -1:
696             return
697         if document.body[i + 1].find("show_label") != -1:
698             if document.body[i + 1].find("true") != -1:
699                 document.body[i + 1] = "status open"
700                 del document.body[ i + 2]
701             else:
702                 if document.body[i + 1].find("false") != -1:
703                     document.body[i + 1] = "status collapsed"
704                     del document.body[ i + 2]
705                 else:
706                     document.warning("Malformed LyX document: show_label neither false nor true.")
707         else:
708             document.warning("Malformed LyX document: show_label missing in CharStyle.")
709
710         i += 1
711
712
713 def revert_show_label(document):
714     i = 0
715     while True:
716         i = find_token(document.body, "\\begin_inset CharStyle", i)
717         if i == -1:
718             return
719         if document.body[i + 1].find("status open") != -1:
720             document.body.insert(i + 1, "show_label true")
721         else:
722             if document.body[i + 1].find("status collapsed") != -1:
723                 document.body.insert(i + 1, "show_label false")
724             else:
725                 document.warning("Malformed LyX document: no legal status line in CharStyle.")
726         i += 1
727
728 def revert_begin_modules(document):
729     i = 0
730     while True:
731         i = find_token(document.header, "\\begin_modules", i)
732         if i == -1:
733             return
734         j = find_end_of(document.header, i, "\\begin_modules", "\\end_modules")
735         if j == -1:
736             # this should not happen
737             break
738         document.header[i : j + 1] = []
739
740 def convert_flex(document):
741     "Convert CharStyle to Flex"
742     i = 0
743     while True:
744         i = find_token(document.body, "\\begin_inset CharStyle", i)
745         if i == -1:
746             return
747         document.body[i] = document.body[i].replace('\\begin_inset CharStyle', '\\begin_inset Flex')
748
749 def revert_flex(document):
750     "Convert Flex to CharStyle"
751     i = 0
752     while True:
753         i = find_token(document.body, "\\begin_inset Flex", i)
754         if i == -1:
755             return
756         document.body[i] = document.body[i].replace('\\begin_inset Flex', '\\begin_inset CharStyle')
757
758
759 #  Discard PDF options for hyperref
760 def revert_pdf_options(document):
761         "Revert PDF options for hyperref."
762         # store the PDF options and delete the entries from the Lyx file
763         i = 0
764         hyperref = False
765         title = ""
766         author = ""
767         subject = ""
768         keywords = ""
769         bookmarks = ""
770         bookmarksnumbered = ""
771         bookmarksopen = ""
772         bookmarksopenlevel = ""
773         breaklinks = ""
774         pdfborder = ""
775         colorlinks = ""
776         backref = ""
777         pagebackref = ""
778         pagemode = ""
779         otheroptions = ""
780         i = find_token(document.header, "\\use_hyperref", i)
781         if i != -1:
782             hyperref = get_value(document.header, "\\use_hyperref", i) == 'true'
783             del document.header[i]
784         i = find_token(document.header, "\\pdf_store_options", i)
785         if i != -1:
786             del document.header[i]
787         i = find_token(document.header, "\\pdf_title", 0)
788         if i != -1:
789             title = get_value_string(document.header, '\\pdf_title', 0, 0, True)
790             title = ' pdftitle={' + title + '}'
791             del document.header[i]
792         i = find_token(document.header, "\\pdf_author", 0)
793         if i != -1:
794             author = get_value_string(document.header, '\\pdf_author', 0, 0, True)
795             if title == "":
796                 author = ' pdfauthor={' + author + '}'
797             else:
798                 author = ',\n pdfauthor={' + author + '}'
799             del document.header[i]
800         i = find_token(document.header, "\\pdf_subject", 0)
801         if i != -1:
802             subject = get_value_string(document.header, '\\pdf_subject', 0, 0, True)
803             if title == "" and author == "":
804                 subject = ' pdfsubject={' + subject + '}'
805             else:
806                 subject = ',\n pdfsubject={' + subject + '}'
807             del document.header[i]
808         i = find_token(document.header, "\\pdf_keywords", 0)
809         if i != -1:
810             keywords = get_value_string(document.header, '\\pdf_keywords', 0, 0, True)
811             if title == "" and author == "" and subject == "":
812                 keywords = ' pdfkeywords={' + keywords + '}'
813             else:
814                 keywords = ',\n pdfkeywords={' + keywords + '}'
815             del document.header[i]
816         i = find_token(document.header, "\\pdf_bookmarks", 0)
817         if i != -1:
818             bookmarks = get_value_string(document.header, '\\pdf_bookmarks', 0)
819             bookmarks = ',\n bookmarks=' + bookmarks
820             del document.header[i]
821         i = find_token(document.header, "\\pdf_bookmarksnumbered", i)
822         if i != -1:
823             bookmarksnumbered = get_value_string(document.header, '\\pdf_bookmarksnumbered', 0)
824             bookmarksnumbered = ',\n bookmarksnumbered=' + bookmarksnumbered
825             del document.header[i]
826         i = find_token(document.header, "\\pdf_bookmarksopen", i)
827         if i != -1:
828             bookmarksopen = get_value_string(document.header, '\\pdf_bookmarksopen', 0)
829             bookmarksopen = ',\n bookmarksopen=' + bookmarksopen
830             del document.header[i]
831         i = find_token(document.header, "\\pdf_bookmarksopenlevel", i)
832         if i != -1:
833             bookmarksopenlevel = get_value_string(document.header, '\\pdf_bookmarksopenlevel', 0, 0, True)
834             bookmarksopenlevel = ',\n bookmarksopenlevel=' + bookmarksopenlevel
835             del document.header[i]
836         i = find_token(document.header, "\\pdf_breaklinks", i)
837         if i != -1:
838             breaklinks = get_value_string(document.header, '\\pdf_breaklinks', 0)
839             breaklinks = ',\n breaklinks=' + breaklinks
840             del document.header[i]
841         i = find_token(document.header, "\\pdf_pdfborder", i)
842         if i != -1:
843             pdfborder = get_value_string(document.header, '\\pdf_pdfborder', 0)
844             if pdfborder == 'true':
845                 pdfborder = ',\n pdfborder={0 0 0}'
846             else:
847                 pdfborder = ',\n pdfborder={0 0 1}'
848             del document.header[i]
849         i = find_token(document.header, "\\pdf_colorlinks", i)
850         if i != -1:
851             colorlinks = get_value_string(document.header, '\\pdf_colorlinks', 0)
852             colorlinks = ',\n colorlinks=' + colorlinks
853             del document.header[i]
854         i = find_token(document.header, "\\pdf_backref", i)
855         if i != -1:
856             backref = get_value_string(document.header, '\\pdf_backref', 0)
857             backref = ',\n backref=' + backref
858             del document.header[i]
859         i = find_token(document.header, "\\pdf_pagebackref", i)
860         if i != -1:
861             pagebackref = get_value_string(document.header, '\\pdf_pagebackref', 0)
862             pagebackref = ',\n pagebackref=' + pagebackref
863             del document.header[i]
864         i = find_token(document.header, "\\pdf_pagemode", 0)
865         if i != -1:
866             pagemode = get_value_string(document.header, '\\pdf_pagemode', 0)
867             pagemode = ',\n pdfpagemode=' + pagemode
868             del document.header[i]
869         i = find_token(document.header, "\\pdf_quoted_options", 0)
870         if i != -1:
871             otheroptions = get_value_string(document.header, '\\pdf_quoted_options', 0, 0, True)
872             if title == "" and author == "" and subject == "" and keywords == "":
873                 otheroptions = ' ' + otheroptions
874             else:
875                 otheroptions = ',\n ' + otheroptions
876             del document.header[i]
877
878         # write to the preamble when hyperref was used
879         if hyperref == True:
880             # preamble write preparations
881             # bookmark numbers are only output when they are turned on
882             if bookmarksopen == ',\n bookmarksopen=true':
883                 bookmarksopen = bookmarksopen + bookmarksopenlevel
884             if bookmarks == ',\n bookmarks=true':
885                 bookmarks = bookmarks + bookmarksnumbered + bookmarksopen
886             else:
887                 bookmarks = bookmarks
888             # hypersetup is only output when there are things to be set up
889             setupstart = '\\hypersetup{%\n'
890             setupend = ' }\n'
891             if otheroptions == "" and title == "" and  author == ""\
892                and  subject == "" and keywords == "":
893                 setupstart = ""
894                 setupend = ""
895             # write the preamble
896             # babel must be loaded before hyperref and hyperref the first part
897             # of the preamble, like in LyX 1.6
898             insert_to_preamble(0, document,
899                                  '% Commands inserted by lyx2lyx for PDF properties\n'
900                                  + '\\usepackage{babel}\n'
901                                  + '\\usepackage[unicode=true'
902                                  + bookmarks
903                                  + breaklinks
904                                  + pdfborder
905                                  + backref
906                                  + pagebackref
907                                  + colorlinks
908                                  + pagemode
909                                  + ']\n'
910                                  + ' {hyperref}\n'
911                                  + setupstart
912                                  + title
913                                  + author
914                                  + subject
915                                  + keywords
916                                  + otheroptions
917                                  + setupend)
918
919
920 def remove_inzip_options(document):
921     "Remove inzipName and embed options from the Graphics inset"
922     i = 0
923     while 1:
924         i = find_token(document.body, "\\begin_inset Graphics", i)
925         if i == -1:
926             return
927         j = find_end_of_inset(document.body, i + 1)
928         if j == -1:
929             # should not happen
930             document.warning("Malformed LyX document: Could not find end of graphics inset.")
931         # If there's a inzip param, just remove that
932         k = find_token(document.body, "\tinzipName", i + 1, j)
933         if k != -1:
934             del document.body[k]
935             # embed option must follow the inzipName option
936             del document.body[k+1]
937         i = i + 1
938
939
940 def convert_inset_command(document):
941     """
942         Convert:
943             \begin_inset LatexCommand cmd
944         to
945             \begin_inset CommandInset InsetType
946             LatexCommand cmd
947     """
948     i = 0
949     while 1:
950         i = find_token(document.body, "\\begin_inset LatexCommand", i)
951         if i == -1:
952             return
953         line = document.body[i]
954         r = re.compile(r'\\begin_inset LatexCommand (.*)$')
955         m = r.match(line)
956         cmdName = m.group(1)
957         insetName = ""
958         #this is adapted from factory.cpp
959         if cmdName[0:4].lower() == "cite":
960             insetName = "citation"
961         elif cmdName == "url" or cmdName == "htmlurl":
962             insetName = "url"
963         elif cmdName[-3:] == "ref":
964             insetName = "ref"
965         elif cmdName == "tableofcontents":
966             insetName = "toc"
967         elif cmdName == "printnomenclature":
968             insetName = "nomencl_print"
969         elif cmdName == "printindex":
970             insetName = "index_print"
971         else:
972             insetName = cmdName
973         insertion = ["\\begin_inset CommandInset " + insetName, "LatexCommand " + cmdName]
974         document.body[i : i+1] = insertion
975
976
977 def revert_inset_command(document):
978     """
979         Convert:
980             \begin_inset CommandInset InsetType
981             LatexCommand cmd
982         to
983             \begin_inset LatexCommand cmd
984         Some insets may end up being converted to insets earlier versions of LyX
985         will not be able to recognize. Not sure what to do about that.
986     """
987     i = 0
988     while 1:
989         i = find_token(document.body, "\\begin_inset CommandInset", i)
990         if i == -1:
991             return
992         nextline = document.body[i+1]
993         r = re.compile(r'LatexCommand\s+(.*)$')
994         m = r.match(nextline)
995         if not m:
996             document.warning("Malformed LyX document: Missing LatexCommand in " + document.body[i] + ".")
997             continue
998         cmdName = m.group(1)
999         insertion = ["\\begin_inset LatexCommand " + cmdName]
1000         document.body[i : i+2] = insertion
1001
1002
1003 def convert_wrapfig_options(document):
1004     "Convert optional options for wrap floats (wrapfig)."
1005     # adds the tokens "lines", "placement", and "overhang"
1006     i = 0
1007     while True:
1008         i = find_token(document.body, "\\begin_inset Wrap figure", i)
1009         if i == -1:
1010             return
1011         document.body.insert(i + 1, "lines 0")
1012         j = find_token(document.body, "placement", i)
1013         # placement can be already set or not; if not, set it
1014         if j == i+2:
1015             document.body.insert(i + 3, "overhang 0col%")
1016         else:
1017            document.body.insert(i + 2, "placement o")
1018            document.body.insert(i + 3, "overhang 0col%")
1019         i = i + 1
1020
1021
1022 def revert_wrapfig_options(document):
1023     "Revert optional options for wrap floats (wrapfig)."
1024     i = 0
1025     while True:
1026         i = find_token(document.body, "\\begin_inset Wrap figure", i)
1027         if i == -1:
1028             return
1029         j = find_end_of_inset(document.body, i)
1030         if j == -1:
1031             document.warning("Can't find end of Wrap inset at line " + str(i))
1032             i += 1
1033             continue
1034         k = find_default_layout(document, i, j)
1035         if k == -1:
1036             document.warning("Can't find default layout for Wrap figure!")
1037             i = j
1038             continue
1039         # Options should be between i and k now
1040         l = find_token(document.body, "lines", i, k)
1041         if l == -1:
1042             document.warning("Can't find lines option for Wrap figure!")
1043             i = k
1044             continue
1045         m = find_token(document.body, "overhang", i + 1, k)
1046         if m == -1:
1047             document.warning("Malformed LyX document: Couldn't find overhang parameter of wrap float!")
1048             i = k
1049             continue
1050         # Do these in reverse order
1051         del document.body[m]
1052         del document.body[l]
1053         i = k
1054
1055
1056 def convert_latexcommand_index(document):
1057     "Convert from LatexCommand form to collapsable form."
1058     i = 0
1059     r1 = re.compile('name "(.*)"')
1060     while True:
1061         i = find_token(document.body, "\\begin_inset CommandInset index", i)
1062         if i == -1:
1063             return
1064         if document.body[i + 1] != "LatexCommand index": # Might also be index_print
1065             return
1066         j = find_end_of_inset(document.body, i + 2)
1067         if j == -1:
1068             document.warning("Unable to find end of index inset at line " + i + "!")
1069             i += 2
1070             continue
1071         m = r1.match(document.body[i + 2])
1072         if m == None:
1073             document.warning("Unable to match: " + document.body[i+2])
1074             # this can happen with empty index insets!
1075             linelist = [""]
1076         else:
1077             fullcontent = m.group(1)
1078             linelist = latex2lyx(fullcontent)
1079         #document.warning(fullcontent)
1080
1081         linelist = ["\\begin_inset Index", "status collapsed", "\\begin_layout Standard", ""] + \
1082                    linelist + ["\\end_layout"]
1083         document.body[i : j] = linelist
1084         i += len(linelist) - (j - i)
1085
1086
1087 def revert_latexcommand_index(document):
1088     "Revert from collapsable form to LatexCommand form."
1089     i = 0
1090     while True:
1091         i = find_token(document.body, "\\begin_inset Index", i)
1092         if i == -1:
1093           return
1094         j = find_end_of_inset(document.body, i + 1)
1095         if j == -1:
1096           return
1097
1098         content = lyx2latex(document, document.body[i:j])
1099         # escape quotes
1100         content = content.replace('"', r'\"')
1101         document.body[i:j] = ["\\begin_inset CommandInset index", "LatexCommand index",
1102             "name " + '"' + content + '"', ""]
1103         i += 5
1104
1105
1106 def revert_wraptable(document):
1107     "Revert wrap table to wrap figure."
1108     i = 0
1109     while True:
1110         i = find_token(document.body, "\\begin_inset Wrap table", i)
1111         if i == -1:
1112             return
1113         document.body[i] = document.body[i].replace('\\begin_inset Wrap table', '\\begin_inset Wrap figure')
1114         i = i + 1
1115
1116
1117 def revert_vietnamese(document):
1118     "Set language Vietnamese to English"
1119     # Set document language from Vietnamese to English
1120     i = 0
1121     if document.language == "vietnamese":
1122         document.language = "english"
1123         i = find_token(document.header, "\\language", 0)
1124         if i != -1:
1125             document.header[i] = "\\language english"
1126     j = 0
1127     while True:
1128         j = find_token(document.body, "\\lang vietnamese", j)
1129         if j == -1:
1130             return
1131         document.body[j] = document.body[j].replace("\\lang vietnamese", "\\lang english")
1132         j = j + 1
1133
1134
1135 def convert_japanese_cjk(document):
1136     "Set language japanese to japanese-cjk"
1137     # Set document language from japanese-plain to japanese
1138     i = 0
1139     if document.language == "japanese":
1140         document.language = "japanese-cjk"
1141         i = find_token(document.header, "\\language", 0)
1142         if i != -1:
1143             document.header[i] = "\\language japanese-cjk"
1144     j = 0
1145     while True:
1146         j = find_token(document.body, "\\lang japanese", j)
1147         if j == -1:
1148             return
1149         document.body[j] = document.body[j].replace("\\lang japanese", "\\lang japanese-cjk")
1150         j = j + 1
1151
1152
1153 def revert_japanese(document):
1154     "Set language japanese-plain to japanese"
1155     # Set document language from japanese-plain to japanese
1156     i = 0
1157     if document.language == "japanese-plain":
1158         document.language = "japanese"
1159         i = find_token(document.header, "\\language", 0)
1160         if i != -1:
1161             document.header[i] = "\\language japanese"
1162     j = 0
1163     while True:
1164         j = find_token(document.body, "\\lang japanese-plain", j)
1165         if j == -1:
1166             return
1167         document.body[j] = document.body[j].replace("\\lang japanese-plain", "\\lang japanese")
1168         j = j + 1
1169
1170
1171 def revert_japanese_cjk(document):
1172     "Set language japanese-cjk to japanese"
1173     # Set document language from japanese-plain to japanese
1174     i = 0
1175     if document.language == "japanese-cjk":
1176         document.language = "japanese"
1177         i = find_token(document.header, "\\language", 0)
1178         if i != -1:
1179             document.header[i] = "\\language japanese"
1180     j = 0
1181     while True:
1182         j = find_token(document.body, "\\lang japanese-cjk", j)
1183         if j == -1:
1184             return
1185         document.body[j] = document.body[j].replace("\\lang japanese-cjk", "\\lang japanese")
1186         j = j + 1
1187
1188
1189 def revert_japanese_encoding(document):
1190     "Set input encoding form EUC-JP-plain to EUC-JP etc."
1191     # Set input encoding form EUC-JP-plain to EUC-JP etc.
1192     i = 0
1193     i = find_token(document.header, "\\inputencoding EUC-JP-plain", 0)
1194     if i != -1:
1195         document.header[i] = "\\inputencoding EUC-JP"
1196     j = 0
1197     j = find_token(document.header, "\\inputencoding JIS-plain", 0)
1198     if j != -1:
1199         document.header[j] = "\\inputencoding JIS"
1200     k = 0
1201     k = find_token(document.header, "\\inputencoding SJIS-plain", 0)
1202     if k != -1: # convert to UTF8 since there is currently no SJIS encoding
1203         document.header[k] = "\\inputencoding UTF8"
1204
1205
1206 def revert_inset_info(document):
1207     'Replace info inset with its content'
1208     i = 0
1209     while 1:
1210         i = find_token(document.body, '\\begin_inset Info', i)
1211         if i == -1:
1212             return
1213         j = find_end_of_inset(document.body, i + 1)
1214         if j == -1:
1215             # should not happen
1216             document.warning("Malformed LyX document: Could not find end of Info inset.")
1217         type = 'unknown'
1218         arg = ''
1219         for k in range(i, j+1):
1220             if document.body[k].startswith("arg"):
1221                 arg = document.body[k][3:].strip().strip('"')
1222             if document.body[k].startswith("type"):
1223                 type = document.body[k][4:].strip().strip('"')
1224         # I think there is a newline after \\end_inset, which should be removed.
1225         if document.body[j + 1].strip() == "":
1226             document.body[i : (j + 2)] = [type + ':' + arg]
1227         else:
1228             document.body[i : (j + 1)] = [type + ':' + arg]
1229
1230
1231 def convert_pdf_options(document):
1232     # Set the pdfusetitle tag, delete the pdf_store_options,
1233     # set quotes for bookmarksopenlevel"
1234     has_hr = get_value(document.header, "\\use_hyperref", 0, default = "0")
1235     if has_hr == "1":
1236         k = find_token(document.header, "\\use_hyperref", 0)
1237         document.header.insert(k + 1, "\\pdf_pdfusetitle true")
1238     k = find_token(document.header, "\\pdf_store_options", 0)
1239     if k != -1:
1240         del document.header[k]
1241     i = find_token(document.header, "\\pdf_bookmarksopenlevel", k)
1242     if i == -1: return
1243     document.header[i] = document.header[i].replace('"', '')
1244
1245
1246 def revert_pdf_options_2(document):
1247     # reset the pdfusetitle tag, set quotes for bookmarksopenlevel"
1248     k = find_token(document.header, "\\use_hyperref", 0)
1249     i = find_token(document.header, "\\pdf_pdfusetitle", k)
1250     if i != -1:
1251         del document.header[i]
1252     i = find_token(document.header, "\\pdf_bookmarksopenlevel", k)
1253     if i == -1: return
1254     values = document.header[i].split()
1255     values[1] = ' "' + values[1] + '"'
1256     document.header[i] = ''.join(values)
1257
1258
1259 def convert_htmlurl(document):
1260     'Convert "htmlurl" to "href" insets for docbook'
1261     if document.backend != "docbook":
1262       return
1263     i = 0
1264     while True:
1265       i = find_token(document.body, "\\begin_inset CommandInset url", i)
1266       if i == -1:
1267         return
1268       document.body[i] = "\\begin_inset CommandInset href"
1269       document.body[i + 1] = "LatexCommand href"
1270       i = i + 1
1271
1272
1273 def convert_url(document):
1274     'Convert url insets to url charstyles'
1275     if document.backend == "docbook":
1276       return
1277     i = 0
1278     while True:
1279       i = find_token(document.body, "\\begin_inset CommandInset url", i)
1280       if i == -1:
1281         break
1282       n = find_token(document.body, "name", i)
1283       if n == i + 2:
1284         # place the URL name in typewriter before the new URL insert
1285         # grab the name 'bla' from the e.g. the line 'name "bla"',
1286         # therefore start with the 6th character
1287         name = document.body[n][6:-1]
1288         newname = [name + " "]
1289         document.body[i:i] = newname
1290         i = i + 1
1291       j = find_token(document.body, "target", i)
1292       if j == -1:
1293         document.warning("Malformed LyX document: Can't find target for url inset")
1294         i = j
1295         continue
1296       target = document.body[j][8:-1]
1297       k = find_token(document.body, "\\end_inset", j)
1298       if k == -1:
1299         document.warning("Malformed LyX document: Can't find end of url inset")
1300         i = k
1301         continue
1302       newstuff = ["\\begin_inset Flex URL",
1303         "status collapsed", "",
1304         "\\begin_layout Standard",
1305         "",
1306         target,
1307         "\\end_layout",
1308         ""]
1309       document.body[i:k] = newstuff
1310       i = k
1311
1312 def convert_ams_classes(document):
1313   tc = document.textclass
1314   if (tc != "amsart" and tc != "amsart-plain" and
1315       tc != "amsart-seq" and tc != "amsbook"):
1316     return
1317   if tc == "amsart-plain":
1318     document.textclass = "amsart"
1319     document.set_textclass()
1320     document.add_module("Theorems (Starred)")
1321     return
1322   if tc == "amsart-seq":
1323     document.textclass = "amsart"
1324     document.set_textclass()
1325   document.add_module("Theorems (AMS)")
1326
1327   #Now we want to see if any of the environments in the extended theorems
1328   #module were used in this document. If so, we'll add that module, too.
1329   layouts = ["Criterion", "Algorithm", "Axiom", "Condition", "Note",  \
1330     "Notation", "Summary", "Acknowledgement", "Conclusion", "Fact", \
1331     "Assumption"]
1332
1333   r = re.compile(r'^\\begin_layout (.*?)\*?\s*$')
1334   i = 0
1335   while True:
1336     i = find_token(document.body, "\\begin_layout", i)
1337     if i == -1:
1338       return
1339     m = r.match(document.body[i])
1340     if m == None:
1341       # This is an empty layout
1342       # document.warning("Weirdly formed \\begin_layout at line %d of body!" % i)
1343       i += 1
1344       continue
1345     m = m.group(1)
1346     if layouts.count(m) != 0:
1347       document.add_module("Theorems (AMS-Extended)")
1348       return
1349     i += 1
1350
1351 def revert_href(document):
1352     'Reverts hyperlink insets (href) to url insets (url)'
1353     i = 0
1354     while True:
1355       i = find_token(document.body, "\\begin_inset CommandInset href", i)
1356       if i == -1:
1357           return
1358       document.body[i : i + 2] = \
1359         ["\\begin_inset CommandInset url", "LatexCommand url"]
1360       i = i + 2
1361
1362 def revert_url(document):
1363     'Reverts Flex URL insets to old-style URL insets'
1364     i = 0
1365     while True:
1366         i = find_token(document.body, "\\begin_inset Flex URL", i)
1367         if i == -1:
1368             return
1369         j = find_end_of_inset(document.body, i)
1370         if j == -1:
1371             document.warning("Can't find end of inset in revert_url!")
1372             return
1373         k = find_default_layout(document, i, j)
1374         if k == -1:
1375             document.warning("Can't find default layout in revert_url!")
1376             i = j
1377             continue
1378         l = find_end_of(document.body, k, "\\begin_layout", "\\end_layout")
1379         if l == -1 or l >= j:
1380             document.warning("Can't find end of default layout in revert_url!")
1381             i = j
1382             continue
1383         # OK, so the inset's data is between lines k and l.
1384         data =  " ".join(document.body[k+1:l])
1385         data = data.strip()
1386         newinset = ["\\begin_inset LatexCommand url", "target \"" + data + "\"",\
1387                     "", "\\end_inset"]
1388         document.body[i:j+1] = newinset
1389         i = i + len(newinset)
1390
1391
1392 def convert_include(document):
1393   'Converts include insets to new format.'
1394   i = 0
1395   r = re.compile(r'\\begin_inset Include\s+\\([^{]+){([^}]*)}(?:\[(.*)\])?')
1396   while True:
1397     i = find_token(document.body, "\\begin_inset Include", i)
1398     if i == -1:
1399       return
1400     line = document.body[i]
1401     previewline = document.body[i + 1]
1402     m = r.match(line)
1403     if m == None:
1404       document.warning("Unable to match line " + str(i) + " of body!")
1405       i += 1
1406       continue
1407     cmd = m.group(1)
1408     fn  = m.group(2)
1409     opt = m.group(3)
1410     insertion = ["\\begin_inset CommandInset include",
1411        "LatexCommand " + cmd, previewline,
1412        "filename \"" + fn + "\""]
1413     newlines = 2
1414     if opt:
1415       insertion.append("lstparams " + '"' + opt + '"')
1416       newlines += 1
1417     document.body[i : i + 2] = insertion
1418     i += newlines
1419
1420
1421 def revert_include(document):
1422   'Reverts include insets to old format.'
1423   i = 0
1424   r0 = re.compile('preview.*')
1425   r1 = re.compile('LatexCommand (.+)')
1426   r2 = re.compile('filename "(.+)"')
1427   r3 = re.compile('lstparams "(.*)"')
1428   while True:
1429     i = find_token(document.body, "\\begin_inset CommandInset include", i)
1430     if i == -1:
1431       return
1432     nextline = i + 1
1433     m = r1.match(document.body[nextline])
1434     if m == None:
1435       document.warning("Malformed LyX document: No LatexCommand line for `" +
1436         document.body[i] + "' on line " + str(i) + ".")
1437       i += 1
1438       continue
1439     cmd = m.group(1)
1440     nextline += 1
1441     if r0.match(document.body[nextline]):
1442       previewline = document.body[nextline]
1443       nextline += 1
1444     else:
1445       previewline = ""
1446     m = r2.match(document.body[nextline])
1447     if m == None:
1448       document.warning("Malformed LyX document: No filename line for `" + \
1449         document.body[i] + "' on line " + str(i) + ".")
1450       i += 2
1451       continue
1452     fn = m.group(1)
1453     nextline += 1
1454     options = ""
1455     if (cmd == "lstinputlisting"):
1456       m = r3.match(document.body[nextline])
1457       if m != None:
1458         options = m.group(1)
1459         numlines = 5
1460         nextline += 1
1461     newline = "\\begin_inset Include \\" + cmd + "{" + fn + "}"
1462     if options:
1463       newline += ("[" + options + "]")
1464     insertion = [newline]
1465     if previewline != "":
1466       insertion.append(previewline)
1467     document.body[i : nextline] = insertion
1468     i += 2
1469
1470
1471 def revert_albanian(document):
1472     "Set language Albanian to English"
1473     i = 0
1474     if document.language == "albanian":
1475         document.language = "english"
1476         i = find_token(document.header, "\\language", 0)
1477         if i != -1:
1478             document.header[i] = "\\language english"
1479     j = 0
1480     while True:
1481         j = find_token(document.body, "\\lang albanian", j)
1482         if j == -1:
1483             return
1484         document.body[j] = document.body[j].replace("\\lang albanian", "\\lang english")
1485         j = j + 1
1486
1487
1488 def revert_lowersorbian(document):
1489     "Set language lower Sorbian to English"
1490     i = 0
1491     if document.language == "lowersorbian":
1492         document.language = "english"
1493         i = find_token(document.header, "\\language", 0)
1494         if i != -1:
1495             document.header[i] = "\\language english"
1496     j = 0
1497     while True:
1498         j = find_token(document.body, "\\lang lowersorbian", j)
1499         if j == -1:
1500             return
1501         document.body[j] = document.body[j].replace("\\lang lowersorbian", "\\lang english")
1502         j = j + 1
1503
1504
1505 def revert_uppersorbian(document):
1506     "Set language uppersorbian to usorbian as this was used in LyX 1.5"
1507     i = 0
1508     if document.language == "uppersorbian":
1509         document.language = "usorbian"
1510         i = find_token(document.header, "\\language", 0)
1511         if i != -1:
1512             document.header[i] = "\\language usorbian"
1513     j = 0
1514     while True:
1515         j = find_token(document.body, "\\lang uppersorbian", j)
1516         if j == -1:
1517             return
1518         document.body[j] = document.body[j].replace("\\lang uppersorbian", "\\lang usorbian")
1519         j = j + 1
1520
1521
1522 def convert_usorbian(document):
1523     "Set language usorbian to uppersorbian"
1524     i = 0
1525     if document.language == "usorbian":
1526         document.language = "uppersorbian"
1527         i = find_token(document.header, "\\language", 0)
1528         if i != -1:
1529             document.header[i] = "\\language uppersorbian"
1530     j = 0
1531     while True:
1532         j = find_token(document.body, "\\lang usorbian", j)
1533         if j == -1:
1534             return
1535         document.body[j] = document.body[j].replace("\\lang usorbian", "\\lang uppersorbian")
1536         j = j + 1
1537
1538
1539 def revert_macro_optional_params(document):
1540     "Convert macro definitions with optional parameters into ERTs"
1541     # Stub to convert macro definitions with one or more optional parameters
1542     # into uninterpreted ERT insets
1543
1544
1545 def revert_hyperlinktype(document):
1546     'Reverts hyperlink type'
1547     i = 0
1548     j = 0
1549     while True:
1550       i = find_token(document.body, "target", i)
1551       if i == -1:
1552           return
1553       j = find_token(document.body, "type", i)
1554       if j == -1:
1555           return
1556       if j == i + 1:
1557           del document.body[j]
1558       i = i + 1
1559
1560
1561 def revert_pagebreak(document):
1562     'Reverts pagebreak to ERT'
1563     i = 0
1564     while True:
1565       i = find_token(document.body, "\\pagebreak", i)
1566       if i == -1:
1567           return
1568       document.body[i] = '\\begin_inset ERT\nstatus collapsed\n\n' \
1569       '\\begin_layout Standard\n\n\n\\backslash\n' \
1570       'pagebreak{}\n\\end_layout\n\n\\end_inset\n\n'
1571       i = i + 1
1572
1573
1574 def revert_linebreak(document):
1575     'Reverts linebreak to ERT'
1576     i = 0
1577     while True:
1578       i = find_token(document.body, "\\linebreak", i)
1579       if i == -1:
1580           return
1581       document.body[i] = '\\begin_inset ERT\nstatus collapsed\n\n' \
1582       '\\begin_layout Standard\n\n\n\\backslash\n' \
1583       'linebreak{}\n\\end_layout\n\n\\end_inset\n\n'
1584       i = i + 1
1585
1586
1587 def revert_latin(document):
1588     "Set language Latin to English"
1589     i = 0
1590     if document.language == "latin":
1591         document.language = "english"
1592         i = find_token(document.header, "\\language", 0)
1593         if i != -1:
1594             document.header[i] = "\\language english"
1595     j = 0
1596     while True:
1597         j = find_token(document.body, "\\lang latin", j)
1598         if j == -1:
1599             return
1600         document.body[j] = document.body[j].replace("\\lang latin", "\\lang english")
1601         j = j + 1
1602
1603
1604 def revert_samin(document):
1605     "Set language North Sami to English"
1606     i = 0
1607     if document.language == "samin":
1608         document.language = "english"
1609         i = find_token(document.header, "\\language", 0)
1610         if i != -1:
1611             document.header[i] = "\\language english"
1612     j = 0
1613     while True:
1614         j = find_token(document.body, "\\lang samin", j)
1615         if j == -1:
1616             return
1617         document.body[j] = document.body[j].replace("\\lang samin", "\\lang english")
1618         j = j + 1
1619
1620
1621 def convert_serbocroatian(document):
1622     "Set language Serbocroatian to Croatian as this was really Croatian in LyX 1.5"
1623     i = 0
1624     if document.language == "serbocroatian":
1625         document.language = "croatian"
1626         i = find_token(document.header, "\\language", 0)
1627         if i != -1:
1628             document.header[i] = "\\language croatian"
1629     j = 0
1630     while True:
1631         j = find_token(document.body, "\\lang serbocroatian", j)
1632         if j == -1:
1633             return
1634         document.body[j] = document.body[j].replace("\\lang serbocroatian", "\\lang croatian")
1635         j = j + 1
1636
1637
1638 def convert_framed_notes(document):
1639     "Convert framed notes to boxes. "
1640     i = 0
1641     while 1:
1642         i = find_tokens(document.body, ["\\begin_inset Note Framed", "\\begin_inset Note Shaded"], i)
1643         if i == -1:
1644             return
1645         subst = [document.body[i].replace("\\begin_inset Note", "\\begin_inset Box"),
1646                  'position "t"',
1647                  'hor_pos "c"',
1648                  'has_inner_box 0',
1649                  'inner_pos "t"',
1650                  'use_parbox 0',
1651                  'width "100col%"',
1652                  'special "none"',
1653                  'height "1in"',
1654                  'height_special "totalheight"']
1655         document.body[i:i+1] = subst
1656         i = i + 9
1657
1658
1659 def convert_module_names(document):
1660   modulemap = { 'Braille' : 'braille', 'Endnote' : 'endnotes', 'Foot to End' : 'foottoend',\
1661     'Hanging' : 'hanging', 'Linguistics' : 'linguistics', 'Logical Markup' : 'logicalmkup', \
1662     'Theorems (AMS-Extended)' : 'theorems-ams-extended', 'Theorems (AMS)' : 'theorems-ams', \
1663     'Theorems (Order By Chapter)' : 'theorems-chap', 'Theorems (Order By Section)' : 'theorems-sec', \
1664     'Theorems (Starred)' : 'theorems-starred', 'Theorems' : 'theorems-std' }
1665   modlist = document.get_module_list()
1666   if len(modlist) == 0:
1667     return
1668   newmodlist = []
1669   for mod in modlist:
1670     if modulemap.has_key(mod):
1671       newmodlist.append(modulemap[mod])
1672     else:
1673       document.warning("Can't find module %s in the module map!" % mod)
1674       newmodlist.append(mod)
1675   document.set_module_list(newmodlist)
1676
1677
1678 def revert_module_names(document):
1679   modulemap = { 'braille' : 'Braille', 'endnotes' : 'Endnote', 'foottoend' : 'Foot to End',\
1680     'hanging' : 'Hanging', 'linguistics' : 'Linguistics', 'logicalmkup' : 'Logical Markup', \
1681     'theorems-ams-extended' : 'Theorems (AMS-Extended)', 'theorems-ams' : 'Theorems (AMS)', \
1682     'theorems-chap' : 'Theorems (Order By Chapter)', 'theorems-sec' : 'Theorems (Order By Section)', \
1683     'theorems-starred' : 'Theorems (Starred)', 'theorems-std' : 'Theorems'}
1684   modlist = document.get_module_list()
1685   if len(modlist) == 0:
1686     return
1687   newmodlist = []
1688   for mod in modlist:
1689     if modulemap.has_key(mod):
1690       newmodlist.append(modulemap[mod])
1691     else:
1692       document.warning("Can't find module %s in the module map!" % mod)
1693       newmodlist.append(mod)
1694   document.set_module_list(newmodlist)
1695
1696
1697 def revert_colsep(document):
1698     i = find_token(document.header, "\\columnsep", 0)
1699     if i == -1:
1700         return
1701     colsepline = document.header[i]
1702     r = re.compile(r'\\columnsep (.*)')
1703     m = r.match(colsepline)
1704     if not m:
1705         document.warning("Malformed column separation line!")
1706         return
1707     colsep = m.group(1)
1708     del document.header[i]
1709     #it seems to be safe to add the package even if it is already used
1710     pretext = ["\\usepackage{geometry}", "\\geometry{columnsep=" + colsep + "}"]
1711
1712     add_to_preamble(document, pretext)
1713
1714
1715 def revert_framed_notes(document):
1716     "Revert framed boxes to notes. "
1717     i = 0
1718     while 1:
1719         i = find_tokens(document.body, ["\\begin_inset Box Framed", "\\begin_inset Box Shaded"], i)
1720
1721         if i == -1:
1722             return
1723         j = find_end_of_inset(document.body, i + 1)
1724         if j == -1:
1725             # should not happen
1726             document.warning("Malformed LyX document: Could not find end of Box inset.")
1727         k = find_token(document.body, "status", i + 1, j)
1728         if k == -1:
1729             document.warning("Malformed LyX document: Missing `status' tag in Box inset.")
1730             return
1731         status = document.body[k]
1732         l = find_default_layout(document, i + 1, j)
1733         if l == -1:
1734             document.warning("Malformed LyX document: Missing `\\begin_layout' in Box inset.")
1735             return
1736         m = find_token(document.body, "\\end_layout", i + 1, j)
1737         if m == -1:
1738             document.warning("Malformed LyX document: Missing `\\end_layout' in Box inset.")
1739             return
1740         ibox = find_token(document.body, "has_inner_box 1", i + 1, k)
1741         pbox = find_token(document.body, "use_parbox 1", i + 1, k)
1742         if ibox == -1 and pbox == -1:
1743             document.body[i] = document.body[i].replace("\\begin_inset Box", "\\begin_inset Note")
1744             del document.body[i+1:k]
1745         else:
1746             document.body[i] = document.body[i].replace("\\begin_inset Box Shaded", "\\begin_inset Box Frameless")
1747             subst1 = [document.body[l],
1748                       "\\begin_inset Note Shaded",
1749                       status,
1750                       '\\begin_layout Standard']
1751             document.body[l:l + 1] = subst1
1752             subst2 = [document.body[m], "\\end_layout", "\\end_inset"]
1753             document.body[m:m + 1] = subst2
1754         i = i + 1
1755
1756
1757 def revert_slash(document):
1758     'Revert \\SpecialChar \\slash{} to ERT'
1759     r = re.compile(r'\\SpecialChar \\slash{}')
1760     i = 0
1761     while i < len(document.body):
1762         m = r.match(document.body[i])
1763         if m:
1764           subst = ['\\begin_inset ERT',
1765                    'status collapsed', '',
1766                    '\\begin_layout Standard',
1767                    '', '', '\\backslash',
1768                    'slash{}',
1769                    '\\end_layout', '',
1770                    '\\end_inset', '']
1771           document.body[i: i+1] = subst
1772           i = i + len(subst)
1773         else:
1774           i = i + 1
1775
1776
1777 def revert_nobreakdash(document):
1778     'Revert \\SpecialChar \\nobreakdash- to ERT'
1779     i = 0
1780     while i < len(document.body):
1781         line = document.body[i]
1782         r = re.compile(r'\\SpecialChar \\nobreakdash-')
1783         m = r.match(line)
1784         if m:
1785             subst = ['\\begin_inset ERT',
1786                     'status collapsed', '',
1787                     '\\begin_layout Standard', '', '',
1788                     '\\backslash',
1789                     'nobreakdash-',
1790                     '\\end_layout', '',
1791                     '\\end_inset', '']
1792             document.body[i:i+1] = subst
1793             i = i + len(subst)
1794             j = find_token(document.header, "\\use_amsmath", 0)
1795             if j == -1:
1796                 document.warning("Malformed LyX document: Missing '\\use_amsmath'.")
1797                 return
1798             document.header[j] = "\\use_amsmath 2"
1799         else:
1800             i = i + 1
1801
1802
1803 #Returns number of lines added/removed
1804 def revert_nocite_key(body, start, end):
1805     'key "..." -> \nocite{...}'
1806     r = re.compile(r'^key "(.*)"')
1807     i = start
1808     j = end
1809     while i < j:
1810         m = r.match(body[i])
1811         if m:
1812             body[i:i+1] = ["\\backslash", "nocite{" + m.group(1) + "}"]
1813             j += 1     # because we added a line
1814             i += 2     # skip that line
1815         else:
1816             del body[i]
1817             j -= 1     # because we deleted a line
1818             # no need to change i, since it now points to the next line
1819     return j - end
1820
1821
1822 def revert_nocite(document):
1823     "Revert LatexCommand nocite to ERT"
1824     i = 0
1825     while 1:
1826         i = find_token(document.body, "\\begin_inset CommandInset citation", i)
1827         if i == -1:
1828             return
1829         if (document.body[i+1] != "LatexCommand nocite"):
1830             # note that we already incremented i
1831             i = i + 1
1832             continue
1833         insetEnd = find_end_of_inset(document.body, i)
1834         if insetEnd == -1:
1835             #this should not happen
1836             document.warning("End of CommandInset citation not found in revert_nocite!")
1837             return
1838
1839         paramLocation = i + 2 #start of the inset's parameters
1840         addedLines = 0
1841         document.body[i:i+2] = \
1842             ["\\begin_inset ERT", "status collapsed", "", "\\begin_layout Standard"]
1843         # that added two lines
1844         paramLocation += 2
1845         insetEnd += 2
1846         #print insetEnd, document.body[i: insetEnd + 1]
1847         insetEnd += revert_nocite_key(document.body, paramLocation, insetEnd)
1848         #print insetEnd, document.body[i: insetEnd + 1]
1849         document.body.insert(insetEnd, "\\end_layout")
1850         document.body.insert(insetEnd + 1, "")
1851         i = insetEnd + 1
1852
1853
1854 def revert_btprintall(document):
1855     "Revert (non-bibtopic) btPrintAll option to ERT \nocite{*}"
1856     i = find_token(document.header, '\\use_bibtopic', 0)
1857     if i == -1:
1858         document.warning("Malformed lyx document: Missing '\\use_bibtopic'.")
1859         return
1860     if get_value(document.header, '\\use_bibtopic', 0) == "false":
1861         i = 0
1862         while i < len(document.body):
1863             i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
1864             if i == -1:
1865                 return
1866             j = find_end_of_inset(document.body, i + 1)
1867             if j == -1:
1868                 #this should not happen
1869                 document.warning("End of CommandInset bibtex not found in revert_btprintall!")
1870                 j = len(document.body)
1871             # this range isn't really right, but it should be OK, since we shouldn't
1872             # see more than one matching line in each inset
1873             addedlines = 0
1874             for k in range(i, j):
1875                 if (document.body[k] == 'btprint "btPrintAll"'):
1876                     del document.body[k]
1877                     subst = ["\\begin_inset ERT",
1878                              "status collapsed", "",
1879                              "\\begin_layout Standard", "",
1880                              "\\backslash",
1881                              "nocite{*}",
1882                              "\\end_layout",
1883                              "\\end_inset"]
1884                     document.body[i:i] = subst
1885                     addlines = addedlines + len(subst) - 1
1886             i = j + addedlines
1887
1888
1889 def revert_bahasam(document):
1890     "Set language Bahasa Malaysia to Bahasa Indonesia"
1891     i = 0
1892     if document.language == "bahasam":
1893         document.language = "bahasa"
1894         i = find_token(document.header, "\\language", 0)
1895         if i != -1:
1896             document.header[i] = "\\language bahasa"
1897     j = 0
1898     while True:
1899         j = find_token(document.body, "\\lang bahasam", j)
1900         if j == -1:
1901             return
1902         document.body[j] = document.body[j].replace("\\lang bahasam", "\\lang bahasa")
1903         j = j + 1
1904
1905
1906 def revert_interlingua(document):
1907     "Set language Interlingua to English"
1908     i = 0
1909     if document.language == "interlingua":
1910         document.language = "english"
1911         i = find_token(document.header, "\\language", 0)
1912         if i != -1:
1913             document.header[i] = "\\language english"
1914     j = 0
1915     while True:
1916         j = find_token(document.body, "\\lang interlingua", j)
1917         if j == -1:
1918             return
1919         document.body[j] = document.body[j].replace("\\lang interlingua", "\\lang english")
1920         j = j + 1
1921
1922
1923 def revert_serbianlatin(document):
1924     "Set language Serbian-Latin to Croatian"
1925     i = 0
1926     if document.language == "serbian-latin":
1927         document.language = "croatian"
1928         i = find_token(document.header, "\\language", 0)
1929         if i != -1:
1930             document.header[i] = "\\language croatian"
1931     j = 0
1932     while True:
1933         j = find_token(document.body, "\\lang serbian-latin", j)
1934         if j == -1:
1935             return
1936         document.body[j] = document.body[j].replace("\\lang serbian-latin", "\\lang croatian")
1937         j = j + 1
1938
1939
1940 def revert_rotfloat(document):
1941     " Revert sideways custom floats. "
1942     i = 0
1943     while 1:
1944         # whitespace intended (exclude \\begin_inset FloatList)
1945         i = find_token(document.body, "\\begin_inset Float ", i)
1946         if i == -1:
1947             return
1948         line = document.body[i]
1949         r = re.compile(r'\\begin_inset Float (.*)$')
1950         m = r.match(line)
1951         if m == None:
1952             document.warning("Unable to match line " + str(i) + " of body!")
1953             i += 1
1954             continue
1955         floattype = m.group(1)
1956         if floattype == "figure" or floattype == "table":
1957             i += 1
1958             continue
1959         j = find_end_of_inset(document.body, i)
1960         if j == -1:
1961             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_rotfloat.")
1962             i += 1
1963             continue
1964         addedLines = 0
1965         if get_value(document.body, 'sideways', i, j) == "false":
1966             i += 1
1967             continue
1968         l = find_default_layout(document, i + 1, j)
1969         if l == -1:
1970             document.warning("Malformed LyX document: Missing `\\begin_layout' in Float inset.")
1971             return
1972         subst = ['\\begin_layout Standard',
1973                   '\\begin_inset ERT',
1974                   'status collapsed', '',
1975                   '\\begin_layout Standard', '', '',
1976                   '\\backslash', '',
1977                   'end{sideways' + floattype + '}',
1978                   '\\end_layout', '', '\\end_inset']
1979         document.body[j : j+1] = subst
1980         addedLines = len(subst) - 1
1981         del document.body[i+1 : l]
1982         addedLines -= (l-1) - (i+1)
1983         subst = ['\\begin_inset ERT', 'status collapsed', '',
1984                   '\\begin_layout Standard', '', '', '\\backslash',
1985                   'begin{sideways' + floattype + '}',
1986                   '\\end_layout', '', '\\end_inset', '',
1987                   '\\end_layout', '']
1988         document.body[i : i+1] = subst
1989         addedLines += len(subst) - 1
1990         if floattype == "algorithm":
1991             add_to_preamble(document,
1992                             ['% Commands inserted by lyx2lyx for sideways algorithm float',
1993                               '\\usepackage{rotfloat}',
1994                               '\\floatstyle{ruled}',
1995                               '\\newfloat{algorithm}{tbp}{loa}',
1996                               '\\floatname{algorithm}{Algorithm}'])
1997         else:
1998             document.warning("Cannot create preamble definition for custom float" + floattype + ".")
1999         i += addedLines + 1
2000
2001
2002 def revert_widesideways(document):
2003     " Revert wide sideways floats. "
2004     i = 0
2005     while 1:
2006         # whitespace intended (exclude \\begin_inset FloatList)
2007         i = find_token(document.body, '\\begin_inset Float ', i)
2008         if i == -1:
2009             return
2010         line = document.body[i]
2011         r = re.compile(r'\\begin_inset Float (.*)$')
2012         m = r.match(line)
2013         if m == None:
2014             document.warning("Unable to match line " + str(i) + " of body!")
2015             i += 1
2016             continue
2017         floattype = m.group(1)
2018         if floattype != "figure" and floattype != "table":
2019             i += 1
2020             continue
2021         j = find_end_of_inset(document.body, i)
2022         if j == -1:
2023             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_widesideways.")
2024             i += 1
2025             continue
2026         if get_value(document.body, 'sideways', i, j) == "false" or \
2027            get_value(document.body, 'wide', i, j) == "false":
2028              i += 1
2029              continue
2030         l = find_default_layout(document, i + 1, j)
2031         if l == -1:
2032             document.warning("Malformed LyX document: Missing `\\begin_layout' in Float inset.")
2033             return
2034         subst = ['\\begin_layout Standard', '\\begin_inset ERT',
2035                   'status collapsed', '',
2036                   '\\begin_layout Standard', '', '', '\\backslash',
2037                   'end{sideways' + floattype + '*}',
2038                   '\\end_layout', '', '\\end_inset']
2039         document.body[j : j+1] = subst
2040         addedLines = len(subst) - 1
2041         del document.body[i+1:l-1]
2042         addedLines -= (l-1) - (i+1)
2043         subst = ['\\begin_inset ERT', 'status collapsed', '',
2044                  '\\begin_layout Standard', '', '', '\\backslash',
2045                  'begin{sideways' + floattype + '*}', '\\end_layout', '',
2046                  '\\end_inset', '', '\\end_layout', '']
2047         document.body[i : i+1] = subst
2048         addedLines += len(subst) - 1
2049         add_to_preamble(document, ['\\usepackage{rotfloat}\n'])
2050         i += addedLines + 1
2051
2052
2053 def revert_inset_embedding(document, type):
2054     ' Remove embed tag from certain type of insets'
2055     i = 0
2056     while 1:
2057         i = find_token(document.body, "\\begin_inset %s" % type, i)
2058         if i == -1:
2059             return
2060         j = find_end_of_inset(document.body, i)
2061         if j == -1:
2062             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_inset_embedding.")
2063             i = i + 1
2064             continue
2065         k = find_token(document.body, "\tembed", i, j)
2066         if k == -1:
2067             k = find_token(document.body, "embed", i, j)
2068         if k != -1:
2069             del document.body[k]
2070         i = i + 1
2071
2072
2073 def revert_external_embedding(document):
2074     ' Remove embed tag from external inset '
2075     revert_inset_embedding(document, 'External')
2076
2077
2078 def convert_subfig(document):
2079     " Convert subfigures to subfloats. "
2080     i = 0
2081     while 1:
2082         i = find_token(document.body, '\\begin_inset Graphics', i)
2083         if i == -1:
2084             return
2085         endInset = find_end_of_inset(document.body, i)
2086         if endInset == -1:
2087             document.warning("Malformed lyx document: Missing '\\end_inset' in convert_subfig.")
2088             i += 1
2089             continue
2090         k = find_token(document.body, '\tsubcaption', i, endInset)
2091         if k == -1:
2092             i = endInset
2093             continue
2094         l = find_token(document.body, '\tsubcaptionText', i, endInset)
2095         if l == -1:
2096             document.warning("Malformed lyx document: Can't find subcaptionText!")
2097             i = endInset
2098             continue
2099         caption = document.body[l][16:].strip('"')
2100         del document.body[l]
2101         del document.body[k]
2102         addedLines = -2
2103         subst = ['\\begin_inset Float figure', 'wide false', 'sideways false',
2104                  'status open', '', '\\begin_layout Plain Layout', '\\begin_inset Caption',
2105                  '', '\\begin_layout Plain Layout'] + latex2lyx(caption) + \
2106                  [ '\\end_layout', '', '\\end_inset', '',
2107                  '\\end_layout', '', '\\begin_layout Plain Layout']
2108         document.body[i : i] = subst
2109         addedLines += len(subst)
2110         endInset += addedLines
2111         subst = ['', '\\end_inset', '', '\\end_layout']
2112         document.body[endInset : endInset] = subst
2113         addedLines += len(subst)
2114         i += addedLines + 1
2115
2116
2117 def revert_subfig(document):
2118     " Revert subfloats. "
2119     i = 0
2120     while 1:
2121         # whitespace intended (exclude \\begin_inset FloatList)
2122         i = find_tokens(document.body, ['\\begin_inset Float ', '\\begin_inset Wrap'], i)
2123         if i == -1:
2124             return
2125         j = 0
2126         addedLines = 0
2127         while j != -1:
2128             j = find_end_of_inset(document.body, i)
2129             if j == -1:
2130                 document.warning("Malformed lyx document: Missing '\\end_inset' (float) at line " + str(i + len(document.header)) + ".\n\t" + document.body[i])
2131                 # document.warning(document.body[i-1] + "\n" + document.body[i+1])
2132                 i += 1
2133                 continue # this will get us back to the outer loop, since j == -1
2134             # look for embedded float (= subfloat)
2135             # whitespace intended (exclude \\begin_inset FloatList)
2136             k = find_token(document.body, '\\begin_inset Float ', i + 1, j)
2137             if k == -1:
2138                 break
2139             l = find_end_of_inset(document.body, k)
2140             if l == -1:
2141                 document.warning("Malformed lyx document: Missing '\\end_inset' (embedded float).")
2142                 i += 1
2143                 j == -1
2144                 continue # escape to the outer loop
2145             m = find_default_layout(document, k + 1, l)
2146             # caption?
2147             cap = find_token(document.body, '\\begin_inset Caption', k + 1, l)
2148             caption = ''
2149             shortcap = ''
2150             capend = cap
2151             if cap != -1:
2152                 capend = find_end_of_inset(document.body, cap)
2153                 if capend == -1:
2154                     document.warning("Malformed lyx document: Missing '\\end_inset' (caption).")
2155                     return
2156                 # label?
2157                 label = ''
2158                 lbl = find_token(document.body, '\\begin_inset CommandInset label', cap, capend)
2159                 if lbl != -1:
2160                     lblend = find_end_of_inset(document.body, lbl + 1)
2161                     if lblend == -1:
2162                         document.warning("Malformed lyx document: Missing '\\end_inset' (label).")
2163                         return
2164                     for line in document.body[lbl:lblend + 1]:
2165                         if line.startswith('name '):
2166                             label = line.split()[1].strip('"')
2167                             break
2168                 else:
2169                     lbl = capend
2170                     lblend = capend
2171                     label = ''
2172                 # opt arg?
2173                 opt = find_token(document.body, '\\begin_inset OptArg', cap, capend)
2174                 if opt != -1:
2175                     optend = find_end_of_inset(document.body, opt)
2176                     if optend == -1:
2177                         document.warning("Malformed lyx document: Missing '\\end_inset' (OptArg).")
2178                         return
2179                     optc = find_default_layout(document, opt, optend)
2180                     if optc == -1:
2181                         document.warning("Malformed LyX document: Missing `\\begin_layout' in Float inset.")
2182                         return
2183                     optcend = find_end_of(document.body, optc, "\\begin_layout", "\\end_layout")
2184                     for line in document.body[optc:optcend]:
2185                         if not line.startswith('\\'):
2186                             shortcap += line.strip()
2187                 else:
2188                     opt = capend
2189                     optend = capend
2190                 for line in document.body[cap:capend]:
2191                     if line in document.body[lbl:lblend]:
2192                         continue
2193                     elif line in document.body[opt:optend]:
2194                         continue
2195                     elif not line.startswith('\\'):
2196                         caption += line.strip()
2197                 if len(label) > 0:
2198                     caption += "\\backslash\nlabel{" + label + "}"
2199             subst = '\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus collapsed\n\n' \
2200                       '\\begin_layout Plain Layout\n\n}\n\\end_layout\n\n\\end_inset\n\n' \
2201                       '\\end_layout\n\n\\begin_layout Plain Layout\n'
2202             subst = subst.split('\n')
2203             document.body[l : l+1] = subst
2204             addedLines = len(subst) - 1
2205             # this is before l and so is unchanged by the multiline insertion
2206             if cap != capend:
2207                 del document.body[cap:capend+1]
2208                 addedLines -= (capend + 1 - cap)
2209             del document.body[k+1:m-1]
2210             addedLines -= (m - 1 - (k + 1))
2211             insertion = '\\begin_inset ERT\nstatus collapsed\n\n' \
2212                         '\\begin_layout Plain Layout\n\n\\backslash\n' \
2213                         'subfloat'
2214             if len(shortcap) > 0:
2215                 insertion = insertion + "[" + shortcap + "]"
2216             if len(caption) > 0:
2217                 insertion = insertion + "[" + caption + "]"
2218             insertion = insertion + '{%\n\\end_layout\n\n\\end_inset\n\n\\end_layout\n'
2219             insertion = insertion.split('\n')
2220             document.body[k : k + 1] = insertion
2221             addedLines += len(insertion) - 1
2222             add_to_preamble(document, ['\\usepackage{subfig}\n'])
2223         i += addedLines + 1
2224
2225
2226 def revert_wrapplacement(document):
2227     " Revert placement options wrap floats (wrapfig). "
2228     i = 0
2229     while True:
2230         i = find_token(document.body, "\\begin_inset Wrap figure", i)
2231         if i == -1:
2232             return
2233         e = find_end_of_inset(document.body, i)
2234         j = find_token(document.body, "placement", i + 1, e)
2235         if j == -1:
2236             document.warning("Malformed LyX document: Couldn't find placement parameter of wrap float.")
2237             i += 1
2238             continue
2239         r = re.compile("placement (o|i|l|r)")
2240         m = r.match(document.body[j])
2241         if m == None:
2242             document.warning("Malformed LyX document: Placement option isn't O|I|R|L!")
2243         document.body[j] = "placement " + m.group(1).lower()
2244         i = j
2245
2246
2247 def remove_extra_embedded_files(document):
2248     " Remove \extra_embedded_files from buffer params "
2249     i = find_token(document.header, '\\extra_embedded_files', 0)
2250     if i == -1:
2251         return
2252     document.header.pop(i)
2253
2254
2255 def convert_spaceinset(document):
2256     " Convert '\\InsetSpace foo' to '\\begin_inset Space foo\n\\end_inset' "
2257     i = 0
2258     while i < len(document.body):
2259         m = re.match(r'(.*)\\InsetSpace (.*)', document.body[i])
2260         if m:
2261             before = m.group(1)
2262             after = m.group(2)
2263             subst = [before, "\\begin_inset Space " + after, "\\end_inset"]
2264             document.body[i: i+1] = subst
2265             i = i + len(subst)
2266         else:
2267             i = i + 1
2268
2269
2270 def revert_spaceinset(document):
2271     " Revert '\\begin_inset Space foo\n\\end_inset' to '\\InsetSpace foo' "
2272     i = 0
2273     while True:
2274         i = find_token(document.body, "\\begin_inset Space", i)
2275         if i == -1:
2276             return
2277         j = find_end_of_inset(document.body, i)
2278         if j == -1:
2279             document.warning("Malformed LyX document: Could not find end of space inset.")
2280             continue
2281         document.body[i] = document.body[i].replace('\\begin_inset Space', '\\InsetSpace')
2282         del document.body[j]
2283
2284
2285 def convert_hfill(document):
2286     " Convert hfill to space inset "
2287     i = 0
2288     while True:
2289         i = find_token(document.body, "\\hfill", i)
2290         if i == -1:
2291             return
2292         subst = document.body[i].replace('\\hfill', \
2293                   '\n\\begin_inset Space \\hfill{}\n\\end_inset')
2294         subst = subst.split('\n')
2295         document.body[i : i+1] = subst
2296         i += len(subst)
2297
2298
2299 def revert_hfills(document):
2300     ' Revert \\hfill commands '
2301     hfill = re.compile(r'\\hfill')
2302     dotfill = re.compile(r'\\dotfill')
2303     hrulefill = re.compile(r'\\hrulefill')
2304     i = 0
2305     while True:
2306         i = find_token(document.body, "\\InsetSpace", i)
2307         if i == -1:
2308             return
2309         if hfill.search(document.body[i]):
2310             document.body[i] = \
2311               document.body[i].replace('\\InsetSpace \\hfill{}', '\\hfill')
2312             i += 1
2313             continue
2314         if dotfill.search(document.body[i]):
2315             subst = document.body[i].replace('\\InsetSpace \\dotfill{}', \
2316               '\\begin_inset ERT\nstatus collapsed\n\n' \
2317               '\\begin_layout Standard\n\n\n\\backslash\n' \
2318               'dotfill{}\n\\end_layout\n\n\\end_inset\n\n')
2319             subst = subst.split('\n')
2320             document.body[i : i+1] = subst
2321             i += len(subst)
2322             continue
2323         if hrulefill.search(document.body[i]):
2324             subst = document.body[i].replace('\\InsetSpace \\hrulefill{}', \
2325               '\\begin_inset ERT\nstatus collapsed\n\n' \
2326               '\\begin_layout Standard\n\n\n\\backslash\n' \
2327               'hrulefill{}\n\\end_layout\n\n\\end_inset\n\n')
2328             subst = subst.split('\n')
2329             document.body[i : i+1] = subst
2330             i += len(subst)
2331             continue
2332         i += 1
2333
2334 def revert_hspace(document):
2335     ' Revert \\InsetSpace \\hspace{} to ERT '
2336     i = 0
2337     hspace = re.compile(r'\\hspace{}')
2338     hstar  = re.compile(r'\\hspace\*{}')
2339     while True:
2340         i = find_token(document.body, "\\InsetSpace \\hspace", i)
2341         if i == -1:
2342             return
2343         length = get_value(document.body, '\\length', i+1)
2344         if length == '':
2345             document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
2346             return
2347         del document.body[i+1]
2348         addedLines = -1
2349         if hstar.search(document.body[i]):
2350             subst = document.body[i].replace('\\InsetSpace \\hspace*{}', \
2351               '\\begin_inset ERT\nstatus collapsed\n\n' \
2352               '\\begin_layout Standard\n\n\n\\backslash\n' \
2353               'hspace*{' + length + '}\n\\end_layout\n\n\\end_inset\n\n')
2354             subst = subst.split('\n')
2355             document.body[i : i+1] = subst
2356             addedLines += len(subst) - 1
2357             i += addedLines + 1
2358             continue
2359         if hspace.search(document.body[i]):
2360             subst = document.body[i].replace('\\InsetSpace \\hspace{}', \
2361               '\\begin_inset ERT\nstatus collapsed\n\n' \
2362               '\\begin_layout Standard\n\n\n\\backslash\n' \
2363               'hspace{' + length + '}\n\\end_layout\n\n\\end_inset\n\n')
2364             subst = subst.split('\n')
2365             document.body[i : i+1] = subst
2366             addedLines += len(subst) - 1
2367             i += addedLines + 1
2368             continue
2369         i += 1
2370
2371
2372 def revert_protected_hfill(document):
2373     ' Revert \\begin_inset Space \\hspace*{\\fill} to ERT '
2374     i = 0
2375     while True:
2376         i = find_token(document.body, '\\begin_inset Space \\hspace*{\\fill}', i)
2377         if i == -1:
2378             return
2379         j = find_end_of_inset(document.body, i)
2380         if j == -1:
2381             document.warning("Malformed LyX document: Could not find end of space inset.")
2382             continue
2383         del document.body[j]
2384         subst = document.body[i].replace('\\begin_inset Space \\hspace*{\\fill}', \
2385           '\\begin_inset ERT\nstatus collapsed\n\n' \
2386           '\\begin_layout Standard\n\n\n\\backslash\n' \
2387           'hspace*{\n\\backslash\nfill}\n\\end_layout\n\n\\end_inset\n\n')
2388         subst = subst.split('\n')
2389         document.body[i : i+1] = subst
2390         i += len(subst)
2391
2392
2393 def revert_leftarrowfill(document):
2394     ' Revert \\begin_inset Space \\leftarrowfill{} to ERT '
2395     i = 0
2396     while True:
2397         i = find_token(document.body, '\\begin_inset Space \\leftarrowfill{}', i)
2398         if i == -1:
2399             return
2400         j = find_end_of_inset(document.body, i)
2401         if j == -1:
2402             document.warning("Malformed LyX document: Could not find end of space inset.")
2403             continue
2404         del document.body[j]
2405         subst = document.body[i].replace('\\begin_inset Space \\leftarrowfill{}', \
2406           '\\begin_inset ERT\nstatus collapsed\n\n' \
2407           '\\begin_layout Standard\n\n\n\\backslash\n' \
2408           'leftarrowfill{}\n\\end_layout\n\n\\end_inset\n\n')
2409         subst = subst.split('\n')
2410         document.body[i : i+1] = subst
2411         i += len(subst)
2412
2413
2414 def revert_rightarrowfill(document):
2415     ' Revert \\begin_inset Space \\rightarrowfill{} to ERT '
2416     i = 0
2417     while True:
2418         i = find_token(document.body, '\\begin_inset Space \\rightarrowfill{}', i)
2419         if i == -1:
2420             return
2421         j = find_end_of_inset(document.body, i)
2422         if j == -1:
2423             document.warning("Malformed LyX document: Could not find end of space inset.")
2424             continue
2425         del document.body[j]
2426         subst = document.body[i].replace('\\begin_inset Space \\rightarrowfill{}', \
2427           '\\begin_inset ERT\nstatus collapsed\n\n' \
2428           '\\begin_layout Standard\n\n\n\\backslash\n' \
2429           'rightarrowfill{}\n\\end_layout\n\n\\end_inset\n\n')
2430         subst = subst.split('\n')
2431         document.body[i : i+1] = subst
2432         i += len(subst)
2433
2434
2435 def revert_upbracefill(document):
2436     ' Revert \\begin_inset Space \\upbracefill{} to ERT '
2437     i = 0
2438     while True:
2439         i = find_token(document.body, '\\begin_inset Space \\upbracefill{}', i)
2440         if i == -1:
2441             return
2442         j = find_end_of_inset(document.body, i)
2443         if j == -1:
2444             document.warning("Malformed LyX document: Could not find end of space inset.")
2445             continue
2446         del document.body[j]
2447         subst = document.body[i].replace('\\begin_inset Space \\upbracefill{}', \
2448           '\\begin_inset ERT\nstatus collapsed\n\n' \
2449           '\\begin_layout Standard\n\n\n\\backslash\n' \
2450           'upbracefill{}\n\\end_layout\n\n\\end_inset\n\n')
2451         subst = subst.split('\n')
2452         document.body[i : i+1] = subst
2453         i += len(subst)
2454
2455
2456 def revert_downbracefill(document):
2457     ' Revert \\begin_inset Space \\downbracefill{} to ERT '
2458     i = 0
2459     while True:
2460         i = find_token(document.body, '\\begin_inset Space \\downbracefill{}', i)
2461         if i == -1:
2462             return
2463         j = find_end_of_inset(document.body, i)
2464         if j == -1:
2465             document.warning("Malformed LyX document: Could not find end of space inset.")
2466             continue
2467         del document.body[j]
2468         subst = document.body[i].replace('\\begin_inset Space \\downbracefill{}', \
2469           '\\begin_inset ERT\nstatus collapsed\n\n' \
2470           '\\begin_layout Standard\n\n\n\\backslash\n' \
2471           'downbracefill{}\n\\end_layout\n\n\\end_inset\n\n')
2472         subst = subst.split('\n')
2473         document.body[i : i+1] = subst
2474         i += len(subst)
2475
2476
2477 def revert_local_layout(document):
2478     ' Revert local layout headers.'
2479     i = 0
2480     while True:
2481         i = find_token(document.header, "\\begin_local_layout", i)
2482         if i == -1:
2483             return
2484         j = find_end_of(document.header, i, "\\begin_local_layout", "\\end_local_layout")
2485         if j == -1:
2486             # this should not happen
2487             break
2488         document.header[i : j + 1] = []
2489
2490
2491 def convert_pagebreaks(document):
2492     ' Convert inline Newpage insets to new format '
2493     i = 0
2494     while True:
2495         i = find_token(document.body, '\\newpage', i)
2496         if i == -1:
2497             break
2498         document.body[i:i+1] = ['\\begin_inset Newpage newpage',
2499                                 '\\end_inset']
2500     i = 0
2501     while True:
2502         i = find_token(document.body, '\\pagebreak', i)
2503         if i == -1:
2504             break
2505         document.body[i:i+1] = ['\\begin_inset Newpage pagebreak',
2506                                 '\\end_inset']
2507     i = 0
2508     while True:
2509         i = find_token(document.body, '\\clearpage', i)
2510         if i == -1:
2511             break
2512         document.body[i:i+1] = ['\\begin_inset Newpage clearpage',
2513                                 '\\end_inset']
2514     i = 0
2515     while True:
2516         i = find_token(document.body, '\\cleardoublepage', i)
2517         if i == -1:
2518             break
2519         document.body[i:i+1] = ['\\begin_inset Newpage cleardoublepage',
2520                                 '\\end_inset']
2521
2522
2523 def revert_pagebreaks(document):
2524     ' Revert \\begin_inset Newpage to previous inline format '
2525     i = 0
2526     while True:
2527         i = find_token(document.body, '\\begin_inset Newpage', i)
2528         if i == -1:
2529             return
2530         j = find_end_of_inset(document.body, i)
2531         if j == -1:
2532             document.warning("Malformed LyX document: Could not find end of Newpage inset.")
2533             continue
2534         del document.body[j]
2535         document.body[i] = document.body[i].replace('\\begin_inset Newpage newpage', '\\newpage')
2536         document.body[i] = document.body[i].replace('\\begin_inset Newpage pagebreak', '\\pagebreak')
2537         document.body[i] = document.body[i].replace('\\begin_inset Newpage clearpage', '\\clearpage')
2538         document.body[i] = document.body[i].replace('\\begin_inset Newpage cleardoublepage', '\\cleardoublepage')
2539
2540
2541 def convert_linebreaks(document):
2542     ' Convert inline Newline insets to new format '
2543     i = 0
2544     while True:
2545         i = find_token(document.body, '\\newline', i)
2546         if i == -1:
2547             break
2548         document.body[i:i+1] = ['\\begin_inset Newline newline',
2549                                 '\\end_inset']
2550     i = 0
2551     while True:
2552         i = find_token(document.body, '\\linebreak', i)
2553         if i == -1:
2554             break
2555         document.body[i:i+1] = ['\\begin_inset Newline linebreak',
2556                                 '\\end_inset']
2557
2558
2559 def revert_linebreaks(document):
2560     ' Revert \\begin_inset Newline to previous inline format '
2561     i = 0
2562     while True:
2563         i = find_token(document.body, '\\begin_inset Newline', i)
2564         if i == -1:
2565             return
2566         j = find_end_of_inset(document.body, i)
2567         if j == -1:
2568             document.warning("Malformed LyX document: Could not find end of Newline inset.")
2569             continue
2570         del document.body[j]
2571         document.body[i] = document.body[i].replace('\\begin_inset Newline newline', '\\newline')
2572         document.body[i] = document.body[i].replace('\\begin_inset Newline linebreak', '\\linebreak')
2573
2574
2575 def convert_japanese_plain(document):
2576     ' Set language japanese-plain to japanese '
2577     i = 0
2578     if document.language == "japanese-plain":
2579         document.language = "japanese"
2580         i = find_token(document.header, "\\language", 0)
2581         if i != -1:
2582             document.header[i] = "\\language japanese"
2583     j = 0
2584     while True:
2585         j = find_token(document.body, "\\lang japanese-plain", j)
2586         if j == -1:
2587             return
2588         document.body[j] = document.body[j].replace("\\lang japanese-plain", "\\lang japanese")
2589         j = j + 1
2590
2591
2592 def revert_pdfpages(document):
2593     ' Revert pdfpages external inset to ERT '
2594     i = 0
2595     while 1:
2596         i = find_token(document.body, "\\begin_inset External", i)
2597         if i == -1:
2598             return
2599         j = find_end_of_inset(document.body, i)
2600         if j == -1:
2601             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_pdfpages.")
2602             i = i + 1
2603             continue
2604         if get_value(document.body, 'template', i, j) == "PDFPages":
2605             filename = get_value(document.body, 'filename', i, j)
2606             extra = ''
2607             r = re.compile(r'\textra PDFLaTeX \"(.*)\"$')
2608             for k in range(i, j):
2609                 m = r.match(document.body[k])
2610                 if m:
2611                     extra = m.group(1)
2612             angle = get_value(document.body, 'rotateAngle', i, j)
2613             width = get_value(document.body, 'width', i, j)
2614             height = get_value(document.body, 'height', i, j)
2615             scale = get_value(document.body, 'scale', i, j)
2616             keepAspectRatio = find_token(document.body, "\tkeepAspectRatio", i, j)
2617             options = extra
2618             if angle != '':
2619                  if options != '':
2620                      options += ",angle=" + angle
2621                  else:
2622                      options += "angle=" + angle
2623             if width != '':
2624                  if options != '':
2625                      options += ",width=" + convert_len(width)
2626                  else:
2627                      options += "width=" + convert_len(width)
2628             if height != '':
2629                  if options != '':
2630                      options += ",height=" + convert_len(height)
2631                  else:
2632                      options += "height=" + convert_len(height)
2633             if scale != '':
2634                  if options != '':
2635                      options += ",scale=" + scale
2636                  else:
2637                      options += "scale=" + scale
2638             if keepAspectRatio != '':
2639                  if options != '':
2640                      options += ",keepaspectratio"
2641                  else:
2642                      options += "keepaspectratio"
2643             if options != '':
2644                      options = '[' + options + ']'
2645             del document.body[i+1:j+1]
2646             document.body[i:i+1] = ['\\begin_inset ERT',
2647                                 'status collapsed',
2648                                 '',
2649                                 '\\begin_layout Standard',
2650                                 '',
2651                                 '\\backslash',
2652                                 'includepdf' + options + '{' + filename + '}',
2653                                 '\\end_layout',
2654                                 '',
2655                                 '\\end_inset']
2656             add_to_preamble(document, ['\\usepackage{pdfpages}\n'])
2657             i = i + 1
2658             continue
2659         i = i + 1
2660
2661
2662 def revert_mexican(document):
2663     ' Set language Spanish(Mexico) to Spanish '
2664     i = 0
2665     if document.language == "spanish-mexico":
2666         document.language = "spanish"
2667         i = find_token(document.header, "\\language", 0)
2668         if i != -1:
2669             document.header[i] = "\\language spanish"
2670     j = 0
2671     while True:
2672         j = find_token(document.body, "\\lang spanish-mexico", j)
2673         if j == -1:
2674             return
2675         document.body[j] = document.body[j].replace("\\lang spanish-mexico", "\\lang spanish")
2676         j = j + 1
2677
2678
2679 def remove_embedding(document):
2680     ' Remove embed tag from all insets '
2681     revert_inset_embedding(document, 'Graphics')
2682     revert_inset_embedding(document, 'External')
2683     revert_inset_embedding(document, 'CommandInset include')
2684     revert_inset_embedding(document, 'CommandInset bibtex')
2685
2686
2687 def revert_master(document):
2688     ' Remove master param '
2689     i = find_token(document.header, "\\master", 0)
2690     if i != -1:
2691         del document.header[i]
2692
2693
2694 def revert_graphics_group(document):
2695     ' Revert group information from graphics insets '
2696     i = 0
2697     while 1:
2698         i = find_token(document.body, "\\begin_inset Graphics", i)
2699         if i == -1:
2700             return
2701         j = find_end_of_inset(document.body, i)
2702         if j == -1:
2703             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_graphics_group.")
2704             i = i + 1
2705             continue
2706         k = find_token(document.body, " groupId", i, j)
2707         if k == -1:
2708             i = i + 1
2709             continue
2710         del document.body[k]
2711         i = i + 1
2712
2713
2714 def update_apa_styles(document):
2715     ' Replace obsolete styles '
2716
2717     if document.textclass != "apa":
2718         return
2719
2720     obsoletedby = { "Acknowledgments": "Acknowledgements",
2721                     "Section*":        "Section",
2722                     "Subsection*":     "Subsection",
2723                     "Subsubsection*":  "Subsubsection",
2724                     "Paragraph*":      "Paragraph",
2725                     "Subparagraph*":   "Subparagraph"}
2726     i = 0
2727     while 1:
2728         i = find_token(document.body, "\\begin_layout", i)
2729         if i == -1:
2730             return
2731
2732         layout = document.body[i][14:]
2733         if layout in obsoletedby:
2734             document.body[i] = "\\begin_layout " + obsoletedby[layout]
2735
2736         i += 1
2737
2738
2739 def convert_paper_sizes(document):
2740     ' exchange size options legalpaper and executivepaper to correct order '
2741     # routine is needed to fix http://bugzilla.lyx.org/show_bug.cgi?id=4868
2742     i = 0
2743     j = 0
2744     i = find_token(document.header, "\\papersize executivepaper", 0)
2745     if i != -1:
2746         document.header[i] = "\\papersize legalpaper"
2747         return
2748     j = find_token(document.header, "\\papersize legalpaper", 0)
2749     if j != -1:
2750         document.header[j] = "\\papersize executivepaper"
2751
2752
2753 def revert_paper_sizes(document):
2754     ' exchange size options legalpaper and executivepaper to correct order '
2755     i = 0
2756     j = 0
2757     i = find_token(document.header, "\\papersize executivepaper", 0)
2758     if i != -1:
2759         document.header[i] = "\\papersize legalpaper"
2760         return
2761     j = find_token(document.header, "\\papersize legalpaper", 0)
2762     if j != -1:
2763         document.header[j] = "\\papersize executivepaper"
2764
2765
2766 def convert_InsetSpace(document):
2767     " Convert '\\begin_inset Space foo' to '\\begin_inset space foo'"
2768     i = 0
2769     while True:
2770         i = find_token(document.body, "\\begin_inset Space", i)
2771         if i == -1:
2772             return
2773         document.body[i] = document.body[i].replace('\\begin_inset Space', '\\begin_inset space')
2774
2775
2776 def revert_InsetSpace(document):
2777     " Revert '\\begin_inset space foo' to '\\begin_inset Space foo'"
2778     i = 0
2779     while True:
2780         i = find_token(document.body, "\\begin_inset space", i)
2781         if i == -1:
2782             return
2783         document.body[i] = document.body[i].replace('\\begin_inset space', '\\begin_inset Space')
2784
2785
2786 def convert_display_enum(document):
2787     " Convert 'display foo' to 'display false/true'"
2788     i = 0
2789     while True:
2790         i = find_token(document.body, "\tdisplay", i)
2791         if i == -1:
2792             return
2793         val = get_value(document.body, 'display', i)
2794         if val == "none":
2795             document.body[i] = document.body[i].replace('none', 'false')
2796         if val == "default":
2797             document.body[i] = document.body[i].replace('default', 'true')
2798         if val == "monochrome":
2799             document.body[i] = document.body[i].replace('monochrome', 'true')
2800         if val == "grayscale":
2801             document.body[i] = document.body[i].replace('grayscale', 'true')
2802         if val == "color":
2803             document.body[i] = document.body[i].replace('color', 'true')
2804         if val == "preview":
2805             document.body[i] = document.body[i].replace('preview', 'true')
2806         i += 1
2807
2808
2809 def revert_display_enum(document):
2810     " Revert 'display false/true' to 'display none/color'"
2811     i = 0
2812     while True:
2813         i = find_token(document.body, "\tdisplay", i)
2814         if i == -1:
2815             return
2816         val = get_value(document.body, 'display', i)
2817         if val == "false":
2818             document.body[i] = document.body[i].replace('false', 'none')
2819         if val == "true":
2820             document.body[i] = document.body[i].replace('true', 'default')
2821         i += 1
2822
2823
2824 def remove_fontsCJK(document):
2825     ' Remove font_cjk param '
2826     i = find_token(document.header, "\\font_cjk", 0)
2827     if i != -1:
2828         del document.header[i]
2829
2830
2831 def convert_plain_layout(document):
2832     " Convert 'PlainLayout' to 'Plain Layout'"
2833     i = 0
2834     while True:
2835         i = find_token(document.body, '\\begin_layout PlainLayout', i)
2836         if i == -1:
2837             return
2838         document.body[i] = document.body[i].replace('\\begin_layout PlainLayout', \
2839           '\\begin_layout Plain Layout')
2840         i += 1
2841
2842
2843 def revert_plain_layout(document):
2844     " Convert 'PlainLayout' to 'Plain Layout'"
2845     i = 0
2846     while True:
2847         i = find_token(document.body, '\\begin_layout Plain Layout', i)
2848         if i == -1:
2849             return
2850         document.body[i] = document.body[i].replace('\\begin_layout Plain Layout', \
2851           '\\begin_layout PlainLayout')
2852         i += 1
2853
2854
2855 def revert_plainlayout(document):
2856     " Convert 'PlainLayout' to 'Plain Layout'"
2857     i = 0
2858     while True:
2859         i = find_token(document.body, '\\begin_layout PlainLayout', i)
2860         if i == -1:
2861             return
2862         # This will be incorrect for some document classes, since Standard is not always
2863         # the default. But (a) it is probably the best we can do and (b) it will actually
2864         # work, in fact, since an unknown layout will be converted to default.
2865         document.body[i] = document.body[i].replace('\\begin_layout PlainLayout', \
2866           '\\begin_layout Standard')
2867         i += 1
2868
2869
2870 def revert_polytonicgreek(document):
2871     "Set language polytonic Greek to Greek"
2872     i = 0
2873     if document.language == "polutonikogreek":
2874         document.language = "greek"
2875         i = find_token(document.header, "\\language", 0)
2876         if i != -1:
2877             document.header[i] = "\\language greek"
2878     j = 0
2879     while True:
2880         j = find_token(document.body, "\\lang polutonikogreek", j)
2881         if j == -1:
2882             return
2883         document.body[j] = document.body[j].replace("\\lang polutonikogreek", "\\lang greek")
2884         j = j + 1
2885
2886
2887 def revert_removed_modules(document):
2888     i = 0
2889     while True:
2890         i = find_token(document.header, "\\begin_remove_modules", i)
2891         if i == -1:
2892             return
2893         j = find_end_of(document.header, i, "\\begin_remove_modules", "\\end_remove_modules")
2894         if j == -1:
2895             # this should not happen
2896             break
2897         document.header[i : j + 1] = []
2898
2899
2900 def add_plain_layout(document):
2901     i = 0
2902     while True:
2903         i = find_token(document.body, "\\begin_layout", i)
2904         if i == -1:
2905             return
2906         if len(document.body[i].split()) == 1:
2907             document.body[i] = "\\begin_layout Plain Layout"
2908         i += 1
2909
2910
2911 def revert_tabulators(document):
2912     "Revert tabulators to 4 spaces"
2913     i = 0
2914     while True:
2915         i = find_token(document.body, "\t", i)
2916         if i == -1:
2917             return
2918         document.body[i] = document.body[i].replace("\t", "    ")
2919         i += 1
2920
2921
2922 def revert_tabsize(document):
2923     "Revert the tabsize parameter of listings"
2924     i = 0
2925     j = 0
2926     while True:
2927         # either it is the only parameter
2928         i = find_token(document.body, 'lstparams "tabsize=4"', i)
2929         if i != -1:
2930             del document.body[i]
2931         # or the last one
2932         j = find_token(document.body, "lstparams", j)
2933         if j == -1:
2934             return
2935         pos = document.body[j].find(",tabsize=")
2936         document.body[j] = document.body[j][:pos] + '"'
2937         i += 1
2938         j += 1
2939
2940
2941 def revert_mongolian(document):
2942     "Set language Mongolian to English"
2943     i = 0
2944     if document.language == "mongolian":
2945         document.language = "english"
2946         i = find_token(document.header, "\\language", 0)
2947         if i != -1:
2948             document.header[i] = "\\language english"
2949     j = 0
2950     while True:
2951         j = find_token(document.body, "\\lang mongolian", j)
2952         if j == -1:
2953             return
2954         document.body[j] = document.body[j].replace("\\lang mongolian", "\\lang english")
2955         j = j + 1
2956
2957
2958 ##
2959 # Conversion hub
2960 #
2961
2962 supported_versions = ["1.6.0","1.6"]
2963 convert = [[277, [fix_wrong_tables]],
2964            [278, [close_begin_deeper]],
2965            [279, [long_charstyle_names]],
2966            [280, [axe_show_label]],
2967            [281, []],
2968            [282, []],
2969            [283, [convert_flex]],
2970            [284, []],
2971            [285, []],
2972            [286, []],
2973            [287, [convert_wrapfig_options]],
2974            [288, [convert_inset_command]],
2975            [289, [convert_latexcommand_index]],
2976            [290, []],
2977            [291, []],
2978            [292, [convert_japanese_cjk]],
2979            [293, []],
2980            [294, [convert_pdf_options]],
2981            [295, [convert_htmlurl, convert_url]],
2982            [296, [convert_include]],
2983            [297, [convert_usorbian]],
2984            [298, []],
2985            [299, []],
2986            [300, []],
2987            [301, []],
2988            [302, []],
2989            [303, [convert_serbocroatian]],
2990            [304, [convert_framed_notes]],
2991            [305, []],
2992            [306, []],
2993            [307, []],
2994            [308, []],
2995            [309, []],
2996            [310, []],
2997            [311, [convert_ams_classes]],
2998            [312, []],
2999            [313, [convert_module_names]],
3000            [314, []],
3001            [315, []],
3002            [316, [convert_subfig]],
3003            [317, []],
3004            [318, []],
3005            [319, [convert_spaceinset, convert_hfill]],
3006            [320, []],
3007            [321, [convert_tablines]],
3008            [322, [convert_plain_layout]],
3009            [323, [convert_pagebreaks]],
3010            [324, [convert_linebreaks]],
3011            [325, [convert_japanese_plain]],
3012            [326, []],
3013            [327, []],
3014            [328, [remove_embedding, remove_extra_embedded_files, remove_inzip_options]],
3015            [329, []],
3016            [330, []],
3017            [331, [convert_ltcaption]],
3018            [332, []],
3019            [333, [update_apa_styles]],
3020            [334, [convert_paper_sizes]],
3021            [335, [convert_InsetSpace]],
3022            [336, []],
3023            [337, [convert_display_enum]],
3024            [338, []],
3025            [339, []],
3026            [340, [add_plain_layout]],
3027            [341, []],
3028            [342, []]
3029           ]
3030
3031 revert =  [[341, [revert_mongolian]],
3032            [340, [revert_tabulators, revert_tabsize]],
3033            [339, []],
3034            [338, [revert_removed_modules]],
3035            [337, [revert_polytonicgreek]],
3036            [336, [revert_display_enum]],
3037            [335, [remove_fontsCJK]],
3038            [334, [revert_InsetSpace]],
3039            [333, [revert_paper_sizes]],
3040            [332, []],
3041            [331, [revert_graphics_group]],
3042            [330, [revert_ltcaption]],
3043            [329, [revert_leftarrowfill, revert_rightarrowfill, revert_upbracefill, revert_downbracefill]],
3044            [328, [revert_master]],
3045            [327, []],
3046            [326, [revert_mexican]],
3047            [325, [revert_pdfpages]],
3048            [324, []],
3049            [323, [revert_linebreaks]],
3050            [322, [revert_pagebreaks]],
3051            [321, [revert_local_layout, revert_plain_layout]],
3052            [320, [revert_tablines]],
3053            [319, [revert_protected_hfill]],
3054            [318, [revert_spaceinset, revert_hfills, revert_hspace]],
3055            [317, [remove_extra_embedded_files]],
3056            [316, [revert_wrapplacement]],
3057            [315, [revert_subfig]],
3058            [314, [revert_colsep, revert_plainlayout]],
3059            [313, []],
3060            [312, [revert_module_names]],
3061            [311, [revert_rotfloat, revert_widesideways]],
3062            [310, [revert_external_embedding]],
3063            [309, [revert_btprintall]],
3064            [308, [revert_nocite]],
3065            [307, [revert_serbianlatin]],
3066            [306, [revert_slash, revert_nobreakdash]],
3067            [305, [revert_interlingua]],
3068            [304, [revert_bahasam]],
3069            [303, [revert_framed_notes]],
3070            [302, []],
3071            [301, [revert_latin, revert_samin]],
3072            [300, [revert_linebreak]],
3073            [299, [revert_pagebreak]],
3074            [298, [revert_hyperlinktype]],
3075            [297, [revert_macro_optional_params]],
3076            [296, [revert_albanian, revert_lowersorbian, revert_uppersorbian]],
3077            [295, [revert_include]],
3078            [294, [revert_href, revert_url]],
3079            [293, [revert_pdf_options_2]],
3080            [292, [revert_inset_info]],
3081            [291, [revert_japanese, revert_japanese_encoding, revert_japanese_cjk]],
3082            [290, [revert_vietnamese]],
3083            [289, [revert_wraptable]],
3084            [288, [revert_latexcommand_index]],
3085            [287, [revert_inset_command]],
3086            [286, [revert_wrapfig_options]],
3087            [285, [revert_pdf_options]],
3088            [284, [remove_inzip_options]],
3089            [283, []],
3090            [282, [revert_flex]],
3091            [281, []],
3092            [280, [revert_begin_modules]],
3093            [279, [revert_show_label]],
3094            [278, [revert_long_charstyle_names]],
3095            [277, []],
3096            [276, []]
3097           ]
3098
3099
3100 if __name__ == "__main__":
3101     pass