]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_1_6.py
Support for Mongolian, fileformat change
[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             i += 1
1075             continue
1076         fullcontent = m.group(1)
1077         linelist = latex2lyx(fullcontent)
1078         #document.warning(fullcontent)
1079
1080         linelist = ["\\begin_inset Index", "status collapsed", "\\begin_layout Standard", ""] + \
1081                    linelist + ["\\end_layout"]
1082         document.body[i : j] = linelist
1083         i += len(linelist) - (j - i)
1084
1085
1086 def revert_latexcommand_index(document):
1087     "Revert from collapsable form to LatexCommand form."
1088     i = 0
1089     while True:
1090         i = find_token(document.body, "\\begin_inset Index", i)
1091         if i == -1:
1092           return
1093         j = find_end_of_inset(document.body, i + 1)
1094         if j == -1:
1095           return
1096
1097         content = lyx2latex(document, document.body[i:j])
1098         # escape quotes
1099         content = content.replace('"', r'\"')
1100         document.body[i:j] = ["\\begin_inset CommandInset index", "LatexCommand index",
1101             "name " + '"' + content + '"', ""]
1102         i += 5
1103
1104
1105 def revert_wraptable(document):
1106     "Revert wrap table to wrap figure."
1107     i = 0
1108     while True:
1109         i = find_token(document.body, "\\begin_inset Wrap table", i)
1110         if i == -1:
1111             return
1112         document.body[i] = document.body[i].replace('\\begin_inset Wrap table', '\\begin_inset Wrap figure')
1113         i = i + 1
1114
1115
1116 def revert_vietnamese(document):
1117     "Set language Vietnamese to English"
1118     # Set document language from Vietnamese to English
1119     i = 0
1120     if document.language == "vietnamese":
1121         document.language = "english"
1122         i = find_token(document.header, "\\language", 0)
1123         if i != -1:
1124             document.header[i] = "\\language english"
1125     j = 0
1126     while True:
1127         j = find_token(document.body, "\\lang vietnamese", j)
1128         if j == -1:
1129             return
1130         document.body[j] = document.body[j].replace("\\lang vietnamese", "\\lang english")
1131         j = j + 1
1132
1133
1134 def convert_japanese_cjk(document):
1135     "Set language japanese to japanese-cjk"
1136     # Set document language from japanese-plain to japanese
1137     i = 0
1138     if document.language == "japanese":
1139         document.language = "japanese-cjk"
1140         i = find_token(document.header, "\\language", 0)
1141         if i != -1:
1142             document.header[i] = "\\language japanese-cjk"
1143     j = 0
1144     while True:
1145         j = find_token(document.body, "\\lang japanese", j)
1146         if j == -1:
1147             return
1148         document.body[j] = document.body[j].replace("\\lang japanese", "\\lang japanese-cjk")
1149         j = j + 1
1150
1151
1152 def revert_japanese(document):
1153     "Set language japanese-plain to japanese"
1154     # Set document language from japanese-plain to japanese
1155     i = 0
1156     if document.language == "japanese-plain":
1157         document.language = "japanese"
1158         i = find_token(document.header, "\\language", 0)
1159         if i != -1:
1160             document.header[i] = "\\language japanese"
1161     j = 0
1162     while True:
1163         j = find_token(document.body, "\\lang japanese-plain", j)
1164         if j == -1:
1165             return
1166         document.body[j] = document.body[j].replace("\\lang japanese-plain", "\\lang japanese")
1167         j = j + 1
1168
1169
1170 def revert_japanese_cjk(document):
1171     "Set language japanese-cjk to japanese"
1172     # Set document language from japanese-plain to japanese
1173     i = 0
1174     if document.language == "japanese-cjk":
1175         document.language = "japanese"
1176         i = find_token(document.header, "\\language", 0)
1177         if i != -1:
1178             document.header[i] = "\\language japanese"
1179     j = 0
1180     while True:
1181         j = find_token(document.body, "\\lang japanese-cjk", j)
1182         if j == -1:
1183             return
1184         document.body[j] = document.body[j].replace("\\lang japanese-cjk", "\\lang japanese")
1185         j = j + 1
1186
1187
1188 def revert_japanese_encoding(document):
1189     "Set input encoding form EUC-JP-plain to EUC-JP etc."
1190     # Set input encoding form EUC-JP-plain to EUC-JP etc.
1191     i = 0
1192     i = find_token(document.header, "\\inputencoding EUC-JP-plain", 0)
1193     if i != -1:
1194         document.header[i] = "\\inputencoding EUC-JP"
1195     j = 0
1196     j = find_token(document.header, "\\inputencoding JIS-plain", 0)
1197     if j != -1:
1198         document.header[j] = "\\inputencoding JIS"
1199     k = 0
1200     k = find_token(document.header, "\\inputencoding SJIS-plain", 0)
1201     if k != -1: # convert to UTF8 since there is currently no SJIS encoding
1202         document.header[k] = "\\inputencoding UTF8"
1203
1204
1205 def revert_inset_info(document):
1206     'Replace info inset with its content'
1207     i = 0
1208     while 1:
1209         i = find_token(document.body, '\\begin_inset Info', i)
1210         if i == -1:
1211             return
1212         j = find_end_of_inset(document.body, i + 1)
1213         if j == -1:
1214             # should not happen
1215             document.warning("Malformed LyX document: Could not find end of Info inset.")
1216         type = 'unknown'
1217         arg = ''
1218         for k in range(i, j+1):
1219             if document.body[k].startswith("arg"):
1220                 arg = document.body[k][3:].strip().strip('"')
1221             if document.body[k].startswith("type"):
1222                 type = document.body[k][4:].strip().strip('"')
1223         # I think there is a newline after \\end_inset, which should be removed.
1224         if document.body[j + 1].strip() == "":
1225             document.body[i : (j + 2)] = [type + ':' + arg]
1226         else:
1227             document.body[i : (j + 1)] = [type + ':' + arg]
1228
1229
1230 def convert_pdf_options(document):
1231     # Set the pdfusetitle tag, delete the pdf_store_options,
1232     # set quotes for bookmarksopenlevel"
1233     has_hr = get_value(document.header, "\\use_hyperref", 0, default = "0")
1234     if has_hr == "1":
1235         k = find_token(document.header, "\\use_hyperref", 0)
1236         document.header.insert(k + 1, "\\pdf_pdfusetitle true")
1237     k = find_token(document.header, "\\pdf_store_options", 0)
1238     if k != -1:
1239         del document.header[k]
1240     i = find_token(document.header, "\\pdf_bookmarksopenlevel", k)
1241     if i == -1: return
1242     document.header[i] = document.header[i].replace('"', '')
1243
1244
1245 def revert_pdf_options_2(document):
1246     # reset the pdfusetitle tag, set quotes for bookmarksopenlevel"
1247     k = find_token(document.header, "\\use_hyperref", 0)
1248     i = find_token(document.header, "\\pdf_pdfusetitle", k)
1249     if i != -1:
1250         del document.header[i]
1251     i = find_token(document.header, "\\pdf_bookmarksopenlevel", k)
1252     if i == -1: return
1253     values = document.header[i].split()
1254     values[1] = ' "' + values[1] + '"'
1255     document.header[i] = ''.join(values)
1256
1257
1258 def convert_htmlurl(document):
1259     'Convert "htmlurl" to "href" insets for docbook'
1260     if document.backend != "docbook":
1261       return
1262     i = 0
1263     while True:
1264       i = find_token(document.body, "\\begin_inset CommandInset url", i)
1265       if i == -1:
1266         return
1267       document.body[i] = "\\begin_inset CommandInset href"
1268       document.body[i + 1] = "LatexCommand href"
1269       i = i + 1
1270
1271
1272 def convert_url(document):
1273     'Convert url insets to url charstyles'
1274     if document.backend == "docbook":
1275       return
1276     i = 0
1277     while True:
1278       i = find_token(document.body, "\\begin_inset CommandInset url", i)
1279       if i == -1:
1280         break
1281       n = find_token(document.body, "name", i)
1282       if n == i + 2:
1283         # place the URL name in typewriter before the new URL insert
1284         # grab the name 'bla' from the e.g. the line 'name "bla"',
1285         # therefore start with the 6th character
1286         name = document.body[n][6:-1]
1287         newname = [name + " "]
1288         document.body[i:i] = newname
1289         i = i + 1
1290       j = find_token(document.body, "target", i)
1291       if j == -1:
1292         document.warning("Malformed LyX document: Can't find target for url inset")
1293         i = j
1294         continue
1295       target = document.body[j][8:-1]
1296       k = find_token(document.body, "\\end_inset", j)
1297       if k == -1:
1298         document.warning("Malformed LyX document: Can't find end of url inset")
1299         i = k
1300         continue
1301       newstuff = ["\\begin_inset Flex URL",
1302         "status collapsed", "",
1303         "\\begin_layout Standard",
1304         "",
1305         target,
1306         "\\end_layout",
1307         ""]
1308       document.body[i:k] = newstuff
1309       i = k
1310
1311 def convert_ams_classes(document):
1312   tc = document.textclass
1313   if (tc != "amsart" and tc != "amsart-plain" and
1314       tc != "amsart-seq" and tc != "amsbook"):
1315     return
1316   if tc == "amsart-plain":
1317     document.textclass = "amsart"
1318     document.set_textclass()
1319     document.add_module("Theorems (Starred)")
1320     return
1321   if tc == "amsart-seq":
1322     document.textclass = "amsart"
1323     document.set_textclass()
1324   document.add_module("Theorems (AMS)")
1325
1326   #Now we want to see if any of the environments in the extended theorems
1327   #module were used in this document. If so, we'll add that module, too.
1328   layouts = ["Criterion", "Algorithm", "Axiom", "Condition", "Note",  \
1329     "Notation", "Summary", "Acknowledgement", "Conclusion", "Fact", \
1330     "Assumption"]
1331
1332   r = re.compile(r'^\\begin_layout (.*?)\*?\s*$')
1333   i = 0
1334   while True:
1335     i = find_token(document.body, "\\begin_layout", i)
1336     if i == -1:
1337       return
1338     m = r.match(document.body[i])
1339     if m == None:
1340       # This is an empty layout
1341       # document.warning("Weirdly formed \\begin_layout at line %d of body!" % i)
1342       i += 1
1343       continue
1344     m = m.group(1)
1345     if layouts.count(m) != 0:
1346       document.add_module("Theorems (AMS-Extended)")
1347       return
1348     i += 1
1349
1350 def revert_href(document):
1351     'Reverts hyperlink insets (href) to url insets (url)'
1352     i = 0
1353     while True:
1354       i = find_token(document.body, "\\begin_inset CommandInset href", i)
1355       if i == -1:
1356           return
1357       document.body[i : i + 2] = \
1358         ["\\begin_inset CommandInset url", "LatexCommand url"]
1359       i = i + 2
1360
1361 def revert_url(document):
1362     'Reverts Flex URL insets to old-style URL insets'
1363     i = 0
1364     while True:
1365         i = find_token(document.body, "\\begin_inset Flex URL", i)
1366         if i == -1:
1367             return
1368         j = find_end_of_inset(document.body, i)
1369         if j == -1:
1370             document.warning("Can't find end of inset in revert_url!")
1371             return
1372         k = find_default_layout(document, i, j)
1373         if k == -1:
1374             document.warning("Can't find default layout in revert_url!")
1375             i = j
1376             continue
1377         l = find_end_of(document.body, k, "\\begin_layout", "\\end_layout")
1378         if l == -1 or l >= j:
1379             document.warning("Can't find end of default layout in revert_url!")
1380             i = j
1381             continue
1382         # OK, so the inset's data is between lines k and l.
1383         data =  " ".join(document.body[k+1:l])
1384         data = data.strip()
1385         newinset = ["\\begin_inset LatexCommand url", "target \"" + data + "\"",\
1386                     "", "\\end_inset"]
1387         document.body[i:j+1] = newinset
1388         i = i + len(newinset)
1389
1390
1391 def convert_include(document):
1392   'Converts include insets to new format.'
1393   i = 0
1394   r = re.compile(r'\\begin_inset Include\s+\\([^{]+){([^}]*)}(?:\[(.*)\])?')
1395   while True:
1396     i = find_token(document.body, "\\begin_inset Include", i)
1397     if i == -1:
1398       return
1399     line = document.body[i]
1400     previewline = document.body[i + 1]
1401     m = r.match(line)
1402     if m == None:
1403       document.warning("Unable to match line " + str(i) + " of body!")
1404       i += 1
1405       continue
1406     cmd = m.group(1)
1407     fn  = m.group(2)
1408     opt = m.group(3)
1409     insertion = ["\\begin_inset CommandInset include",
1410        "LatexCommand " + cmd, previewline,
1411        "filename \"" + fn + "\""]
1412     newlines = 2
1413     if opt:
1414       insertion.append("lstparams " + '"' + opt + '"')
1415       newlines += 1
1416     document.body[i : i + 2] = insertion
1417     i += newlines
1418
1419
1420 def revert_include(document):
1421   'Reverts include insets to old format.'
1422   i = 0
1423   r0 = re.compile('preview.*')
1424   r1 = re.compile('LatexCommand (.+)')
1425   r2 = re.compile('filename "(.+)"')
1426   r3 = re.compile('lstparams "(.*)"')
1427   while True:
1428     i = find_token(document.body, "\\begin_inset CommandInset include", i)
1429     if i == -1:
1430       return
1431     nextline = i + 1
1432     m = r1.match(document.body[nextline])
1433     if m == None:
1434       document.warning("Malformed LyX document: No LatexCommand line for `" +
1435         document.body[i] + "' on line " + str(i) + ".")
1436       i += 1
1437       continue
1438     cmd = m.group(1)
1439     nextline += 1
1440     if r0.match(document.body[nextline]):
1441       previewline = document.body[nextline]
1442       nextline += 1
1443     else:
1444       previewline = ""
1445     m = r2.match(document.body[nextline])
1446     if m == None:
1447       document.warning("Malformed LyX document: No filename line for `" + \
1448         document.body[i] + "' on line " + str(i) + ".")
1449       i += 2
1450       continue
1451     fn = m.group(1)
1452     nextline += 1
1453     options = ""
1454     if (cmd == "lstinputlisting"):
1455       m = r3.match(document.body[nextline])
1456       if m != None:
1457         options = m.group(1)
1458         numlines = 5
1459         nextline += 1
1460     newline = "\\begin_inset Include \\" + cmd + "{" + fn + "}"
1461     if options:
1462       newline += ("[" + options + "]")
1463     insertion = [newline]
1464     if previewline != "":
1465       insertion.append(previewline)
1466     document.body[i : nextline] = insertion
1467     i += 2
1468
1469
1470 def revert_albanian(document):
1471     "Set language Albanian to English"
1472     i = 0
1473     if document.language == "albanian":
1474         document.language = "english"
1475         i = find_token(document.header, "\\language", 0)
1476         if i != -1:
1477             document.header[i] = "\\language english"
1478     j = 0
1479     while True:
1480         j = find_token(document.body, "\\lang albanian", j)
1481         if j == -1:
1482             return
1483         document.body[j] = document.body[j].replace("\\lang albanian", "\\lang english")
1484         j = j + 1
1485
1486
1487 def revert_lowersorbian(document):
1488     "Set language lower Sorbian to English"
1489     i = 0
1490     if document.language == "lowersorbian":
1491         document.language = "english"
1492         i = find_token(document.header, "\\language", 0)
1493         if i != -1:
1494             document.header[i] = "\\language english"
1495     j = 0
1496     while True:
1497         j = find_token(document.body, "\\lang lowersorbian", j)
1498         if j == -1:
1499             return
1500         document.body[j] = document.body[j].replace("\\lang lowersorbian", "\\lang english")
1501         j = j + 1
1502
1503
1504 def revert_uppersorbian(document):
1505     "Set language uppersorbian to usorbian as this was used in LyX 1.5"
1506     i = 0
1507     if document.language == "uppersorbian":
1508         document.language = "usorbian"
1509         i = find_token(document.header, "\\language", 0)
1510         if i != -1:
1511             document.header[i] = "\\language usorbian"
1512     j = 0
1513     while True:
1514         j = find_token(document.body, "\\lang uppersorbian", j)
1515         if j == -1:
1516             return
1517         document.body[j] = document.body[j].replace("\\lang uppersorbian", "\\lang usorbian")
1518         j = j + 1
1519
1520
1521 def convert_usorbian(document):
1522     "Set language usorbian to uppersorbian"
1523     i = 0
1524     if document.language == "usorbian":
1525         document.language = "uppersorbian"
1526         i = find_token(document.header, "\\language", 0)
1527         if i != -1:
1528             document.header[i] = "\\language uppersorbian"
1529     j = 0
1530     while True:
1531         j = find_token(document.body, "\\lang usorbian", j)
1532         if j == -1:
1533             return
1534         document.body[j] = document.body[j].replace("\\lang usorbian", "\\lang uppersorbian")
1535         j = j + 1
1536
1537
1538 def revert_macro_optional_params(document):
1539     "Convert macro definitions with optional parameters into ERTs"
1540     # Stub to convert macro definitions with one or more optional parameters
1541     # into uninterpreted ERT insets
1542
1543
1544 def revert_hyperlinktype(document):
1545     'Reverts hyperlink type'
1546     i = 0
1547     j = 0
1548     while True:
1549       i = find_token(document.body, "target", i)
1550       if i == -1:
1551           return
1552       j = find_token(document.body, "type", i)
1553       if j == -1:
1554           return
1555       if j == i + 1:
1556           del document.body[j]
1557       i = i + 1
1558
1559
1560 def revert_pagebreak(document):
1561     'Reverts pagebreak to ERT'
1562     i = 0
1563     while True:
1564       i = find_token(document.body, "\\pagebreak", i)
1565       if i == -1:
1566           return
1567       document.body[i] = '\\begin_inset ERT\nstatus collapsed\n\n' \
1568       '\\begin_layout Standard\n\n\n\\backslash\n' \
1569       'pagebreak{}\n\\end_layout\n\n\\end_inset\n\n'
1570       i = i + 1
1571
1572
1573 def revert_linebreak(document):
1574     'Reverts linebreak to ERT'
1575     i = 0
1576     while True:
1577       i = find_token(document.body, "\\linebreak", i)
1578       if i == -1:
1579           return
1580       document.body[i] = '\\begin_inset ERT\nstatus collapsed\n\n' \
1581       '\\begin_layout Standard\n\n\n\\backslash\n' \
1582       'linebreak{}\n\\end_layout\n\n\\end_inset\n\n'
1583       i = i + 1
1584
1585
1586 def revert_latin(document):
1587     "Set language Latin to English"
1588     i = 0
1589     if document.language == "latin":
1590         document.language = "english"
1591         i = find_token(document.header, "\\language", 0)
1592         if i != -1:
1593             document.header[i] = "\\language english"
1594     j = 0
1595     while True:
1596         j = find_token(document.body, "\\lang latin", j)
1597         if j == -1:
1598             return
1599         document.body[j] = document.body[j].replace("\\lang latin", "\\lang english")
1600         j = j + 1
1601
1602
1603 def revert_samin(document):
1604     "Set language North Sami to English"
1605     i = 0
1606     if document.language == "samin":
1607         document.language = "english"
1608         i = find_token(document.header, "\\language", 0)
1609         if i != -1:
1610             document.header[i] = "\\language english"
1611     j = 0
1612     while True:
1613         j = find_token(document.body, "\\lang samin", j)
1614         if j == -1:
1615             return
1616         document.body[j] = document.body[j].replace("\\lang samin", "\\lang english")
1617         j = j + 1
1618
1619
1620 def convert_serbocroatian(document):
1621     "Set language Serbocroatian to Croatian as this was really Croatian in LyX 1.5"
1622     i = 0
1623     if document.language == "serbocroatian":
1624         document.language = "croatian"
1625         i = find_token(document.header, "\\language", 0)
1626         if i != -1:
1627             document.header[i] = "\\language croatian"
1628     j = 0
1629     while True:
1630         j = find_token(document.body, "\\lang serbocroatian", j)
1631         if j == -1:
1632             return
1633         document.body[j] = document.body[j].replace("\\lang serbocroatian", "\\lang croatian")
1634         j = j + 1
1635
1636
1637 def convert_framed_notes(document):
1638     "Convert framed notes to boxes. "
1639     i = 0
1640     while 1:
1641         i = find_tokens(document.body, ["\\begin_inset Note Framed", "\\begin_inset Note Shaded"], i)
1642         if i == -1:
1643             return
1644         subst = [document.body[i].replace("\\begin_inset Note", "\\begin_inset Box"),
1645                  'position "t"',
1646                  'hor_pos "c"',
1647                  'has_inner_box 0',
1648                  'inner_pos "t"',
1649                  'use_parbox 0',
1650                  'width "100col%"',
1651                  'special "none"',
1652                  'height "1in"',
1653                  'height_special "totalheight"']
1654         document.body[i:i+1] = subst
1655         i = i + 9
1656
1657
1658 def convert_module_names(document):
1659   modulemap = { 'Braille' : 'braille', 'Endnote' : 'endnotes', 'Foot to End' : 'foottoend',\
1660     'Hanging' : 'hanging', 'Linguistics' : 'linguistics', 'Logical Markup' : 'logicalmkup', \
1661     'Theorems (AMS-Extended)' : 'theorems-ams-extended', 'Theorems (AMS)' : 'theorems-ams', \
1662     'Theorems (Order By Chapter)' : 'theorems-chap', 'Theorems (Order By Section)' : 'theorems-sec', \
1663     'Theorems (Starred)' : 'theorems-starred', 'Theorems' : 'theorems-std' }
1664   modlist = document.get_module_list()
1665   if len(modlist) == 0:
1666     return
1667   newmodlist = []
1668   for mod in modlist:
1669     if modulemap.has_key(mod):
1670       newmodlist.append(modulemap[mod])
1671     else:
1672       document.warning("Can't find module %s in the module map!" % mod)
1673       newmodlist.append(mod)
1674   document.set_module_list(newmodlist)
1675
1676
1677 def revert_module_names(document):
1678   modulemap = { 'braille' : 'Braille', 'endnotes' : 'Endnote', 'foottoend' : 'Foot to End',\
1679     'hanging' : 'Hanging', 'linguistics' : 'Linguistics', 'logicalmkup' : 'Logical Markup', \
1680     'theorems-ams-extended' : 'Theorems (AMS-Extended)', 'theorems-ams' : 'Theorems (AMS)', \
1681     'theorems-chap' : 'Theorems (Order By Chapter)', 'theorems-sec' : 'Theorems (Order By Section)', \
1682     'theorems-starred' : 'Theorems (Starred)', 'theorems-std' : 'Theorems'}
1683   modlist = document.get_module_list()
1684   if len(modlist) == 0:
1685     return
1686   newmodlist = []
1687   for mod in modlist:
1688     if modulemap.has_key(mod):
1689       newmodlist.append(modulemap[mod])
1690     else:
1691       document.warning("Can't find module %s in the module map!" % mod)
1692       newmodlist.append(mod)
1693   document.set_module_list(newmodlist)
1694
1695
1696 def revert_colsep(document):
1697     i = find_token(document.header, "\\columnsep", 0)
1698     if i == -1:
1699         return
1700     colsepline = document.header[i]
1701     r = re.compile(r'\\columnsep (.*)')
1702     m = r.match(colsepline)
1703     if not m:
1704         document.warning("Malformed column separation line!")
1705         return
1706     colsep = m.group(1)
1707     del document.header[i]
1708     #it seems to be safe to add the package even if it is already used
1709     pretext = ["\\usepackage{geometry}", "\\geometry{columnsep=" + colsep + "}"]
1710
1711     add_to_preamble(document, pretext)
1712
1713
1714 def revert_framed_notes(document):
1715     "Revert framed boxes to notes. "
1716     i = 0
1717     while 1:
1718         i = find_tokens(document.body, ["\\begin_inset Box Framed", "\\begin_inset Box Shaded"], i)
1719
1720         if i == -1:
1721             return
1722         j = find_end_of_inset(document.body, i + 1)
1723         if j == -1:
1724             # should not happen
1725             document.warning("Malformed LyX document: Could not find end of Box inset.")
1726         k = find_token(document.body, "status", i + 1, j)
1727         if k == -1:
1728             document.warning("Malformed LyX document: Missing `status' tag in Box inset.")
1729             return
1730         status = document.body[k]
1731         l = find_default_layout(document, i + 1, j)
1732         if l == -1:
1733             document.warning("Malformed LyX document: Missing `\\begin_layout' in Box inset.")
1734             return
1735         m = find_token(document.body, "\\end_layout", i + 1, j)
1736         if m == -1:
1737             document.warning("Malformed LyX document: Missing `\\end_layout' in Box inset.")
1738             return
1739         ibox = find_token(document.body, "has_inner_box 1", i + 1, k)
1740         pbox = find_token(document.body, "use_parbox 1", i + 1, k)
1741         if ibox == -1 and pbox == -1:
1742             document.body[i] = document.body[i].replace("\\begin_inset Box", "\\begin_inset Note")
1743             del document.body[i+1:k]
1744         else:
1745             document.body[i] = document.body[i].replace("\\begin_inset Box Shaded", "\\begin_inset Box Frameless")
1746             subst1 = [document.body[l],
1747                       "\\begin_inset Note Shaded",
1748                       status,
1749                       '\\begin_layout Standard']
1750             document.body[l:l + 1] = subst1
1751             subst2 = [document.body[m], "\\end_layout", "\\end_inset"]
1752             document.body[m:m + 1] = subst2
1753         i = i + 1
1754
1755
1756 def revert_slash(document):
1757     'Revert \\SpecialChar \\slash{} to ERT'
1758     r = re.compile(r'\\SpecialChar \\slash{}')
1759     i = 0
1760     while i < len(document.body):
1761         m = r.match(document.body[i])
1762         if m:
1763           subst = ['\\begin_inset ERT',
1764                    'status collapsed', '',
1765                    '\\begin_layout Standard',
1766                    '', '', '\\backslash',
1767                    'slash{}',
1768                    '\\end_layout', '',
1769                    '\\end_inset', '']
1770           document.body[i: i+1] = subst
1771           i = i + len(subst)
1772         else:
1773           i = i + 1
1774
1775
1776 def revert_nobreakdash(document):
1777     'Revert \\SpecialChar \\nobreakdash- to ERT'
1778     i = 0
1779     while i < len(document.body):
1780         line = document.body[i]
1781         r = re.compile(r'\\SpecialChar \\nobreakdash-')
1782         m = r.match(line)
1783         if m:
1784             subst = ['\\begin_inset ERT',
1785                     'status collapsed', '',
1786                     '\\begin_layout Standard', '', '',
1787                     '\\backslash',
1788                     'nobreakdash-',
1789                     '\\end_layout', '',
1790                     '\\end_inset', '']
1791             document.body[i:i+1] = subst
1792             i = i + len(subst)
1793             j = find_token(document.header, "\\use_amsmath", 0)
1794             if j == -1:
1795                 document.warning("Malformed LyX document: Missing '\\use_amsmath'.")
1796                 return
1797             document.header[j] = "\\use_amsmath 2"
1798         else:
1799             i = i + 1
1800
1801
1802 #Returns number of lines added/removed
1803 def revert_nocite_key(body, start, end):
1804     'key "..." -> \nocite{...}'
1805     r = re.compile(r'^key "(.*)"')
1806     i = start
1807     j = end
1808     while i < j:
1809         m = r.match(body[i])
1810         if m:
1811             body[i:i+1] = ["\\backslash", "nocite{" + m.group(1) + "}"]
1812             j += 1     # because we added a line
1813             i += 2     # skip that line
1814         else:
1815             del body[i]
1816             j -= 1     # because we deleted a line
1817             # no need to change i, since it now points to the next line
1818     return j - end
1819
1820
1821 def revert_nocite(document):
1822     "Revert LatexCommand nocite to ERT"
1823     i = 0
1824     while 1:
1825         i = find_token(document.body, "\\begin_inset CommandInset citation", i)
1826         if i == -1:
1827             return
1828         if (document.body[i+1] != "LatexCommand nocite"):
1829             # note that we already incremented i
1830             i = i + 1
1831             continue
1832         insetEnd = find_end_of_inset(document.body, i)
1833         if insetEnd == -1:
1834             #this should not happen
1835             document.warning("End of CommandInset citation not found in revert_nocite!")
1836             return
1837
1838         paramLocation = i + 2 #start of the inset's parameters
1839         addedLines = 0
1840         document.body[i:i+2] = \
1841             ["\\begin_inset ERT", "status collapsed", "", "\\begin_layout Standard"]
1842         # that added two lines
1843         paramLocation += 2
1844         insetEnd += 2
1845         #print insetEnd, document.body[i: insetEnd + 1]
1846         insetEnd += revert_nocite_key(document.body, paramLocation, insetEnd)
1847         #print insetEnd, document.body[i: insetEnd + 1]
1848         document.body.insert(insetEnd, "\\end_layout")
1849         document.body.insert(insetEnd + 1, "")
1850         i = insetEnd + 1
1851
1852
1853 def revert_btprintall(document):
1854     "Revert (non-bibtopic) btPrintAll option to ERT \nocite{*}"
1855     i = find_token(document.header, '\\use_bibtopic', 0)
1856     if i == -1:
1857         document.warning("Malformed lyx document: Missing '\\use_bibtopic'.")
1858         return
1859     if get_value(document.header, '\\use_bibtopic', 0) == "false":
1860         i = 0
1861         while i < len(document.body):
1862             i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
1863             if i == -1:
1864                 return
1865             j = find_end_of_inset(document.body, i + 1)
1866             if j == -1:
1867                 #this should not happen
1868                 document.warning("End of CommandInset bibtex not found in revert_btprintall!")
1869                 j = len(document.body)
1870             # this range isn't really right, but it should be OK, since we shouldn't
1871             # see more than one matching line in each inset
1872             addedlines = 0
1873             for k in range(i, j):
1874                 if (document.body[k] == 'btprint "btPrintAll"'):
1875                     del document.body[k]
1876                     subst = ["\\begin_inset ERT",
1877                              "status collapsed", "",
1878                              "\\begin_layout Standard", "",
1879                              "\\backslash",
1880                              "nocite{*}",
1881                              "\\end_layout",
1882                              "\\end_inset"]
1883                     document.body[i:i] = subst
1884                     addlines = addedlines + len(subst) - 1
1885             i = j + addedlines
1886
1887
1888 def revert_bahasam(document):
1889     "Set language Bahasa Malaysia to Bahasa Indonesia"
1890     i = 0
1891     if document.language == "bahasam":
1892         document.language = "bahasa"
1893         i = find_token(document.header, "\\language", 0)
1894         if i != -1:
1895             document.header[i] = "\\language bahasa"
1896     j = 0
1897     while True:
1898         j = find_token(document.body, "\\lang bahasam", j)
1899         if j == -1:
1900             return
1901         document.body[j] = document.body[j].replace("\\lang bahasam", "\\lang bahasa")
1902         j = j + 1
1903
1904
1905 def revert_interlingua(document):
1906     "Set language Interlingua to English"
1907     i = 0
1908     if document.language == "interlingua":
1909         document.language = "english"
1910         i = find_token(document.header, "\\language", 0)
1911         if i != -1:
1912             document.header[i] = "\\language english"
1913     j = 0
1914     while True:
1915         j = find_token(document.body, "\\lang interlingua", j)
1916         if j == -1:
1917             return
1918         document.body[j] = document.body[j].replace("\\lang interlingua", "\\lang english")
1919         j = j + 1
1920
1921
1922 def revert_serbianlatin(document):
1923     "Set language Serbian-Latin to Croatian"
1924     i = 0
1925     if document.language == "serbian-latin":
1926         document.language = "croatian"
1927         i = find_token(document.header, "\\language", 0)
1928         if i != -1:
1929             document.header[i] = "\\language croatian"
1930     j = 0
1931     while True:
1932         j = find_token(document.body, "\\lang serbian-latin", j)
1933         if j == -1:
1934             return
1935         document.body[j] = document.body[j].replace("\\lang serbian-latin", "\\lang croatian")
1936         j = j + 1
1937
1938
1939 def revert_rotfloat(document):
1940     " Revert sideways custom floats. "
1941     i = 0
1942     while 1:
1943         # whitespace intended (exclude \\begin_inset FloatList)
1944         i = find_token(document.body, "\\begin_inset Float ", i)
1945         if i == -1:
1946             return
1947         line = document.body[i]
1948         r = re.compile(r'\\begin_inset Float (.*)$')
1949         m = r.match(line)
1950         if m == None:
1951             document.warning("Unable to match line " + str(i) + " of body!")
1952             i += 1
1953             continue
1954         floattype = m.group(1)
1955         if floattype == "figure" or floattype == "table":
1956             i += 1
1957             continue
1958         j = find_end_of_inset(document.body, i)
1959         if j == -1:
1960             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_rotfloat.")
1961             i += 1
1962             continue
1963         addedLines = 0
1964         if get_value(document.body, 'sideways', i, j) == "false":
1965             i += 1
1966             continue
1967         l = find_default_layout(document, i + 1, j)
1968         if l == -1:
1969             document.warning("Malformed LyX document: Missing `\\begin_layout' in Float inset.")
1970             return
1971         subst = ['\\begin_layout Standard',
1972                   '\\begin_inset ERT',
1973                   'status collapsed', '',
1974                   '\\begin_layout Standard', '', '',
1975                   '\\backslash', '',
1976                   'end{sideways' + floattype + '}',
1977                   '\\end_layout', '', '\\end_inset']
1978         document.body[j : j+1] = subst
1979         addedLines = len(subst) - 1
1980         del document.body[i+1 : l]
1981         addedLines -= (l-1) - (i+1)
1982         subst = ['\\begin_inset ERT', 'status collapsed', '',
1983                   '\\begin_layout Standard', '', '', '\\backslash',
1984                   'begin{sideways' + floattype + '}',
1985                   '\\end_layout', '', '\\end_inset', '',
1986                   '\\end_layout', '']
1987         document.body[i : i+1] = subst
1988         addedLines += len(subst) - 1
1989         if floattype == "algorithm":
1990             add_to_preamble(document,
1991                             ['% Commands inserted by lyx2lyx for sideways algorithm float',
1992                               '\\usepackage{rotfloat}',
1993                               '\\floatstyle{ruled}',
1994                               '\\newfloat{algorithm}{tbp}{loa}',
1995                               '\\floatname{algorithm}{Algorithm}'])
1996         else:
1997             document.warning("Cannot create preamble definition for custom float" + floattype + ".")
1998         i += addedLines + 1
1999
2000
2001 def revert_widesideways(document):
2002     " Revert wide sideways floats. "
2003     i = 0
2004     while 1:
2005         # whitespace intended (exclude \\begin_inset FloatList)
2006         i = find_token(document.body, '\\begin_inset Float ', i)
2007         if i == -1:
2008             return
2009         line = document.body[i]
2010         r = re.compile(r'\\begin_inset Float (.*)$')
2011         m = r.match(line)
2012         if m == None:
2013             document.warning("Unable to match line " + str(i) + " of body!")
2014             i += 1
2015             continue
2016         floattype = m.group(1)
2017         if floattype != "figure" and floattype != "table":
2018             i += 1
2019             continue
2020         j = find_end_of_inset(document.body, i)
2021         if j == -1:
2022             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_widesideways.")
2023             i += 1
2024             continue
2025         if get_value(document.body, 'sideways', i, j) == "false" or \
2026            get_value(document.body, 'wide', i, j) == "false":
2027              i += 1
2028              continue
2029         l = find_default_layout(document, i + 1, j)
2030         if l == -1:
2031             document.warning("Malformed LyX document: Missing `\\begin_layout' in Float inset.")
2032             return
2033         subst = ['\\begin_layout Standard', '\\begin_inset ERT',
2034                   'status collapsed', '',
2035                   '\\begin_layout Standard', '', '', '\\backslash',
2036                   'end{sideways' + floattype + '*}',
2037                   '\\end_layout', '', '\\end_inset']
2038         document.body[j : j+1] = subst
2039         addedLines = len(subst) - 1
2040         del document.body[i+1:l-1]
2041         addedLines -= (l-1) - (i+1)
2042         subst = ['\\begin_inset ERT', 'status collapsed', '',
2043                  '\\begin_layout Standard', '', '', '\\backslash',
2044                  'begin{sideways' + floattype + '*}', '\\end_layout', '',
2045                  '\\end_inset', '', '\\end_layout', '']
2046         document.body[i : i+1] = subst
2047         addedLines += len(subst) - 1
2048         add_to_preamble(document, ['\\usepackage{rotfloat}\n'])
2049         i += addedLines + 1
2050
2051
2052 def revert_inset_embedding(document, type):
2053     ' Remove embed tag from certain type of insets'
2054     i = 0
2055     while 1:
2056         i = find_token(document.body, "\\begin_inset %s" % type, i)
2057         if i == -1:
2058             return
2059         j = find_end_of_inset(document.body, i)
2060         if j == -1:
2061             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_inset_embedding.")
2062             i = i + 1
2063             continue
2064         k = find_token(document.body, "\tembed", i, j)
2065         if k == -1:
2066             k = find_token(document.body, "embed", i, j)
2067         if k != -1:
2068             del document.body[k]
2069         i = i + 1
2070
2071
2072 def revert_external_embedding(document):
2073     ' Remove embed tag from external inset '
2074     revert_inset_embedding(document, 'External')
2075
2076
2077 def convert_subfig(document):
2078     " Convert subfigures to subfloats. "
2079     i = 0
2080     while 1:
2081         i = find_token(document.body, '\\begin_inset Graphics', i)
2082         if i == -1:
2083             return
2084         endInset = find_end_of_inset(document.body, i)
2085         if endInset == -1:
2086             document.warning("Malformed lyx document: Missing '\\end_inset' in convert_subfig.")
2087             i += 1
2088             continue
2089         k = find_token(document.body, '\tsubcaption', i, endInset)
2090         if k == -1:
2091             i = endInset
2092             continue
2093         l = find_token(document.body, '\tsubcaptionText', i, endInset)
2094         if l == -1:
2095             document.warning("Malformed lyx document: Can't find subcaptionText!")
2096             i = endInset
2097             continue
2098         caption = document.body[l][16:].strip('"')
2099         del document.body[l]
2100         del document.body[k]
2101         addedLines = -2
2102         subst = ['\\begin_inset Float figure', 'wide false', 'sideways false',
2103                  'status open', '', '\\begin_layout Plain Layout', '\\begin_inset Caption',
2104                  '', '\\begin_layout Plain Layout'] + latex2lyx(caption) + \
2105                  [ '\\end_layout', '', '\\end_inset', '',
2106                  '\\end_layout', '', '\\begin_layout Plain Layout']
2107         document.body[i : i] = subst
2108         addedLines += len(subst)
2109         endInset += addedLines
2110         subst = ['', '\\end_inset', '', '\\end_layout']
2111         document.body[endInset : endInset] = subst
2112         addedLines += len(subst)
2113         i += addedLines + 1
2114
2115
2116 def revert_subfig(document):
2117     " Revert subfloats. "
2118     i = 0
2119     while 1:
2120         # whitespace intended (exclude \\begin_inset FloatList)
2121         i = find_tokens(document.body, ['\\begin_inset Float ', '\\begin_inset Wrap'], i)
2122         if i == -1:
2123             return
2124         j = 0
2125         addedLines = 0
2126         while j != -1:
2127             j = find_end_of_inset(document.body, i)
2128             if j == -1:
2129                 document.warning("Malformed lyx document: Missing '\\end_inset' (float) at line " + str(i + len(document.header)) + ".\n\t" + document.body[i])
2130                 # document.warning(document.body[i-1] + "\n" + document.body[i+1])
2131                 i += 1
2132                 continue # this will get us back to the outer loop, since j == -1
2133             # look for embedded float (= subfloat)
2134             # whitespace intended (exclude \\begin_inset FloatList)
2135             k = find_token(document.body, '\\begin_inset Float ', i + 1, j)
2136             if k == -1:
2137                 break
2138             l = find_end_of_inset(document.body, k)
2139             if l == -1:
2140                 document.warning("Malformed lyx document: Missing '\\end_inset' (embedded float).")
2141                 i += 1
2142                 j == -1
2143                 continue # escape to the outer loop
2144             m = find_default_layout(document, k + 1, l)
2145             # caption?
2146             cap = find_token(document.body, '\\begin_inset Caption', k + 1, l)
2147             caption = ''
2148             shortcap = ''
2149             capend = cap
2150             if cap != -1:
2151                 capend = find_end_of_inset(document.body, cap)
2152                 if capend == -1:
2153                     document.warning("Malformed lyx document: Missing '\\end_inset' (caption).")
2154                     return
2155                 # label?
2156                 label = ''
2157                 lbl = find_token(document.body, '\\begin_inset CommandInset label', cap, capend)
2158                 if lbl != -1:
2159                     lblend = find_end_of_inset(document.body, lbl + 1)
2160                     if lblend == -1:
2161                         document.warning("Malformed lyx document: Missing '\\end_inset' (label).")
2162                         return
2163                     for line in document.body[lbl:lblend + 1]:
2164                         if line.startswith('name '):
2165                             label = line.split()[1].strip('"')
2166                             break
2167                 else:
2168                     lbl = capend
2169                     lblend = capend
2170                     label = ''
2171                 # opt arg?
2172                 opt = find_token(document.body, '\\begin_inset OptArg', cap, capend)
2173                 if opt != -1:
2174                     optend = find_end_of_inset(document.body, opt)
2175                     if optend == -1:
2176                         document.warning("Malformed lyx document: Missing '\\end_inset' (OptArg).")
2177                         return
2178                     optc = find_default_layout(document, opt, optend)
2179                     if optc == -1:
2180                         document.warning("Malformed LyX document: Missing `\\begin_layout' in Float inset.")
2181                         return
2182                     optcend = find_end_of(document.body, optc, "\\begin_layout", "\\end_layout")
2183                     for line in document.body[optc:optcend]:
2184                         if not line.startswith('\\'):
2185                             shortcap += line.strip()
2186                 else:
2187                     opt = capend
2188                     optend = capend
2189                 for line in document.body[cap:capend]:
2190                     if line in document.body[lbl:lblend]:
2191                         continue
2192                     elif line in document.body[opt:optend]:
2193                         continue
2194                     elif not line.startswith('\\'):
2195                         caption += line.strip()
2196                 if len(label) > 0:
2197                     caption += "\\backslash\nlabel{" + label + "}"
2198             subst = '\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus collapsed\n\n' \
2199                       '\\begin_layout Plain Layout\n\n}\n\\end_layout\n\n\\end_inset\n\n' \
2200                       '\\end_layout\n\n\\begin_layout Plain Layout\n'
2201             subst = subst.split('\n')
2202             document.body[l : l+1] = subst
2203             addedLines = len(subst) - 1
2204             # this is before l and so is unchanged by the multiline insertion
2205             if cap != capend:
2206                 del document.body[cap:capend+1]
2207                 addedLines -= (capend + 1 - cap)
2208             del document.body[k+1:m-1]
2209             addedLines -= (m - 1 - (k + 1))
2210             insertion = '\\begin_inset ERT\nstatus collapsed\n\n' \
2211                         '\\begin_layout Plain Layout\n\n\\backslash\n' \
2212                         'subfloat'
2213             if len(shortcap) > 0:
2214                 insertion = insertion + "[" + shortcap + "]"
2215             if len(caption) > 0:
2216                 insertion = insertion + "[" + caption + "]"
2217             insertion = insertion + '{%\n\\end_layout\n\n\\end_inset\n\n\\end_layout\n'
2218             insertion = insertion.split('\n')
2219             document.body[k : k + 1] = insertion
2220             addedLines += len(insertion) - 1
2221             add_to_preamble(document, ['\\usepackage{subfig}\n'])
2222         i += addedLines + 1
2223
2224
2225 def revert_wrapplacement(document):
2226     " Revert placement options wrap floats (wrapfig). "
2227     i = 0
2228     while True:
2229         i = find_token(document.body, "\\begin_inset Wrap figure", i)
2230         if i == -1:
2231             return
2232         e = find_end_of_inset(document.body, i)
2233         j = find_token(document.body, "placement", i + 1, e)
2234         if j == -1:
2235             document.warning("Malformed LyX document: Couldn't find placement parameter of wrap float.")
2236             i += 1
2237             continue
2238         r = re.compile("placement (o|i|l|r)")
2239         m = r.match(document.body[j])
2240         if m == None:
2241             document.warning("Malformed LyX document: Placement option isn't O|I|R|L!")
2242         document.body[j] = "placement " + m.group(1).lower()
2243         i = j
2244
2245
2246 def remove_extra_embedded_files(document):
2247     " Remove \extra_embedded_files from buffer params "
2248     i = find_token(document.header, '\\extra_embedded_files', 0)
2249     if i == -1:
2250         return
2251     document.header.pop(i)
2252
2253
2254 def convert_spaceinset(document):
2255     " Convert '\\InsetSpace foo' to '\\begin_inset Space foo\n\\end_inset' "
2256     i = 0
2257     while i < len(document.body):
2258         m = re.match(r'(.*)\\InsetSpace (.*)', document.body[i])
2259         if m:
2260             before = m.group(1)
2261             after = m.group(2)
2262             subst = [before, "\\begin_inset Space " + after, "\\end_inset"]
2263             document.body[i: i+1] = subst
2264             i = i + len(subst)
2265         else:
2266             i = i + 1
2267
2268
2269 def revert_spaceinset(document):
2270     " Revert '\\begin_inset Space foo\n\\end_inset' to '\\InsetSpace foo' "
2271     i = 0
2272     while True:
2273         i = find_token(document.body, "\\begin_inset Space", i)
2274         if i == -1:
2275             return
2276         j = find_end_of_inset(document.body, i)
2277         if j == -1:
2278             document.warning("Malformed LyX document: Could not find end of space inset.")
2279             continue
2280         document.body[i] = document.body[i].replace('\\begin_inset Space', '\\InsetSpace')
2281         del document.body[j]
2282
2283
2284 def convert_hfill(document):
2285     " Convert hfill to space inset "
2286     i = 0
2287     while True:
2288         i = find_token(document.body, "\\hfill", i)
2289         if i == -1:
2290             return
2291         subst = document.body[i].replace('\\hfill', \
2292                   '\n\\begin_inset Space \\hfill{}\n\\end_inset')
2293         subst = subst.split('\n')
2294         document.body[i : i+1] = subst
2295         i += len(subst)
2296
2297
2298 def revert_hfills(document):
2299     ' Revert \\hfill commands '
2300     hfill = re.compile(r'\\hfill')
2301     dotfill = re.compile(r'\\dotfill')
2302     hrulefill = re.compile(r'\\hrulefill')
2303     i = 0
2304     while True:
2305         i = find_token(document.body, "\\InsetSpace", i)
2306         if i == -1:
2307             return
2308         if hfill.search(document.body[i]):
2309             document.body[i] = \
2310               document.body[i].replace('\\InsetSpace \\hfill{}', '\\hfill')
2311             i += 1
2312             continue
2313         if dotfill.search(document.body[i]):
2314             subst = document.body[i].replace('\\InsetSpace \\dotfill{}', \
2315               '\\begin_inset ERT\nstatus collapsed\n\n' \
2316               '\\begin_layout Standard\n\n\n\\backslash\n' \
2317               'dotfill{}\n\\end_layout\n\n\\end_inset\n\n')
2318             subst = subst.split('\n')
2319             document.body[i : i+1] = subst
2320             i += len(subst)
2321             continue
2322         if hrulefill.search(document.body[i]):
2323             subst = document.body[i].replace('\\InsetSpace \\hrulefill{}', \
2324               '\\begin_inset ERT\nstatus collapsed\n\n' \
2325               '\\begin_layout Standard\n\n\n\\backslash\n' \
2326               'hrulefill{}\n\\end_layout\n\n\\end_inset\n\n')
2327             subst = subst.split('\n')
2328             document.body[i : i+1] = subst
2329             i += len(subst)
2330             continue
2331         i += 1
2332
2333 def revert_hspace(document):
2334     ' Revert \\InsetSpace \\hspace{} to ERT '
2335     i = 0
2336     hspace = re.compile(r'\\hspace{}')
2337     hstar  = re.compile(r'\\hspace\*{}')
2338     while True:
2339         i = find_token(document.body, "\\InsetSpace \\hspace", i)
2340         if i == -1:
2341             return
2342         length = get_value(document.body, '\\length', i+1)
2343         if length == '':
2344             document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
2345             return
2346         del document.body[i+1]
2347         addedLines = -1
2348         if hstar.search(document.body[i]):
2349             subst = document.body[i].replace('\\InsetSpace \\hspace*{}', \
2350               '\\begin_inset ERT\nstatus collapsed\n\n' \
2351               '\\begin_layout Standard\n\n\n\\backslash\n' \
2352               'hspace*{' + length + '}\n\\end_layout\n\n\\end_inset\n\n')
2353             subst = subst.split('\n')
2354             document.body[i : i+1] = subst
2355             addedLines += len(subst) - 1
2356             i += addedLines + 1
2357             continue
2358         if hspace.search(document.body[i]):
2359             subst = document.body[i].replace('\\InsetSpace \\hspace{}', \
2360               '\\begin_inset ERT\nstatus collapsed\n\n' \
2361               '\\begin_layout Standard\n\n\n\\backslash\n' \
2362               'hspace{' + length + '}\n\\end_layout\n\n\\end_inset\n\n')
2363             subst = subst.split('\n')
2364             document.body[i : i+1] = subst
2365             addedLines += len(subst) - 1
2366             i += addedLines + 1
2367             continue
2368         i += 1
2369
2370
2371 def revert_protected_hfill(document):
2372     ' Revert \\begin_inset Space \\hspace*{\\fill} to ERT '
2373     i = 0
2374     while True:
2375         i = find_token(document.body, '\\begin_inset Space \\hspace*{\\fill}', i)
2376         if i == -1:
2377             return
2378         j = find_end_of_inset(document.body, i)
2379         if j == -1:
2380             document.warning("Malformed LyX document: Could not find end of space inset.")
2381             continue
2382         del document.body[j]
2383         subst = document.body[i].replace('\\begin_inset Space \\hspace*{\\fill}', \
2384           '\\begin_inset ERT\nstatus collapsed\n\n' \
2385           '\\begin_layout Standard\n\n\n\\backslash\n' \
2386           'hspace*{\n\\backslash\nfill}\n\\end_layout\n\n\\end_inset\n\n')
2387         subst = subst.split('\n')
2388         document.body[i : i+1] = subst
2389         i += len(subst)
2390
2391
2392 def revert_leftarrowfill(document):
2393     ' Revert \\begin_inset Space \\leftarrowfill{} to ERT '
2394     i = 0
2395     while True:
2396         i = find_token(document.body, '\\begin_inset Space \\leftarrowfill{}', i)
2397         if i == -1:
2398             return
2399         j = find_end_of_inset(document.body, i)
2400         if j == -1:
2401             document.warning("Malformed LyX document: Could not find end of space inset.")
2402             continue
2403         del document.body[j]
2404         subst = document.body[i].replace('\\begin_inset Space \\leftarrowfill{}', \
2405           '\\begin_inset ERT\nstatus collapsed\n\n' \
2406           '\\begin_layout Standard\n\n\n\\backslash\n' \
2407           'leftarrowfill{}\n\\end_layout\n\n\\end_inset\n\n')
2408         subst = subst.split('\n')
2409         document.body[i : i+1] = subst
2410         i += len(subst)
2411
2412
2413 def revert_rightarrowfill(document):
2414     ' Revert \\begin_inset Space \\rightarrowfill{} to ERT '
2415     i = 0
2416     while True:
2417         i = find_token(document.body, '\\begin_inset Space \\rightarrowfill{}', i)
2418         if i == -1:
2419             return
2420         j = find_end_of_inset(document.body, i)
2421         if j == -1:
2422             document.warning("Malformed LyX document: Could not find end of space inset.")
2423             continue
2424         del document.body[j]
2425         subst = document.body[i].replace('\\begin_inset Space \\rightarrowfill{}', \
2426           '\\begin_inset ERT\nstatus collapsed\n\n' \
2427           '\\begin_layout Standard\n\n\n\\backslash\n' \
2428           'rightarrowfill{}\n\\end_layout\n\n\\end_inset\n\n')
2429         subst = subst.split('\n')
2430         document.body[i : i+1] = subst
2431         i += len(subst)
2432
2433
2434 def revert_upbracefill(document):
2435     ' Revert \\begin_inset Space \\upbracefill{} to ERT '
2436     i = 0
2437     while True:
2438         i = find_token(document.body, '\\begin_inset Space \\upbracefill{}', i)
2439         if i == -1:
2440             return
2441         j = find_end_of_inset(document.body, i)
2442         if j == -1:
2443             document.warning("Malformed LyX document: Could not find end of space inset.")
2444             continue
2445         del document.body[j]
2446         subst = document.body[i].replace('\\begin_inset Space \\upbracefill{}', \
2447           '\\begin_inset ERT\nstatus collapsed\n\n' \
2448           '\\begin_layout Standard\n\n\n\\backslash\n' \
2449           'upbracefill{}\n\\end_layout\n\n\\end_inset\n\n')
2450         subst = subst.split('\n')
2451         document.body[i : i+1] = subst
2452         i += len(subst)
2453
2454
2455 def revert_downbracefill(document):
2456     ' Revert \\begin_inset Space \\downbracefill{} to ERT '
2457     i = 0
2458     while True:
2459         i = find_token(document.body, '\\begin_inset Space \\downbracefill{}', i)
2460         if i == -1:
2461             return
2462         j = find_end_of_inset(document.body, i)
2463         if j == -1:
2464             document.warning("Malformed LyX document: Could not find end of space inset.")
2465             continue
2466         del document.body[j]
2467         subst = document.body[i].replace('\\begin_inset Space \\downbracefill{}', \
2468           '\\begin_inset ERT\nstatus collapsed\n\n' \
2469           '\\begin_layout Standard\n\n\n\\backslash\n' \
2470           'downbracefill{}\n\\end_layout\n\n\\end_inset\n\n')
2471         subst = subst.split('\n')
2472         document.body[i : i+1] = subst
2473         i += len(subst)
2474
2475
2476 def revert_local_layout(document):
2477     ' Revert local layout headers.'
2478     i = 0
2479     while True:
2480         i = find_token(document.header, "\\begin_local_layout", i)
2481         if i == -1:
2482             return
2483         j = find_end_of(document.header, i, "\\begin_local_layout", "\\end_local_layout")
2484         if j == -1:
2485             # this should not happen
2486             break
2487         document.header[i : j + 1] = []
2488
2489
2490 def convert_pagebreaks(document):
2491     ' Convert inline Newpage insets to new format '
2492     i = 0
2493     while True:
2494         i = find_token(document.body, '\\newpage', i)
2495         if i == -1:
2496             break
2497         document.body[i:i+1] = ['\\begin_inset Newpage newpage',
2498                                 '\\end_inset']
2499     i = 0
2500     while True:
2501         i = find_token(document.body, '\\pagebreak', i)
2502         if i == -1:
2503             break
2504         document.body[i:i+1] = ['\\begin_inset Newpage pagebreak',
2505                                 '\\end_inset']
2506     i = 0
2507     while True:
2508         i = find_token(document.body, '\\clearpage', i)
2509         if i == -1:
2510             break
2511         document.body[i:i+1] = ['\\begin_inset Newpage clearpage',
2512                                 '\\end_inset']
2513     i = 0
2514     while True:
2515         i = find_token(document.body, '\\cleardoublepage', i)
2516         if i == -1:
2517             break
2518         document.body[i:i+1] = ['\\begin_inset Newpage cleardoublepage',
2519                                 '\\end_inset']
2520
2521
2522 def revert_pagebreaks(document):
2523     ' Revert \\begin_inset Newpage to previous inline format '
2524     i = 0
2525     while True:
2526         i = find_token(document.body, '\\begin_inset Newpage', i)
2527         if i == -1:
2528             return
2529         j = find_end_of_inset(document.body, i)
2530         if j == -1:
2531             document.warning("Malformed LyX document: Could not find end of Newpage inset.")
2532             continue
2533         del document.body[j]
2534         document.body[i] = document.body[i].replace('\\begin_inset Newpage newpage', '\\newpage')
2535         document.body[i] = document.body[i].replace('\\begin_inset Newpage pagebreak', '\\pagebreak')
2536         document.body[i] = document.body[i].replace('\\begin_inset Newpage clearpage', '\\clearpage')
2537         document.body[i] = document.body[i].replace('\\begin_inset Newpage cleardoublepage', '\\cleardoublepage')
2538
2539
2540 def convert_linebreaks(document):
2541     ' Convert inline Newline insets to new format '
2542     i = 0
2543     while True:
2544         i = find_token(document.body, '\\newline', i)
2545         if i == -1:
2546             break
2547         document.body[i:i+1] = ['\\begin_inset Newline newline',
2548                                 '\\end_inset']
2549     i = 0
2550     while True:
2551         i = find_token(document.body, '\\linebreak', i)
2552         if i == -1:
2553             break
2554         document.body[i:i+1] = ['\\begin_inset Newline linebreak',
2555                                 '\\end_inset']
2556
2557
2558 def revert_linebreaks(document):
2559     ' Revert \\begin_inset Newline to previous inline format '
2560     i = 0
2561     while True:
2562         i = find_token(document.body, '\\begin_inset Newline', i)
2563         if i == -1:
2564             return
2565         j = find_end_of_inset(document.body, i)
2566         if j == -1:
2567             document.warning("Malformed LyX document: Could not find end of Newline inset.")
2568             continue
2569         del document.body[j]
2570         document.body[i] = document.body[i].replace('\\begin_inset Newline newline', '\\newline')
2571         document.body[i] = document.body[i].replace('\\begin_inset Newline linebreak', '\\linebreak')
2572
2573
2574 def convert_japanese_plain(document):
2575     ' Set language japanese-plain to japanese '
2576     i = 0
2577     if document.language == "japanese-plain":
2578         document.language = "japanese"
2579         i = find_token(document.header, "\\language", 0)
2580         if i != -1:
2581             document.header[i] = "\\language japanese"
2582     j = 0
2583     while True:
2584         j = find_token(document.body, "\\lang japanese-plain", j)
2585         if j == -1:
2586             return
2587         document.body[j] = document.body[j].replace("\\lang japanese-plain", "\\lang japanese")
2588         j = j + 1
2589
2590
2591 def revert_pdfpages(document):
2592     ' Revert pdfpages external inset to ERT '
2593     i = 0
2594     while 1:
2595         i = find_token(document.body, "\\begin_inset External", i)
2596         if i == -1:
2597             return
2598         j = find_end_of_inset(document.body, i)
2599         if j == -1:
2600             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_pdfpages.")
2601             i = i + 1
2602             continue
2603         if get_value(document.body, 'template', i, j) == "PDFPages":
2604             filename = get_value(document.body, 'filename', i, j)
2605             extra = ''
2606             r = re.compile(r'\textra PDFLaTeX \"(.*)\"$')
2607             for k in range(i, j):
2608                 m = r.match(document.body[k])
2609                 if m:
2610                     extra = m.group(1)
2611             angle = get_value(document.body, 'rotateAngle', i, j)
2612             width = get_value(document.body, 'width', i, j)
2613             height = get_value(document.body, 'height', i, j)
2614             scale = get_value(document.body, 'scale', i, j)
2615             keepAspectRatio = find_token(document.body, "\tkeepAspectRatio", i, j)
2616             options = extra
2617             if angle != '':
2618                  if options != '':
2619                      options += ",angle=" + angle
2620                  else:
2621                      options += "angle=" + angle
2622             if width != '':
2623                  if options != '':
2624                      options += ",width=" + convert_len(width)
2625                  else:
2626                      options += "width=" + convert_len(width)
2627             if height != '':
2628                  if options != '':
2629                      options += ",height=" + convert_len(height)
2630                  else:
2631                      options += "height=" + convert_len(height)
2632             if scale != '':
2633                  if options != '':
2634                      options += ",scale=" + scale
2635                  else:
2636                      options += "scale=" + scale
2637             if keepAspectRatio != '':
2638                  if options != '':
2639                      options += ",keepaspectratio"
2640                  else:
2641                      options += "keepaspectratio"
2642             if options != '':
2643                      options = '[' + options + ']'
2644             del document.body[i+1:j+1]
2645             document.body[i:i+1] = ['\\begin_inset ERT',
2646                                 'status collapsed',
2647                                 '',
2648                                 '\\begin_layout Standard',
2649                                 '',
2650                                 '\\backslash',
2651                                 'includepdf' + options + '{' + filename + '}',
2652                                 '\\end_layout',
2653                                 '',
2654                                 '\\end_inset']
2655             add_to_preamble(document, ['\\usepackage{pdfpages}\n'])
2656             i = i + 1
2657             continue
2658         i = i + 1
2659
2660
2661 def revert_mexican(document):
2662     ' Set language Spanish(Mexico) to Spanish '
2663     i = 0
2664     if document.language == "spanish-mexico":
2665         document.language = "spanish"
2666         i = find_token(document.header, "\\language", 0)
2667         if i != -1:
2668             document.header[i] = "\\language spanish"
2669     j = 0
2670     while True:
2671         j = find_token(document.body, "\\lang spanish-mexico", j)
2672         if j == -1:
2673             return
2674         document.body[j] = document.body[j].replace("\\lang spanish-mexico", "\\lang spanish")
2675         j = j + 1
2676
2677
2678 def remove_embedding(document):
2679     ' Remove embed tag from all insets '
2680     revert_inset_embedding(document, 'Graphics')
2681     revert_inset_embedding(document, 'External')
2682     revert_inset_embedding(document, 'CommandInset include')
2683     revert_inset_embedding(document, 'CommandInset bibtex')
2684
2685
2686 def revert_master(document):
2687     ' Remove master param '
2688     i = find_token(document.header, "\\master", 0)
2689     if i != -1:
2690         del document.header[i]
2691
2692
2693 def revert_graphics_group(document):
2694     ' Revert group information from graphics insets '
2695     i = 0
2696     while 1:
2697         i = find_token(document.body, "\\begin_inset Graphics", i)
2698         if i == -1:
2699             return
2700         j = find_end_of_inset(document.body, i)
2701         if j == -1:
2702             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_graphics_group.")
2703             i = i + 1
2704             continue
2705         k = find_token(document.body, " groupId", i, j)
2706         if k == -1:
2707             i = i + 1
2708             continue
2709         del document.body[k]
2710         i = i + 1
2711
2712
2713 def update_apa_styles(document):
2714     ' Replace obsolete styles '
2715
2716     if document.textclass != "apa":
2717         return
2718
2719     obsoletedby = { "Acknowledgments": "Acknowledgements",
2720                     "Section*":        "Section",
2721                     "Subsection*":     "Subsection",
2722                     "Subsubsection*":  "Subsubsection",
2723                     "Paragraph*":      "Paragraph",
2724                     "Subparagraph*":   "Subparagraph"}
2725     i = 0
2726     while 1:
2727         i = find_token(document.body, "\\begin_layout", i)
2728         if i == -1:
2729             return
2730
2731         layout = document.body[i][14:]
2732         if layout in obsoletedby:
2733             document.body[i] = "\\begin_layout " + obsoletedby[layout]
2734
2735         i += 1
2736
2737
2738 def convert_paper_sizes(document):
2739     ' exchange size options legalpaper and executivepaper to correct order '
2740     # routine is needed to fix http://bugzilla.lyx.org/show_bug.cgi?id=4868
2741     i = 0
2742     j = 0
2743     i = find_token(document.header, "\\papersize executivepaper", 0)
2744     if i != -1:
2745         document.header[i] = "\\papersize legalpaper"
2746         return
2747     j = find_token(document.header, "\\papersize legalpaper", 0)
2748     if j != -1:
2749         document.header[j] = "\\papersize executivepaper"
2750
2751
2752 def revert_paper_sizes(document):
2753     ' exchange size options legalpaper and executivepaper to correct order '
2754     i = 0
2755     j = 0
2756     i = find_token(document.header, "\\papersize executivepaper", 0)
2757     if i != -1:
2758         document.header[i] = "\\papersize legalpaper"
2759         return
2760     j = find_token(document.header, "\\papersize legalpaper", 0)
2761     if j != -1:
2762         document.header[j] = "\\papersize executivepaper"
2763
2764
2765 def convert_InsetSpace(document):
2766     " Convert '\\begin_inset Space foo' to '\\begin_inset space foo'"
2767     i = 0
2768     while True:
2769         i = find_token(document.body, "\\begin_inset Space", i)
2770         if i == -1:
2771             return
2772         document.body[i] = document.body[i].replace('\\begin_inset Space', '\\begin_inset space')
2773
2774
2775 def revert_InsetSpace(document):
2776     " Revert '\\begin_inset space foo' to '\\begin_inset Space foo'"
2777     i = 0
2778     while True:
2779         i = find_token(document.body, "\\begin_inset space", i)
2780         if i == -1:
2781             return
2782         document.body[i] = document.body[i].replace('\\begin_inset space', '\\begin_inset Space')
2783
2784
2785 def convert_display_enum(document):
2786     " Convert 'display foo' to 'display false/true'"
2787     i = 0
2788     while True:
2789         i = find_token(document.body, "\tdisplay", i)
2790         if i == -1:
2791             return
2792         val = get_value(document.body, 'display', i)
2793         if val == "none":
2794             document.body[i] = document.body[i].replace('none', 'false')
2795         if val == "default":
2796             document.body[i] = document.body[i].replace('default', 'true')
2797         if val == "monochrome":
2798             document.body[i] = document.body[i].replace('monochrome', 'true')
2799         if val == "grayscale":
2800             document.body[i] = document.body[i].replace('grayscale', 'true')
2801         if val == "color":
2802             document.body[i] = document.body[i].replace('color', 'true')
2803         if val == "preview":
2804             document.body[i] = document.body[i].replace('preview', 'true')
2805         i += 1
2806
2807
2808 def revert_display_enum(document):
2809     " Revert 'display false/true' to 'display none/color'"
2810     i = 0
2811     while True:
2812         i = find_token(document.body, "\tdisplay", i)
2813         if i == -1:
2814             return
2815         val = get_value(document.body, 'display', i)
2816         if val == "false":
2817             document.body[i] = document.body[i].replace('false', 'none')
2818         if val == "true":
2819             document.body[i] = document.body[i].replace('true', 'default')
2820         i += 1
2821
2822
2823 def remove_fontsCJK(document):
2824     ' Remove font_cjk param '
2825     i = find_token(document.header, "\\font_cjk", 0)
2826     if i != -1:
2827         del document.header[i]
2828
2829
2830 def convert_plain_layout(document):
2831     " Convert 'PlainLayout' to 'Plain Layout'"
2832     i = 0
2833     while True:
2834         i = find_token(document.body, '\\begin_layout PlainLayout', i)
2835         if i == -1:
2836             return
2837         document.body[i] = document.body[i].replace('\\begin_layout PlainLayout', \
2838           '\\begin_layout Plain Layout')
2839         i += 1
2840
2841
2842 def revert_plain_layout(document):
2843     " Convert 'PlainLayout' to 'Plain Layout'"
2844     i = 0
2845     while True:
2846         i = find_token(document.body, '\\begin_layout Plain Layout', i)
2847         if i == -1:
2848             return
2849         document.body[i] = document.body[i].replace('\\begin_layout Plain Layout', \
2850           '\\begin_layout PlainLayout')
2851         i += 1
2852
2853
2854 def revert_plainlayout(document):
2855     " Convert 'PlainLayout' to 'Plain Layout'"
2856     i = 0
2857     while True:
2858         i = find_token(document.body, '\\begin_layout PlainLayout', i)
2859         if i == -1:
2860             return
2861         # This will be incorrect for some document classes, since Standard is not always
2862         # the default. But (a) it is probably the best we can do and (b) it will actually
2863         # work, in fact, since an unknown layout will be converted to default.
2864         document.body[i] = document.body[i].replace('\\begin_layout PlainLayout', \
2865           '\\begin_layout Standard')
2866         i += 1
2867
2868
2869 def revert_polytonicgreek(document):
2870     "Set language polytonic Greek to Greek"
2871     i = 0
2872     if document.language == "polutonikogreek":
2873         document.language = "greek"
2874         i = find_token(document.header, "\\language", 0)
2875         if i != -1:
2876             document.header[i] = "\\language greek"
2877     j = 0
2878     while True:
2879         j = find_token(document.body, "\\lang polutonikogreek", j)
2880         if j == -1:
2881             return
2882         document.body[j] = document.body[j].replace("\\lang polutonikogreek", "\\lang greek")
2883         j = j + 1
2884
2885
2886 def revert_removed_modules(document):
2887     i = 0
2888     while True:
2889         i = find_token(document.header, "\\begin_remove_modules", i)
2890         if i == -1:
2891             return
2892         j = find_end_of(document.header, i, "\\begin_remove_modules", "\\end_remove_modules")
2893         if j == -1:
2894             # this should not happen
2895             break
2896         document.header[i : j + 1] = []
2897
2898
2899 def add_plain_layout(document):
2900     i = 0
2901     while True:
2902         i = find_token(document.body, "\\begin_layout", i)
2903         if i == -1:
2904             return
2905         if len(document.body[i].split()) == 1:
2906             document.body[i] = "\\begin_layout Plain Layout"
2907         i += 1
2908
2909
2910 def revert_tabulators(document):
2911     "Revert tabulators to 4 spaces"
2912     i = 0
2913     while True:
2914         i = find_token(document.body, "\t", i)
2915         if i == -1:
2916             return
2917         document.body[i] = document.body[i].replace("\t", "    ")
2918         i += 1
2919
2920
2921 def revert_tabsize(document):
2922     "Revert the tabsize parameter of listings"
2923     i = 0
2924     j = 0
2925     while True:
2926         # either it is the only parameter
2927         i = find_token(document.body, 'lstparams "tabsize=4"', i)
2928         if i != -1:
2929             del document.body[i]
2930         # or the last one
2931         j = find_token(document.body, "lstparams", j)
2932         if j == -1:
2933             return
2934         pos = document.body[j].find(",tabsize=")
2935         document.body[j] = document.body[j][:pos] + '"'
2936         i += 1
2937         j += 1
2938
2939
2940 def revert_mongolian(document):
2941     "Set language Mongolian to English"
2942     i = 0
2943     if document.language == "mongolian":
2944         document.language = "english"
2945         i = find_token(document.header, "\\language", 0)
2946         if i != -1:
2947             document.header[i] = "\\language english"
2948     j = 0
2949     while True:
2950         j = find_token(document.body, "\\lang mongolian", j)
2951         if j == -1:
2952             return
2953         document.body[j] = document.body[j].replace("\\lang mongolian", "\\lang english")
2954         j = j + 1
2955
2956
2957 ##
2958 # Conversion hub
2959 #
2960
2961 supported_versions = ["1.6.0","1.6"]
2962 convert = [[277, [fix_wrong_tables]],
2963            [278, [close_begin_deeper]],
2964            [279, [long_charstyle_names]],
2965            [280, [axe_show_label]],
2966            [281, []],
2967            [282, []],
2968            [283, [convert_flex]],
2969            [284, []],
2970            [285, []],
2971            [286, []],
2972            [287, [convert_wrapfig_options]],
2973            [288, [convert_inset_command]],
2974            [289, [convert_latexcommand_index]],
2975            [290, []],
2976            [291, []],
2977            [292, [convert_japanese_cjk]],
2978            [293, []],
2979            [294, [convert_pdf_options]],
2980            [295, [convert_htmlurl, convert_url]],
2981            [296, [convert_include]],
2982            [297, [convert_usorbian]],
2983            [298, []],
2984            [299, []],
2985            [300, []],
2986            [301, []],
2987            [302, []],
2988            [303, [convert_serbocroatian]],
2989            [304, [convert_framed_notes]],
2990            [305, []],
2991            [306, []],
2992            [307, []],
2993            [308, []],
2994            [309, []],
2995            [310, []],
2996            [311, [convert_ams_classes]],
2997            [312, []],
2998            [313, [convert_module_names]],
2999            [314, []],
3000            [315, []],
3001            [316, [convert_subfig]],
3002            [317, []],
3003            [318, []],
3004            [319, [convert_spaceinset, convert_hfill]],
3005            [320, []],
3006            [321, [convert_tablines]],
3007            [322, [convert_plain_layout]],
3008            [323, [convert_pagebreaks]],
3009            [324, [convert_linebreaks]],
3010            [325, [convert_japanese_plain]],
3011            [326, []],
3012            [327, []],
3013            [328, [remove_embedding, remove_extra_embedded_files, remove_inzip_options]],
3014            [329, []],
3015            [330, []],
3016            [331, [convert_ltcaption]],
3017            [332, []],
3018            [333, [update_apa_styles]],
3019            [334, [convert_paper_sizes]],
3020            [335, [convert_InsetSpace]],
3021            [336, []],
3022            [337, [convert_display_enum]],
3023            [338, []],
3024            [339, []],
3025            [340, [add_plain_layout]],
3026            [341, []],
3027            [342, []]
3028           ]
3029
3030 revert =  [[341, [revert_mongolian]],
3031            [340, [revert_tabulators, revert_tabsize]],
3032            [339, []],
3033            [338, [revert_removed_modules]],
3034            [337, [revert_polytonicgreek]],
3035            [336, [revert_display_enum]],
3036            [335, [remove_fontsCJK]],
3037            [334, [revert_InsetSpace]],
3038            [333, [revert_paper_sizes]],
3039            [332, []],
3040            [331, [revert_graphics_group]],
3041            [330, [revert_ltcaption]],
3042            [329, [revert_leftarrowfill, revert_rightarrowfill, revert_upbracefill, revert_downbracefill]],
3043            [328, [revert_master]],
3044            [327, []],
3045            [326, [revert_mexican]],
3046            [325, [revert_pdfpages]],
3047            [324, []],
3048            [323, [revert_linebreaks]],
3049            [322, [revert_pagebreaks]],
3050            [321, [revert_local_layout, revert_plain_layout]],
3051            [320, [revert_tablines]],
3052            [319, [revert_protected_hfill]],
3053            [318, [revert_spaceinset, revert_hfills, revert_hspace]],
3054            [317, [remove_extra_embedded_files]],
3055            [316, [revert_wrapplacement]],
3056            [315, [revert_subfig]],
3057            [314, [revert_colsep, revert_plainlayout]],
3058            [313, []],
3059            [312, [revert_module_names]],
3060            [311, [revert_rotfloat, revert_widesideways]],
3061            [310, [revert_external_embedding]],
3062            [309, [revert_btprintall]],
3063            [308, [revert_nocite]],
3064            [307, [revert_serbianlatin]],
3065            [306, [revert_slash, revert_nobreakdash]],
3066            [305, [revert_interlingua]],
3067            [304, [revert_bahasam]],
3068            [303, [revert_framed_notes]],
3069            [302, []],
3070            [301, [revert_latin, revert_samin]],
3071            [300, [revert_linebreak]],
3072            [299, [revert_pagebreak]],
3073            [298, [revert_hyperlinktype]],
3074            [297, [revert_macro_optional_params]],
3075            [296, [revert_albanian, revert_lowersorbian, revert_uppersorbian]],
3076            [295, [revert_include]],
3077            [294, [revert_href, revert_url]],
3078            [293, [revert_pdf_options_2]],
3079            [292, [revert_inset_info]],
3080            [291, [revert_japanese, revert_japanese_encoding, revert_japanese_cjk]],
3081            [290, [revert_vietnamese]],
3082            [289, [revert_wraptable]],
3083            [288, [revert_latexcommand_index]],
3084            [287, [revert_inset_command]],
3085            [286, [revert_wrapfig_options]],
3086            [285, [revert_pdf_options]],
3087            [284, [remove_inzip_options]],
3088            [283, []],
3089            [282, [revert_flex]],
3090            [281, []],
3091            [280, [revert_begin_modules]],
3092            [279, [revert_show_label]],
3093            [278, [revert_long_charstyle_names]],
3094            [277, []],
3095            [276, []]
3096           ]
3097
3098
3099 if __name__ == "__main__":
3100     pass