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