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