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