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