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