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