]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_1_6.py
* lyx_1_6.py (revert_wrapplacement):
[lyx.git] / lib / lyx2lyx / lyx_1_6.py
1 # This file is part of lyx2lyx
2 # -*- coding: utf-8 -*-
3 # Copyright (C) 2007-2008 The LyX Team <lyx-devel@lists.lyx.org>
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 """ Convert files to the file format generated by lyx 1.6"""
20
21 import re
22 import unicodedata
23 import sys, os
24
25 from parser_tools import find_token, find_end_of, find_tokens, get_value, get_value_string
26
27 ####################################################################
28 # Private helper functions
29
30 def find_end_of_inset(lines, i):
31     " Find end of inset, where lines[i] is included."
32     return find_end_of(lines, i, "\\begin_inset", "\\end_inset")
33
34 # WARNING!
35 # DO NOT do this:
36 #   document.body[i] = wrap_insert_ert(...)
37 # wrap_into_ert may returns a multiline string, which should NOT appear
38 # in document.body. Insetad, do something like this:
39 #   subst = wrap_inset_ert(...)
40 #   subst = subst.split('\n')
41 #   document.body[i:i+1] = subst
42 #   i+= len(subst) - 1
43 # where the last statement resets the counter to accord with the added
44 # lines.
45 def wrap_into_ert(string, src, dst):
46     '''Within string, replace occurrences of src with dst, wrapped into ERT
47        E.g.: wrap_into_ert('sch\"on', "\\", "\\backslash") is:
48        sch<ERT>\\backslash</ERT>"on'''
49     return string.replace(src, '\n\\begin_inset ERT\nstatus collapsed\n\\begin_layout Standard\n'
50       + dst + '\n\\end_layout\n\\end_inset\n')
51
52 def put_cmd_in_ert(string):
53     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|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         else:
2327             document.body[j] = "placement " + m.group(1).lower()
2328         i = j
2329
2330
2331 def remove_extra_embedded_files(document):
2332     " Remove \extra_embedded_files from buffer params "
2333     i = find_token(document.header, '\\extra_embedded_files', 0)
2334     if i == -1:
2335         return
2336     document.header.pop(i)
2337
2338
2339 def convert_spaceinset(document):
2340     " Convert '\\InsetSpace foo' to '\\begin_inset Space foo\n\\end_inset' "
2341     i = 0
2342     while i < len(document.body):
2343         m = re.match(r'(.*)\\InsetSpace (.*)', document.body[i])
2344         if m:
2345             before = m.group(1)
2346             after = m.group(2)
2347             subst = [before, "\\begin_inset Space " + after, "\\end_inset"]
2348             document.body[i: i+1] = subst
2349             i = i + len(subst)
2350         else:
2351             i = i + 1
2352
2353
2354 def revert_spaceinset(document):
2355     " Revert '\\begin_inset Space foo\n\\end_inset' to '\\InsetSpace foo' "
2356     i = 0
2357     while True:
2358         i = find_token(document.body, "\\begin_inset Space", i)
2359         if i == -1:
2360             return
2361         j = find_end_of_inset(document.body, i)
2362         if j == -1:
2363             document.warning("Malformed LyX document: Could not find end of space inset.")
2364             continue
2365         document.body[i] = document.body[i].replace('\\begin_inset Space', '\\InsetSpace')
2366         del document.body[j]
2367
2368
2369 def convert_hfill(document):
2370     " Convert hfill to space inset "
2371     i = 0
2372     while True:
2373         i = find_token(document.body, "\\hfill", i)
2374         if i == -1:
2375             return
2376         subst = document.body[i].replace('\\hfill', \
2377                   '\n\\begin_inset Space \\hfill{}\n\\end_inset')
2378         subst = subst.split('\n')
2379         document.body[i : i+1] = subst
2380         i += len(subst)
2381
2382
2383 def revert_hfills(document):
2384     ' Revert \\hfill commands '
2385     hfill = re.compile(r'\\hfill')
2386     dotfill = re.compile(r'\\dotfill')
2387     hrulefill = re.compile(r'\\hrulefill')
2388     i = 0
2389     while True:
2390         i = find_token(document.body, "\\InsetSpace", i)
2391         if i == -1:
2392             return
2393         if hfill.search(document.body[i]):
2394             document.body[i] = \
2395               document.body[i].replace('\\InsetSpace \\hfill{}', '\\hfill')
2396             i += 1
2397             continue
2398         if dotfill.search(document.body[i]):
2399             subst = document.body[i].replace('\\InsetSpace \\dotfill{}', \
2400               '\\begin_inset ERT\nstatus collapsed\n\n' \
2401               '\\begin_layout Standard\n\n\n\\backslash\n' \
2402               'dotfill{}\n\\end_layout\n\n\\end_inset\n\n')
2403             subst = subst.split('\n')
2404             document.body[i : i+1] = subst
2405             i += len(subst)
2406             continue
2407         if hrulefill.search(document.body[i]):
2408             subst = document.body[i].replace('\\InsetSpace \\hrulefill{}', \
2409               '\\begin_inset ERT\nstatus collapsed\n\n' \
2410               '\\begin_layout Standard\n\n\n\\backslash\n' \
2411               'hrulefill{}\n\\end_layout\n\n\\end_inset\n\n')
2412             subst = subst.split('\n')
2413             document.body[i : i+1] = subst
2414             i += len(subst)
2415             continue
2416         i += 1
2417
2418 def revert_hspace(document):
2419     ' Revert \\InsetSpace \\hspace{} to ERT '
2420     i = 0
2421     hspace = re.compile(r'\\hspace{}')
2422     hstar  = re.compile(r'\\hspace\*{}')
2423     while True:
2424         i = find_token(document.body, "\\InsetSpace \\hspace", i)
2425         if i == -1:
2426             return
2427         length = get_value(document.body, '\\length', i+1)
2428         if length == '':
2429             document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
2430             return
2431         del document.body[i+1]
2432         addedLines = -1
2433         if hstar.search(document.body[i]):
2434             subst = document.body[i].replace('\\InsetSpace \\hspace*{}', \
2435               '\\begin_inset ERT\nstatus collapsed\n\n' \
2436               '\\begin_layout Standard\n\n\n\\backslash\n' \
2437               'hspace*{' + length + '}\n\\end_layout\n\n\\end_inset\n\n')
2438             subst = subst.split('\n')
2439             document.body[i : i+1] = subst
2440             addedLines += len(subst) - 1
2441             i += addedLines + 1
2442             continue
2443         if hspace.search(document.body[i]):
2444             subst = document.body[i].replace('\\InsetSpace \\hspace{}', \
2445               '\\begin_inset ERT\nstatus collapsed\n\n' \
2446               '\\begin_layout Standard\n\n\n\\backslash\n' \
2447               'hspace{' + length + '}\n\\end_layout\n\n\\end_inset\n\n')
2448             subst = subst.split('\n')
2449             document.body[i : i+1] = subst
2450             addedLines += len(subst) - 1
2451             i += addedLines + 1
2452             continue
2453         i += 1
2454
2455
2456 def revert_protected_hfill(document):
2457     ' Revert \\begin_inset Space \\hspace*{\\fill} to ERT '
2458     i = 0
2459     while True:
2460         i = find_token(document.body, '\\begin_inset Space \\hspace*{\\fill}', i)
2461         if i == -1:
2462             return
2463         j = find_end_of_inset(document.body, i)
2464         if j == -1:
2465             document.warning("Malformed LyX document: Could not find end of space inset.")
2466             continue
2467         del document.body[j]
2468         subst = document.body[i].replace('\\begin_inset Space \\hspace*{\\fill}', \
2469           '\\begin_inset ERT\nstatus collapsed\n\n' \
2470           '\\begin_layout Standard\n\n\n\\backslash\n' \
2471           'hspace*{\n\\backslash\nfill}\n\\end_layout\n\n\\end_inset\n\n')
2472         subst = subst.split('\n')
2473         document.body[i : i+1] = subst
2474         i += len(subst)
2475
2476
2477 def revert_leftarrowfill(document):
2478     ' Revert \\begin_inset Space \\leftarrowfill{} to ERT '
2479     i = 0
2480     while True:
2481         i = find_token(document.body, '\\begin_inset Space \\leftarrowfill{}', i)
2482         if i == -1:
2483             return
2484         j = find_end_of_inset(document.body, i)
2485         if j == -1:
2486             document.warning("Malformed LyX document: Could not find end of space inset.")
2487             continue
2488         del document.body[j]
2489         subst = document.body[i].replace('\\begin_inset Space \\leftarrowfill{}', \
2490           '\\begin_inset ERT\nstatus collapsed\n\n' \
2491           '\\begin_layout Standard\n\n\n\\backslash\n' \
2492           'leftarrowfill{}\n\\end_layout\n\n\\end_inset\n\n')
2493         subst = subst.split('\n')
2494         document.body[i : i+1] = subst
2495         i += len(subst)
2496
2497
2498 def revert_rightarrowfill(document):
2499     ' Revert \\begin_inset Space \\rightarrowfill{} to ERT '
2500     i = 0
2501     while True:
2502         i = find_token(document.body, '\\begin_inset Space \\rightarrowfill{}', i)
2503         if i == -1:
2504             return
2505         j = find_end_of_inset(document.body, i)
2506         if j == -1:
2507             document.warning("Malformed LyX document: Could not find end of space inset.")
2508             continue
2509         del document.body[j]
2510         subst = document.body[i].replace('\\begin_inset Space \\rightarrowfill{}', \
2511           '\\begin_inset ERT\nstatus collapsed\n\n' \
2512           '\\begin_layout Standard\n\n\n\\backslash\n' \
2513           'rightarrowfill{}\n\\end_layout\n\n\\end_inset\n\n')
2514         subst = subst.split('\n')
2515         document.body[i : i+1] = subst
2516         i += len(subst)
2517
2518
2519 def revert_upbracefill(document):
2520     ' Revert \\begin_inset Space \\upbracefill{} to ERT '
2521     i = 0
2522     while True:
2523         i = find_token(document.body, '\\begin_inset Space \\upbracefill{}', i)
2524         if i == -1:
2525             return
2526         j = find_end_of_inset(document.body, i)
2527         if j == -1:
2528             document.warning("Malformed LyX document: Could not find end of space inset.")
2529             continue
2530         del document.body[j]
2531         subst = document.body[i].replace('\\begin_inset Space \\upbracefill{}', \
2532           '\\begin_inset ERT\nstatus collapsed\n\n' \
2533           '\\begin_layout Standard\n\n\n\\backslash\n' \
2534           'upbracefill{}\n\\end_layout\n\n\\end_inset\n\n')
2535         subst = subst.split('\n')
2536         document.body[i : i+1] = subst
2537         i += len(subst)
2538
2539
2540 def revert_downbracefill(document):
2541     ' Revert \\begin_inset Space \\downbracefill{} to ERT '
2542     i = 0
2543     while True:
2544         i = find_token(document.body, '\\begin_inset Space \\downbracefill{}', i)
2545         if i == -1:
2546             return
2547         j = find_end_of_inset(document.body, i)
2548         if j == -1:
2549             document.warning("Malformed LyX document: Could not find end of space inset.")
2550             continue
2551         del document.body[j]
2552         subst = document.body[i].replace('\\begin_inset Space \\downbracefill{}', \
2553           '\\begin_inset ERT\nstatus collapsed\n\n' \
2554           '\\begin_layout Standard\n\n\n\\backslash\n' \
2555           'downbracefill{}\n\\end_layout\n\n\\end_inset\n\n')
2556         subst = subst.split('\n')
2557         document.body[i : i+1] = subst
2558         i += len(subst)
2559
2560
2561 def revert_local_layout(document):
2562     ' Revert local layout headers.'
2563     i = 0
2564     while True:
2565         i = find_token(document.header, "\\begin_local_layout", i)
2566         if i == -1:
2567             return
2568         j = find_end_of(document.header, i, "\\begin_local_layout", "\\end_local_layout")
2569         if j == -1:
2570             # this should not happen
2571             break
2572         document.header[i : j + 1] = []
2573
2574
2575 def convert_pagebreaks(document):
2576     ' Convert inline Newpage insets to new format '
2577     i = 0
2578     while True:
2579         i = find_token(document.body, '\\newpage', i)
2580         if i == -1:
2581             break
2582         document.body[i:i+1] = ['\\begin_inset Newpage newpage',
2583                                 '\\end_inset']
2584     i = 0
2585     while True:
2586         i = find_token(document.body, '\\pagebreak', i)
2587         if i == -1:
2588             break
2589         document.body[i:i+1] = ['\\begin_inset Newpage pagebreak',
2590                                 '\\end_inset']
2591     i = 0
2592     while True:
2593         i = find_token(document.body, '\\clearpage', i)
2594         if i == -1:
2595             break
2596         document.body[i:i+1] = ['\\begin_inset Newpage clearpage',
2597                                 '\\end_inset']
2598     i = 0
2599     while True:
2600         i = find_token(document.body, '\\cleardoublepage', i)
2601         if i == -1:
2602             break
2603         document.body[i:i+1] = ['\\begin_inset Newpage cleardoublepage',
2604                                 '\\end_inset']
2605
2606
2607 def revert_pagebreaks(document):
2608     ' Revert \\begin_inset Newpage to previous inline format '
2609     i = 0
2610     while True:
2611         i = find_token(document.body, '\\begin_inset Newpage', i)
2612         if i == -1:
2613             return
2614         j = find_end_of_inset(document.body, i)
2615         if j == -1:
2616             document.warning("Malformed LyX document: Could not find end of Newpage inset.")
2617             continue
2618         del document.body[j]
2619         document.body[i] = document.body[i].replace('\\begin_inset Newpage newpage', '\\newpage')
2620         document.body[i] = document.body[i].replace('\\begin_inset Newpage pagebreak', '\\pagebreak')
2621         document.body[i] = document.body[i].replace('\\begin_inset Newpage clearpage', '\\clearpage')
2622         document.body[i] = document.body[i].replace('\\begin_inset Newpage cleardoublepage', '\\cleardoublepage')
2623
2624
2625 def convert_linebreaks(document):
2626     ' Convert inline Newline insets to new format '
2627     i = 0
2628     while True:
2629         i = find_token(document.body, '\\newline', i)
2630         if i == -1:
2631             break
2632         document.body[i:i+1] = ['\\begin_inset Newline newline',
2633                                 '\\end_inset']
2634     i = 0
2635     while True:
2636         i = find_token(document.body, '\\linebreak', i)
2637         if i == -1:
2638             break
2639         document.body[i:i+1] = ['\\begin_inset Newline linebreak',
2640                                 '\\end_inset']
2641
2642
2643 def revert_linebreaks(document):
2644     ' Revert \\begin_inset Newline to previous inline format '
2645     i = 0
2646     while True:
2647         i = find_token(document.body, '\\begin_inset Newline', i)
2648         if i == -1:
2649             return
2650         j = find_end_of_inset(document.body, i)
2651         if j == -1:
2652             document.warning("Malformed LyX document: Could not find end of Newline inset.")
2653             continue
2654         del document.body[j]
2655         document.body[i] = document.body[i].replace('\\begin_inset Newline newline', '\\newline')
2656         document.body[i] = document.body[i].replace('\\begin_inset Newline linebreak', '\\linebreak')
2657
2658
2659 def convert_japanese_plain(document):
2660     ' Set language japanese-plain to japanese '
2661     i = 0
2662     if document.language == "japanese-plain":
2663         document.language = "japanese"
2664         i = find_token(document.header, "\\language", 0)
2665         if i != -1:
2666             document.header[i] = "\\language japanese"
2667     j = 0
2668     while True:
2669         j = find_token(document.body, "\\lang japanese-plain", j)
2670         if j == -1:
2671             return
2672         document.body[j] = document.body[j].replace("\\lang japanese-plain", "\\lang japanese")
2673         j = j + 1
2674
2675
2676 def revert_pdfpages(document):
2677     ' Revert pdfpages external inset to ERT '
2678     i = 0
2679     while 1:
2680         i = find_token(document.body, "\\begin_inset External", i)
2681         if i == -1:
2682             return
2683         j = find_end_of_inset(document.body, i)
2684         if j == -1:
2685             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_pdfpages.")
2686             i = i + 1
2687             continue
2688         if get_value(document.body, 'template', i, j) == "PDFPages":
2689             filename = get_value(document.body, 'filename', i, j)
2690             extra = ''
2691             r = re.compile(r'\textra PDFLaTeX \"(.*)\"$')
2692             for k in range(i, j):
2693                 m = r.match(document.body[k])
2694                 if m:
2695                     extra = m.group(1)
2696             angle = get_value(document.body, 'rotateAngle', i, j)
2697             width = get_value(document.body, 'width', i, j)
2698             height = get_value(document.body, 'height', i, j)
2699             scale = get_value(document.body, 'scale', i, j)
2700             keepAspectRatio = find_token(document.body, "\tkeepAspectRatio", i, j)
2701             options = extra
2702             if angle != '':
2703                  if options != '':
2704                      options += ",angle=" + angle
2705                  else:
2706                      options += "angle=" + angle
2707             if width != '':
2708                  if options != '':
2709                      options += ",width=" + convert_len(width)
2710                  else:
2711                      options += "width=" + convert_len(width)
2712             if height != '':
2713                  if options != '':
2714                      options += ",height=" + convert_len(height)
2715                  else:
2716                      options += "height=" + convert_len(height)
2717             if scale != '':
2718                  if options != '':
2719                      options += ",scale=" + scale
2720                  else:
2721                      options += "scale=" + scale
2722             if keepAspectRatio != '':
2723                  if options != '':
2724                      options += ",keepaspectratio"
2725                  else:
2726                      options += "keepaspectratio"
2727             if options != '':
2728                      options = '[' + options + ']'
2729             del document.body[i+1:j+1]
2730             document.body[i:i+1] = ['\\begin_inset ERT',
2731                                 'status collapsed',
2732                                 '',
2733                                 '\\begin_layout Standard',
2734                                 '',
2735                                 '\\backslash',
2736                                 'includepdf' + options + '{' + filename + '}',
2737                                 '\\end_layout',
2738                                 '',
2739                                 '\\end_inset']
2740             add_to_preamble(document, ['\\usepackage{pdfpages}\n'])
2741             i = i + 1
2742             continue
2743         i = i + 1
2744
2745
2746 def revert_mexican(document):
2747     ' Set language Spanish(Mexico) to Spanish '
2748     i = 0
2749     if document.language == "spanish-mexico":
2750         document.language = "spanish"
2751         i = find_token(document.header, "\\language", 0)
2752         if i != -1:
2753             document.header[i] = "\\language spanish"
2754     j = 0
2755     while True:
2756         j = find_token(document.body, "\\lang spanish-mexico", j)
2757         if j == -1:
2758             return
2759         document.body[j] = document.body[j].replace("\\lang spanish-mexico", "\\lang spanish")
2760         j = j + 1
2761
2762
2763 def remove_embedding(document):
2764     ' Remove embed tag from all insets '
2765     revert_inset_embedding(document, 'Graphics')
2766     revert_inset_embedding(document, 'External')
2767     revert_inset_embedding(document, 'CommandInset include')
2768     revert_inset_embedding(document, 'CommandInset bibtex')
2769
2770
2771 def revert_master(document):
2772     ' Remove master param '
2773     i = find_token(document.header, "\\master", 0)
2774     if i != -1:
2775         del document.header[i]
2776
2777
2778 def revert_graphics_group(document):
2779     ' Revert group information from graphics insets '
2780     i = 0
2781     while 1:
2782         i = find_token(document.body, "\\begin_inset Graphics", i)
2783         if i == -1:
2784             return
2785         j = find_end_of_inset(document.body, i)
2786         if j == -1:
2787             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_graphics_group.")
2788             i = i + 1
2789             continue
2790         k = find_token(document.body, " groupId", i, j)
2791         if k == -1:
2792             i = i + 1
2793             continue
2794         del document.body[k]
2795         i = i + 1
2796
2797
2798 def update_apa_styles(document):
2799     ' Replace obsolete styles '
2800
2801     if document.textclass != "apa":
2802         return
2803
2804     obsoletedby = { "Acknowledgments": "Acknowledgements",
2805                     "Section*":        "Section",
2806                     "Subsection*":     "Subsection",
2807                     "Subsubsection*":  "Subsubsection",
2808                     "Paragraph*":      "Paragraph",
2809                     "Subparagraph*":   "Subparagraph"}
2810     i = 0
2811     while 1:
2812         i = find_token(document.body, "\\begin_layout", i)
2813         if i == -1:
2814             return
2815
2816         layout = document.body[i][14:]
2817         if layout in obsoletedby:
2818             document.body[i] = "\\begin_layout " + obsoletedby[layout]
2819
2820         i += 1
2821
2822
2823 def convert_paper_sizes(document):
2824     ' exchange size options legalpaper and executivepaper to correct order '
2825     # routine is needed to fix http://bugzilla.lyx.org/show_bug.cgi?id=4868
2826     i = 0
2827     j = 0
2828     i = find_token(document.header, "\\papersize executivepaper", 0)
2829     if i != -1:
2830         document.header[i] = "\\papersize legalpaper"
2831         return
2832     j = find_token(document.header, "\\papersize legalpaper", 0)
2833     if j != -1:
2834         document.header[j] = "\\papersize executivepaper"
2835
2836
2837 def revert_paper_sizes(document):
2838     ' exchange size options legalpaper and executivepaper to correct order '
2839     i = 0
2840     j = 0
2841     i = find_token(document.header, "\\papersize executivepaper", 0)
2842     if i != -1:
2843         document.header[i] = "\\papersize legalpaper"
2844         return
2845     j = find_token(document.header, "\\papersize legalpaper", 0)
2846     if j != -1:
2847         document.header[j] = "\\papersize executivepaper"
2848
2849
2850 def convert_InsetSpace(document):
2851     " Convert '\\begin_inset Space foo' to '\\begin_inset space foo'"
2852     i = 0
2853     while True:
2854         i = find_token(document.body, "\\begin_inset Space", i)
2855         if i == -1:
2856             return
2857         document.body[i] = document.body[i].replace('\\begin_inset Space', '\\begin_inset space')
2858
2859
2860 def revert_InsetSpace(document):
2861     " Revert '\\begin_inset space foo' to '\\begin_inset Space foo'"
2862     i = 0
2863     while True:
2864         i = find_token(document.body, "\\begin_inset space", i)
2865         if i == -1:
2866             return
2867         document.body[i] = document.body[i].replace('\\begin_inset space', '\\begin_inset Space')
2868
2869
2870 def convert_display_enum(document):
2871     " Convert 'display foo' to 'display false/true'"
2872     i = 0
2873     while True:
2874         i = find_token(document.body, "\tdisplay", i)
2875         if i == -1:
2876             return
2877         val = get_value(document.body, 'display', i)
2878         if val == "none":
2879             document.body[i] = document.body[i].replace('none', 'false')
2880         if val == "default":
2881             document.body[i] = document.body[i].replace('default', 'true')
2882         if val == "monochrome":
2883             document.body[i] = document.body[i].replace('monochrome', 'true')
2884         if val == "grayscale":
2885             document.body[i] = document.body[i].replace('grayscale', 'true')
2886         if val == "color":
2887             document.body[i] = document.body[i].replace('color', 'true')
2888         if val == "preview":
2889             document.body[i] = document.body[i].replace('preview', 'true')
2890         i += 1
2891
2892
2893 def revert_display_enum(document):
2894     " Revert 'display false/true' to 'display none/color'"
2895     i = 0
2896     while True:
2897         i = find_token(document.body, "\tdisplay", i)
2898         if i == -1:
2899             return
2900         val = get_value(document.body, 'display', i)
2901         if val == "false":
2902             document.body[i] = document.body[i].replace('false', 'none')
2903         if val == "true":
2904             document.body[i] = document.body[i].replace('true', 'default')
2905         i += 1
2906
2907
2908 def remove_fontsCJK(document):
2909     ' Remove font_cjk param '
2910     i = find_token(document.header, "\\font_cjk", 0)
2911     if i != -1:
2912         del document.header[i]
2913
2914
2915 def convert_plain_layout(document):
2916     " Convert 'PlainLayout' to 'Plain Layout'"
2917     i = 0
2918     while True:
2919         i = find_token(document.body, '\\begin_layout PlainLayout', i)
2920         if i == -1:
2921             return
2922         document.body[i] = document.body[i].replace('\\begin_layout PlainLayout', \
2923           '\\begin_layout Plain Layout')
2924         i += 1
2925
2926
2927 def revert_plain_layout(document):
2928     " Convert 'PlainLayout' to 'Plain Layout'"
2929     i = 0
2930     while True:
2931         i = find_token(document.body, '\\begin_layout Plain Layout', i)
2932         if i == -1:
2933             return
2934         document.body[i] = document.body[i].replace('\\begin_layout Plain Layout', \
2935           '\\begin_layout PlainLayout')
2936         i += 1
2937
2938
2939 def revert_plainlayout(document):
2940     " Convert 'PlainLayout' to 'Plain Layout'"
2941     i = 0
2942     while True:
2943         i = find_token(document.body, '\\begin_layout PlainLayout', i)
2944         if i == -1:
2945             return
2946         # This will be incorrect for some document classes, since Standard is not always
2947         # the default. But (a) it is probably the best we can do and (b) it will actually
2948         # work, in fact, since an unknown layout will be converted to default.
2949         document.body[i] = document.body[i].replace('\\begin_layout PlainLayout', \
2950           '\\begin_layout Standard')
2951         i += 1
2952
2953
2954 def revert_polytonicgreek(document):
2955     "Set language polytonic Greek to Greek"
2956     i = 0
2957     if document.language == "polutonikogreek":
2958         document.language = "greek"
2959         i = find_token(document.header, "\\language", 0)
2960         if i != -1:
2961             document.header[i] = "\\language greek"
2962     j = 0
2963     while True:
2964         j = find_token(document.body, "\\lang polutonikogreek", j)
2965         if j == -1:
2966             return
2967         document.body[j] = document.body[j].replace("\\lang polutonikogreek", "\\lang greek")
2968         j = j + 1
2969
2970
2971 def revert_removed_modules(document):
2972     i = 0
2973     while True:
2974         i = find_token(document.header, "\\begin_remove_modules", i)
2975         if i == -1:
2976             return
2977         j = find_end_of(document.header, i, "\\begin_remove_modules", "\\end_remove_modules")
2978         if j == -1:
2979             # this should not happen
2980             break
2981         document.header[i : j + 1] = []
2982
2983
2984 def add_plain_layout(document):
2985     i = 0
2986     while True:
2987         i = find_token(document.body, "\\begin_layout", i)
2988         if i == -1:
2989             return
2990         if len(document.body[i].split()) == 1:
2991             document.body[i] = "\\begin_layout Plain Layout"
2992         i += 1
2993
2994
2995 def revert_tabulators(document):
2996     "Revert tabulators to 4 spaces"
2997     i = 0
2998     while True:
2999         i = find_token(document.body, "\t", i)
3000         if i == -1:
3001             return
3002         document.body[i] = document.body[i].replace("\t", "    ")
3003         i += 1
3004
3005
3006 def revert_tabsize(document):
3007     "Revert the tabsize parameter of listings"
3008     i = 0
3009     j = 0
3010     while True:
3011         # either it is the only parameter
3012         i = find_token(document.body, 'lstparams "tabsize=4"', i)
3013         if i != -1:
3014             del document.body[i]
3015         # or the last one
3016         j = find_token(document.body, "lstparams", j)
3017         if j == -1:
3018             return
3019         pos = document.body[j].find(",tabsize=")
3020         document.body[j] = document.body[j][:pos] + '"'
3021         i += 1
3022         j += 1
3023
3024
3025 def revert_mongolian(document):
3026     "Set language Mongolian to English"
3027     i = 0
3028     if document.language == "mongolian":
3029         document.language = "english"
3030         i = find_token(document.header, "\\language", 0)
3031         if i != -1:
3032             document.header[i] = "\\language english"
3033     j = 0
3034     while True:
3035         j = find_token(document.body, "\\lang mongolian", j)
3036         if j == -1:
3037             return
3038         document.body[j] = document.body[j].replace("\\lang mongolian", "\\lang english")
3039         j = j + 1
3040
3041
3042 def revert_default_options(document):
3043     ' Remove param use_default_options '
3044     i = find_token(document.header, "\\use_default_options", 0)
3045     if i != -1:
3046         del document.header[i]
3047
3048
3049 def convert_default_options(document):
3050     ' Add param use_default_options and set it to false '
3051     i = find_token(document.header, "\\textclass", 0)
3052     if i == -1:
3053         document.warning("Malformed LyX document: Missing `\\textclass'.")
3054         return
3055     document.header.insert(i, '\\use_default_options false')
3056
3057
3058 def revert_backref_options(document):
3059     ' Revert option pdf_backref=page to pagebackref '
3060     i = find_token(document.header, "\\pdf_backref page", 0)
3061     if i != -1:
3062         document.header[i] = "\\pdf_pagebackref true"
3063
3064
3065 def convert_backref_options(document):
3066     ' We have changed the option pagebackref to backref=true '
3067     i = find_token(document.header, "\\pdf_pagebackref true", 0)
3068     if i != -1:
3069         document.header[i] = "\\pdf_backref page"
3070     j = find_token(document.header, "\\pdf_pagebackref false", 0)
3071     if j != -1:
3072         del document.header[j]
3073     # backref=true was not a valid option, we meant backref=section
3074     k = find_token(document.header, "\\pdf_backref true", 0)
3075     if k != -1 and i != -1:
3076         del document.header[k]
3077     elif k != -1 and j != -1:
3078         document.header[k] = "\\pdf_backref section"
3079
3080
3081 def convert_charstyle_element(document):
3082     "Convert CharStyle to Element for docbook backend"
3083     if document.backend != "docbook":
3084         return
3085     i = 0
3086     while True:
3087         i = find_token(document.body, "\\begin_inset Flex CharStyle:", i)
3088         if i == -1:
3089             return
3090         document.body[i] = document.body[i].replace('\\begin_inset Flex CharStyle:',
3091                                                     '\\begin_inset Flex Element:')
3092
3093 def revert_charstyle_element(document):
3094     "Convert Element to CharStyle for docbook backend"
3095     if document.backend != "docbook":
3096         return
3097     i = 0
3098     while True:
3099         i = find_token(document.body, "\\begin_inset Flex Element:", i)
3100         if i == -1:
3101             return
3102         document.body[i] = document.body[i].replace('\\begin_inset Flex Element:',
3103                                                     '\\begin_inset Flex CharStyle:')
3104
3105 ##
3106 # Conversion hub
3107 #
3108
3109 supported_versions = ["1.6.0","1.6"]
3110 convert = [[277, [fix_wrong_tables]],
3111            [278, [close_begin_deeper]],
3112            [279, [long_charstyle_names]],
3113            [280, [axe_show_label]],
3114            [281, []],
3115            [282, []],
3116            [283, [convert_flex]],
3117            [284, []],
3118            [285, []],
3119            [286, []],
3120            [287, [convert_wrapfig_options]],
3121            [288, [convert_inset_command]],
3122            [289, [convert_latexcommand_index]],
3123            [290, []],
3124            [291, []],
3125            [292, [convert_japanese_cjk]],
3126            [293, []],
3127            [294, [convert_pdf_options]],
3128            [295, [convert_htmlurl, convert_url]],
3129            [296, [convert_include]],
3130            [297, [convert_usorbian]],
3131            [298, [convert_macro_global]],
3132            [299, []],
3133            [300, []],
3134            [301, []],
3135            [302, []],
3136            [303, [convert_serbocroatian]],
3137            [304, [convert_framed_notes]],
3138            [305, []],
3139            [306, []],
3140            [307, []],
3141            [308, []],
3142            [309, []],
3143            [310, []],
3144            [311, [convert_ams_classes]],
3145            [312, []],
3146            [313, [convert_module_names]],
3147            [314, []],
3148            [315, []],
3149            [316, [convert_subfig]],
3150            [317, []],
3151            [318, []],
3152            [319, [convert_spaceinset, convert_hfill]],
3153            [320, []],
3154            [321, [convert_tablines]],
3155            [322, [convert_plain_layout]],
3156            [323, [convert_pagebreaks]],
3157            [324, [convert_linebreaks]],
3158            [325, [convert_japanese_plain]],
3159            [326, []],
3160            [327, []],
3161            [328, [remove_embedding, remove_extra_embedded_files, remove_inzip_options]],
3162            [329, []],
3163            [330, []],
3164            [331, [convert_ltcaption]],
3165            [332, []],
3166            [333, [update_apa_styles]],
3167            [334, [convert_paper_sizes]],
3168            [335, [convert_InsetSpace]],
3169            [336, []],
3170            [337, [convert_display_enum]],
3171            [338, []],
3172            [339, []],
3173            [340, [add_plain_layout]],
3174            [341, []],
3175            [342, []],
3176            [343, [convert_default_options]],
3177            [344, [convert_backref_options]],
3178            [345, [convert_charstyle_element]]
3179           ]
3180
3181 revert =  [[344, [revert_charstyle_element]],
3182            [343, [revert_backref_options]],
3183            [342, [revert_default_options]],
3184            [341, [revert_mongolian]],
3185            [340, [revert_tabulators, revert_tabsize]],
3186            [339, []],
3187            [338, [revert_removed_modules]],
3188            [337, [revert_polytonicgreek]],
3189            [336, [revert_display_enum]],
3190            [335, [remove_fontsCJK]],
3191            [334, [revert_InsetSpace]],
3192            [333, [revert_paper_sizes]],
3193            [332, []],
3194            [331, [revert_graphics_group]],
3195            [330, [revert_ltcaption]],
3196            [329, [revert_leftarrowfill, revert_rightarrowfill, revert_upbracefill, revert_downbracefill]],
3197            [328, [revert_master]],
3198            [327, []],
3199            [326, [revert_mexican]],
3200            [325, [revert_pdfpages]],
3201            [324, []],
3202            [323, [revert_linebreaks]],
3203            [322, [revert_pagebreaks]],
3204            [321, [revert_local_layout, revert_plain_layout]],
3205            [320, [revert_tablines]],
3206            [319, [revert_protected_hfill]],
3207            [318, [revert_spaceinset, revert_hfills, revert_hspace]],
3208            [317, [remove_extra_embedded_files]],
3209            [316, [revert_wrapplacement]],
3210            [315, [revert_subfig]],
3211            [314, [revert_colsep, revert_plainlayout]],
3212            [313, []],
3213            [312, [revert_module_names]],
3214            [311, [revert_rotfloat, revert_widesideways]],
3215            [310, [revert_external_embedding]],
3216            [309, [revert_btprintall]],
3217            [308, [revert_nocite]],
3218            [307, [revert_serbianlatin]],
3219            [306, [revert_slash, revert_nobreakdash]],
3220            [305, [revert_interlingua]],
3221            [304, [revert_bahasam]],
3222            [303, [revert_framed_notes]],
3223            [302, []],
3224            [301, [revert_latin, revert_samin]],
3225            [300, [revert_linebreak]],
3226            [299, [revert_pagebreak]],
3227            [298, [revert_hyperlinktype]],
3228            [297, [revert_macro_optional_params]],
3229            [296, [revert_albanian, revert_lowersorbian, revert_uppersorbian]],
3230            [295, [revert_include]],
3231            [294, [revert_href, revert_url]],
3232            [293, [revert_pdf_options_2]],
3233            [292, [revert_inset_info]],
3234            [291, [revert_japanese, revert_japanese_encoding, revert_japanese_cjk]],
3235            [290, [revert_vietnamese]],
3236            [289, [revert_wraptable]],
3237            [288, [revert_latexcommand_index]],
3238            [287, [revert_inset_command]],
3239            [286, [revert_wrapfig_options]],
3240            [285, [revert_pdf_options]],
3241            [284, [remove_inzip_options]],
3242            [283, []],
3243            [282, [revert_flex]],
3244            [281, []],
3245            [280, [revert_begin_modules]],
3246            [279, [revert_show_label]],
3247            [278, [revert_long_charstyle_names]],
3248            [277, []],
3249            [276, []]
3250           ]
3251
3252
3253 if __name__ == "__main__":
3254     pass