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