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