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