]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_1_6.py
e6bf42515030a468efc74e7c813daacdfe8269b1
[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             i += 1
1080             continue
1081         j = find_end_of_inset(document.body, i + 2)
1082         if j == -1:
1083             document.warning("Unable to find end of index inset at line " + i + "!")
1084             i += 2
1085             continue
1086         m = r1.match(document.body[i + 2])
1087         if m == None:
1088             document.warning("Unable to match: " + document.body[i+2])
1089             # this can happen with empty index insets!
1090             linelist = [""]
1091         else:
1092             fullcontent = m.group(1)
1093             linelist = latex2lyx(fullcontent)
1094         #document.warning(fullcontent)
1095
1096         linelist = ["\\begin_inset Index", "status collapsed", "\\begin_layout Standard", ""] + \
1097                    linelist + ["\\end_layout"]
1098         document.body[i : j] = linelist
1099         i += len(linelist) - (j - i)
1100
1101
1102 def revert_latexcommand_index(document):
1103     "Revert from collapsable form to LatexCommand form."
1104     i = 0
1105     while True:
1106         i = find_token(document.body, "\\begin_inset Index", i)
1107         if i == -1:
1108           return
1109         j = find_end_of_inset(document.body, i + 1)
1110         if j == -1:
1111           return
1112
1113         content = lyx2latex(document, document.body[i:j])
1114         # escape quotes
1115         content = content.replace('"', r'\"')
1116         document.body[i:j] = ["\\begin_inset CommandInset index", "LatexCommand index",
1117             "name " + '"' + content + '"', ""]
1118         i += 5
1119
1120
1121 def revert_wraptable(document):
1122     "Revert wrap table to wrap figure."
1123     i = 0
1124     while True:
1125         i = find_token(document.body, "\\begin_inset Wrap table", i)
1126         if i == -1:
1127             return
1128         document.body[i] = document.body[i].replace('\\begin_inset Wrap table', '\\begin_inset Wrap figure')
1129         i = i + 1
1130
1131
1132 def revert_vietnamese(document):
1133     "Set language Vietnamese to English"
1134     # Set document language from Vietnamese to English
1135     i = 0
1136     if document.language == "vietnamese":
1137         document.language = "english"
1138         i = find_token(document.header, "\\language", 0)
1139         if i != -1:
1140             document.header[i] = "\\language english"
1141     j = 0
1142     while True:
1143         j = find_token(document.body, "\\lang vietnamese", j)
1144         if j == -1:
1145             return
1146         document.body[j] = document.body[j].replace("\\lang vietnamese", "\\lang english")
1147         j = j + 1
1148
1149
1150 def convert_japanese_cjk(document):
1151     "Set language japanese to japanese-cjk"
1152     # Set document language from japanese-plain to japanese
1153     i = 0
1154     if document.language == "japanese":
1155         document.language = "japanese-cjk"
1156         i = find_token(document.header, "\\language", 0)
1157         if i != -1:
1158             document.header[i] = "\\language japanese-cjk"
1159     j = 0
1160     while True:
1161         j = find_token(document.body, "\\lang japanese", j)
1162         if j == -1:
1163             return
1164         document.body[j] = document.body[j].replace("\\lang japanese", "\\lang japanese-cjk")
1165         j = j + 1
1166
1167
1168 def revert_japanese(document):
1169     "Set language japanese-plain to japanese"
1170     # Set document language from japanese-plain to japanese
1171     i = 0
1172     if document.language == "japanese-plain":
1173         document.language = "japanese"
1174         i = find_token(document.header, "\\language", 0)
1175         if i != -1:
1176             document.header[i] = "\\language japanese"
1177     j = 0
1178     while True:
1179         j = find_token(document.body, "\\lang japanese-plain", j)
1180         if j == -1:
1181             return
1182         document.body[j] = document.body[j].replace("\\lang japanese-plain", "\\lang japanese")
1183         j = j + 1
1184
1185
1186 def revert_japanese_cjk(document):
1187     "Set language japanese-cjk to japanese"
1188     # Set document language from japanese-plain to japanese
1189     i = 0
1190     if document.language == "japanese-cjk":
1191         document.language = "japanese"
1192         i = find_token(document.header, "\\language", 0)
1193         if i != -1:
1194             document.header[i] = "\\language japanese"
1195     j = 0
1196     while True:
1197         j = find_token(document.body, "\\lang japanese-cjk", j)
1198         if j == -1:
1199             return
1200         document.body[j] = document.body[j].replace("\\lang japanese-cjk", "\\lang japanese")
1201         j = j + 1
1202
1203
1204 def revert_japanese_encoding(document):
1205     "Set input encoding form EUC-JP-plain to EUC-JP etc."
1206     # Set input encoding form EUC-JP-plain to EUC-JP etc.
1207     i = 0
1208     i = find_token(document.header, "\\inputencoding EUC-JP-plain", 0)
1209     if i != -1:
1210         document.header[i] = "\\inputencoding EUC-JP"
1211     j = 0
1212     j = find_token(document.header, "\\inputencoding JIS-plain", 0)
1213     if j != -1:
1214         document.header[j] = "\\inputencoding JIS"
1215     k = 0
1216     k = find_token(document.header, "\\inputencoding SJIS-plain", 0)
1217     if k != -1: # convert to UTF8 since there is currently no SJIS encoding
1218         document.header[k] = "\\inputencoding UTF8"
1219
1220
1221 def revert_inset_info(document):
1222     'Replace info inset with its content'
1223     i = 0
1224     while 1:
1225         i = find_token(document.body, '\\begin_inset Info', i)
1226         if i == -1:
1227             return
1228         j = find_end_of_inset(document.body, i + 1)
1229         if j == -1:
1230             # should not happen
1231             document.warning("Malformed LyX document: Could not find end of Info inset.")
1232         type = 'unknown'
1233         arg = ''
1234         for k in range(i, j+1):
1235             if document.body[k].startswith("arg"):
1236                 arg = document.body[k][3:].strip()
1237                 # remove embracing quotation marks
1238                 if arg[0] == '"':
1239                     arg = arg[1:]
1240                 if arg[len(arg) - 1] == '"':
1241                     arg = arg[:len(arg) - 1]
1242                 # \" to straight quote
1243                 arg = arg.replace(r'\"','"')
1244             if document.body[k].startswith("type"):
1245                 type = document.body[k][4:].strip().strip('"')
1246         # I think there is a newline after \\end_inset, which should be removed.
1247         if document.body[j + 1].strip() == "":
1248             document.body[i : (j + 2)] = [type + ':' + arg]
1249         else:
1250             document.body[i : (j + 1)] = [type + ':' + arg]
1251
1252
1253 def convert_pdf_options(document):
1254     # Set the pdfusetitle tag, delete the pdf_store_options,
1255     # set quotes for bookmarksopenlevel"
1256     has_hr = get_value(document.header, "\\use_hyperref", 0, default = "0")
1257     if has_hr == "1":
1258         k = find_token(document.header, "\\use_hyperref", 0)
1259         document.header.insert(k + 1, "\\pdf_pdfusetitle true")
1260     k = find_token(document.header, "\\pdf_store_options", 0)
1261     if k != -1:
1262         del document.header[k]
1263     i = find_token(document.header, "\\pdf_bookmarksopenlevel", k)
1264     if i == -1: return
1265     document.header[i] = document.header[i].replace('"', '')
1266
1267
1268 def revert_pdf_options_2(document):
1269     # reset the pdfusetitle tag, set quotes for bookmarksopenlevel"
1270     k = find_token(document.header, "\\use_hyperref", 0)
1271     i = find_token(document.header, "\\pdf_pdfusetitle", k)
1272     if i != -1:
1273         del document.header[i]
1274     i = find_token(document.header, "\\pdf_bookmarksopenlevel", k)
1275     if i == -1: return
1276     values = document.header[i].split()
1277     values[1] = ' "' + values[1] + '"'
1278     document.header[i] = ''.join(values)
1279
1280
1281 def convert_htmlurl(document):
1282     'Convert "htmlurl" to "href" insets for docbook'
1283     if document.backend != "docbook":
1284       return
1285     i = 0
1286     while True:
1287       i = find_token(document.body, "\\begin_inset CommandInset url", i)
1288       if i == -1:
1289         return
1290       document.body[i] = "\\begin_inset CommandInset href"
1291       document.body[i + 1] = "LatexCommand href"
1292       i = i + 1
1293
1294
1295 def convert_url(document):
1296     'Convert url insets to url charstyles'
1297     if document.backend == "docbook":
1298       return
1299     i = 0
1300     while True:
1301       i = find_token(document.body, "\\begin_inset CommandInset url", i)
1302       if i == -1:
1303         break
1304       n = find_token(document.body, "name", i)
1305       if n == i + 2:
1306         # place the URL name in typewriter before the new URL insert
1307         # grab the name 'bla' from the e.g. the line 'name "bla"',
1308         # therefore start with the 6th character
1309         name = document.body[n][6:-1]
1310         newname = [name + " "]
1311         document.body[i:i] = newname
1312         i = i + 1
1313       j = find_token(document.body, "target", i)
1314       if j == -1:
1315         document.warning("Malformed LyX document: Can't find target for url inset")
1316         i = j
1317         continue
1318       target = document.body[j][8:-1]
1319       k = find_token(document.body, "\\end_inset", j)
1320       if k == -1:
1321         document.warning("Malformed LyX document: Can't find end of url inset")
1322         i = k
1323         continue
1324       newstuff = ["\\begin_inset Flex URL",
1325         "status collapsed", "",
1326         "\\begin_layout Standard",
1327         "",
1328         target,
1329         "\\end_layout",
1330         ""]
1331       document.body[i:k] = newstuff
1332       i = k
1333
1334 def convert_ams_classes(document):
1335   tc = document.textclass
1336   if (tc != "amsart" and tc != "amsart-plain" and
1337       tc != "amsart-seq" and tc != "amsbook"):
1338     return
1339   if tc == "amsart-plain":
1340     document.textclass = "amsart"
1341     document.set_textclass()
1342     document.add_module("Theorems (Starred)")
1343     return
1344   if tc == "amsart-seq":
1345     document.textclass = "amsart"
1346     document.set_textclass()
1347   document.add_module("Theorems (AMS)")
1348
1349   #Now we want to see if any of the environments in the extended theorems
1350   #module were used in this document. If so, we'll add that module, too.
1351   layouts = ["Criterion", "Algorithm", "Axiom", "Condition", "Note",  \
1352     "Notation", "Summary", "Acknowledgement", "Conclusion", "Fact", \
1353     "Assumption"]
1354
1355   r = re.compile(r'^\\begin_layout (.*?)\*?\s*$')
1356   i = 0
1357   while True:
1358     i = find_token(document.body, "\\begin_layout", i)
1359     if i == -1:
1360       return
1361     m = r.match(document.body[i])
1362     if m == None:
1363       # This is an empty layout
1364       # document.warning("Weirdly formed \\begin_layout at line %d of body!" % i)
1365       i += 1
1366       continue
1367     m = m.group(1)
1368     if layouts.count(m) != 0:
1369       document.add_module("Theorems (AMS-Extended)")
1370       return
1371     i += 1
1372
1373 def revert_href(document):
1374     'Reverts hyperlink insets (href) to url insets (url)'
1375     i = 0
1376     while True:
1377       i = find_token(document.body, "\\begin_inset CommandInset href", i)
1378       if i == -1:
1379           return
1380       document.body[i : i + 2] = \
1381         ["\\begin_inset CommandInset url", "LatexCommand url"]
1382       i = i + 2
1383
1384 def revert_url(document):
1385     'Reverts Flex URL insets to old-style URL insets'
1386     i = 0
1387     while True:
1388         i = find_token(document.body, "\\begin_inset Flex URL", i)
1389         if i == -1:
1390             return
1391         j = find_end_of_inset(document.body, i)
1392         if j == -1:
1393             document.warning("Can't find end of inset in revert_url!")
1394             return
1395         k = find_default_layout(document, i, j)
1396         if k == -1:
1397             document.warning("Can't find default layout in revert_url!")
1398             i = j
1399             continue
1400         l = find_end_of(document.body, k, "\\begin_layout", "\\end_layout")
1401         if l == -1 or l >= j:
1402             document.warning("Can't find end of default layout in revert_url!")
1403             i = j
1404             continue
1405         # OK, so the inset's data is between lines k and l.
1406         data =  " ".join(document.body[k+1:l])
1407         data = data.strip()
1408         newinset = ["\\begin_inset LatexCommand url", "target \"" + data + "\"",\
1409                     "", "\\end_inset"]
1410         document.body[i:j+1] = newinset
1411         i = i + len(newinset)
1412
1413
1414 def convert_include(document):
1415   'Converts include insets to new format.'
1416   i = 0
1417   r = re.compile(r'\\begin_inset Include\s+\\([^{]+){([^}]*)}(?:\[(.*)\])?')
1418   while True:
1419     i = find_token(document.body, "\\begin_inset Include", i)
1420     if i == -1:
1421       return
1422     line = document.body[i]
1423     previewline = document.body[i + 1]
1424     m = r.match(line)
1425     if m == None:
1426       document.warning("Unable to match line " + str(i) + " of body!")
1427       i += 1
1428       continue
1429     cmd = m.group(1)
1430     fn  = m.group(2)
1431     opt = m.group(3)
1432     insertion = ["\\begin_inset CommandInset include",
1433        "LatexCommand " + cmd, previewline,
1434        "filename \"" + fn + "\""]
1435     newlines = 2
1436     if opt:
1437       insertion.append("lstparams " + '"' + opt + '"')
1438       newlines += 1
1439     document.body[i : i + 2] = insertion
1440     i += newlines
1441
1442
1443 def revert_include(document):
1444   'Reverts include insets to old format.'
1445   i = 0
1446   r0 = re.compile('preview.*')
1447   r1 = re.compile('LatexCommand (.+)')
1448   r2 = re.compile('filename "(.+)"')
1449   r3 = re.compile('lstparams "(.*)"')
1450   while True:
1451     i = find_token(document.body, "\\begin_inset CommandInset include", i)
1452     if i == -1:
1453       return
1454     nextline = i + 1
1455     m = r1.match(document.body[nextline])
1456     if m == None:
1457       document.warning("Malformed LyX document: No LatexCommand line for `" +
1458         document.body[i] + "' on line " + str(i) + ".")
1459       i += 1
1460       continue
1461     cmd = m.group(1)
1462     nextline += 1
1463     if r0.match(document.body[nextline]):
1464       previewline = document.body[nextline]
1465       nextline += 1
1466     else:
1467       previewline = ""
1468     m = r2.match(document.body[nextline])
1469     if m == None:
1470       document.warning("Malformed LyX document: No filename line for `" + \
1471         document.body[i] + "' on line " + str(i) + ".")
1472       i += 2
1473       continue
1474     fn = m.group(1)
1475     nextline += 1
1476     options = ""
1477     if (cmd == "lstinputlisting"):
1478       m = r3.match(document.body[nextline])
1479       if m != None:
1480         options = m.group(1)
1481         numlines = 5
1482         nextline += 1
1483     newline = "\\begin_inset Include \\" + cmd + "{" + fn + "}"
1484     if options:
1485       newline += ("[" + options + "]")
1486     insertion = [newline]
1487     if previewline != "":
1488       insertion.append(previewline)
1489     document.body[i : nextline] = insertion
1490     i += 2
1491
1492
1493 def revert_albanian(document):
1494     "Set language Albanian to English"
1495     i = 0
1496     if document.language == "albanian":
1497         document.language = "english"
1498         i = find_token(document.header, "\\language", 0)
1499         if i != -1:
1500             document.header[i] = "\\language english"
1501     j = 0
1502     while True:
1503         j = find_token(document.body, "\\lang albanian", j)
1504         if j == -1:
1505             return
1506         document.body[j] = document.body[j].replace("\\lang albanian", "\\lang english")
1507         j = j + 1
1508
1509
1510 def revert_lowersorbian(document):
1511     "Set language lower Sorbian to English"
1512     i = 0
1513     if document.language == "lowersorbian":
1514         document.language = "english"
1515         i = find_token(document.header, "\\language", 0)
1516         if i != -1:
1517             document.header[i] = "\\language english"
1518     j = 0
1519     while True:
1520         j = find_token(document.body, "\\lang lowersorbian", j)
1521         if j == -1:
1522             return
1523         document.body[j] = document.body[j].replace("\\lang lowersorbian", "\\lang english")
1524         j = j + 1
1525
1526
1527 def revert_uppersorbian(document):
1528     "Set language uppersorbian to usorbian as this was used in LyX 1.5"
1529     i = 0
1530     if document.language == "uppersorbian":
1531         document.language = "usorbian"
1532         i = find_token(document.header, "\\language", 0)
1533         if i != -1:
1534             document.header[i] = "\\language usorbian"
1535     j = 0
1536     while True:
1537         j = find_token(document.body, "\\lang uppersorbian", j)
1538         if j == -1:
1539             return
1540         document.body[j] = document.body[j].replace("\\lang uppersorbian", "\\lang usorbian")
1541         j = j + 1
1542
1543
1544 def convert_usorbian(document):
1545     "Set language usorbian to uppersorbian"
1546     i = 0
1547     if document.language == "usorbian":
1548         document.language = "uppersorbian"
1549         i = find_token(document.header, "\\language", 0)
1550         if i != -1:
1551             document.header[i] = "\\language uppersorbian"
1552     j = 0
1553     while True:
1554         j = find_token(document.body, "\\lang usorbian", j)
1555         if j == -1:
1556             return
1557         document.body[j] = document.body[j].replace("\\lang usorbian", "\\lang uppersorbian")
1558         j = j + 1
1559
1560
1561 def convert_macro_global(document):
1562     "Remove TeX code command \global when it is in front of a macro"
1563     # math macros are nowadays already defined \global, so that an additional
1564     # \global would make the document uncompilable, see
1565     # http://bugzilla.lyx.org/show_bug.cgi?id=5371
1566     # We're looking for something like this:
1567     # \begin_inset ERT
1568     # status collapsed
1569     #
1570     # \begin_layout Plain Layout
1571     #
1572     #
1573     # \backslash
1574     # global
1575     # \end_layout
1576     #
1577     # \end_inset
1578     #
1579     #
1580     # \begin_inset FormulaMacro
1581     # \renewcommand{\foo}{123}
1582     # \end_inset
1583     i = 0
1584     while True:
1585         i = find_token(document.body, "\\begin_inset FormulaMacro", i)
1586         if i == -1:
1587             return
1588         # if i <= 13, then there isn't enough room for the ERT
1589         if i <= 12:
1590             i += 1
1591             continue
1592         if document.body[i-6] == "global":
1593             del document.body[i-13 : i]
1594             i = i - 12
1595         else:
1596             i += 1
1597
1598
1599 def revert_macro_optional_params(document):
1600     "Convert macro definitions with optional parameters into ERTs"
1601     # Stub to convert macro definitions with one or more optional parameters
1602     # into uninterpreted ERT insets
1603
1604
1605 def revert_hyperlinktype(document):
1606     'Reverts hyperlink type'
1607     i = 0
1608     j = 0
1609     while True:
1610       i = find_token(document.body, "target", i)
1611       if i == -1:
1612           return
1613       j = find_token(document.body, "type", i)
1614       if j == -1:
1615           return
1616       if j == i + 1:
1617           del document.body[j]
1618       i = i + 1
1619
1620
1621 def revert_pagebreak(document):
1622     'Reverts pagebreak to ERT'
1623     i = 0
1624     while True:
1625       i = find_token(document.body, "\\pagebreak", i)
1626       if i == -1:
1627           return
1628       document.body[i] = '\\begin_inset ERT\nstatus collapsed\n\n' \
1629       '\\begin_layout Standard\n\n\n\\backslash\n' \
1630       'pagebreak{}\n\\end_layout\n\n\\end_inset\n\n'
1631       i = i + 1
1632
1633
1634 def revert_linebreak(document):
1635     'Reverts linebreak to ERT'
1636     i = 0
1637     while True:
1638       i = find_token(document.body, "\\linebreak", i)
1639       if i == -1:
1640           return
1641       document.body[i] = '\\begin_inset ERT\nstatus collapsed\n\n' \
1642       '\\begin_layout Standard\n\n\n\\backslash\n' \
1643       'linebreak{}\n\\end_layout\n\n\\end_inset\n\n'
1644       i = i + 1
1645
1646
1647 def revert_latin(document):
1648     "Set language Latin to English"
1649     i = 0
1650     if document.language == "latin":
1651         document.language = "english"
1652         i = find_token(document.header, "\\language", 0)
1653         if i != -1:
1654             document.header[i] = "\\language english"
1655     j = 0
1656     while True:
1657         j = find_token(document.body, "\\lang latin", j)
1658         if j == -1:
1659             return
1660         document.body[j] = document.body[j].replace("\\lang latin", "\\lang english")
1661         j = j + 1
1662
1663
1664 def revert_samin(document):
1665     "Set language North Sami to English"
1666     i = 0
1667     if document.language == "samin":
1668         document.language = "english"
1669         i = find_token(document.header, "\\language", 0)
1670         if i != -1:
1671             document.header[i] = "\\language english"
1672     j = 0
1673     while True:
1674         j = find_token(document.body, "\\lang samin", j)
1675         if j == -1:
1676             return
1677         document.body[j] = document.body[j].replace("\\lang samin", "\\lang english")
1678         j = j + 1
1679
1680
1681 def convert_serbocroatian(document):
1682     "Set language Serbocroatian to Croatian as this was really Croatian in LyX 1.5"
1683     i = 0
1684     if document.language == "serbocroatian":
1685         document.language = "croatian"
1686         i = find_token(document.header, "\\language", 0)
1687         if i != -1:
1688             document.header[i] = "\\language croatian"
1689     j = 0
1690     while True:
1691         j = find_token(document.body, "\\lang serbocroatian", j)
1692         if j == -1:
1693             return
1694         document.body[j] = document.body[j].replace("\\lang serbocroatian", "\\lang croatian")
1695         j = j + 1
1696
1697
1698 def convert_framed_notes(document):
1699     "Convert framed notes to boxes. "
1700     i = 0
1701     while 1:
1702         i = find_tokens(document.body, ["\\begin_inset Note Framed", "\\begin_inset Note Shaded"], i)
1703         if i == -1:
1704             return
1705         subst = [document.body[i].replace("\\begin_inset Note", "\\begin_inset Box"),
1706                  'position "t"',
1707                  'hor_pos "c"',
1708                  'has_inner_box 0',
1709                  'inner_pos "t"',
1710                  'use_parbox 0',
1711                  'width "100col%"',
1712                  'special "none"',
1713                  'height "1in"',
1714                  'height_special "totalheight"']
1715         document.body[i:i+1] = subst
1716         i = i + 9
1717
1718
1719 def convert_module_names(document):
1720   modulemap = { 'Braille' : 'braille', 'Endnote' : 'endnotes', 'Foot to End' : 'foottoend',\
1721     'Hanging' : 'hanging', 'Linguistics' : 'linguistics', 'Logical Markup' : 'logicalmkup', \
1722     'Theorems (AMS-Extended)' : 'theorems-ams-extended', 'Theorems (AMS)' : 'theorems-ams', \
1723     'Theorems (Order By Chapter)' : 'theorems-chap', 'Theorems (Order By Section)' : 'theorems-sec', \
1724     'Theorems (Starred)' : 'theorems-starred', 'Theorems' : 'theorems-std' }
1725   modlist = document.get_module_list()
1726   if len(modlist) == 0:
1727     return
1728   newmodlist = []
1729   for mod in modlist:
1730     if modulemap.has_key(mod):
1731       newmodlist.append(modulemap[mod])
1732     else:
1733       document.warning("Can't find module %s in the module map!" % mod)
1734       newmodlist.append(mod)
1735   document.set_module_list(newmodlist)
1736
1737
1738 def revert_module_names(document):
1739   modulemap = { 'braille' : 'Braille', 'endnotes' : 'Endnote', 'foottoend' : 'Foot to End',\
1740     'hanging' : 'Hanging', 'linguistics' : 'Linguistics', 'logicalmkup' : 'Logical Markup', \
1741     'theorems-ams-extended' : 'Theorems (AMS-Extended)', 'theorems-ams' : 'Theorems (AMS)', \
1742     'theorems-chap' : 'Theorems (Order By Chapter)', 'theorems-sec' : 'Theorems (Order By Section)', \
1743     'theorems-starred' : 'Theorems (Starred)', 'theorems-std' : 'Theorems'}
1744   modlist = document.get_module_list()
1745   if len(modlist) == 0:
1746     return
1747   newmodlist = []
1748   for mod in modlist:
1749     if modulemap.has_key(mod):
1750       newmodlist.append(modulemap[mod])
1751     else:
1752       document.warning("Can't find module %s in the module map!" % mod)
1753       newmodlist.append(mod)
1754   document.set_module_list(newmodlist)
1755
1756
1757 def revert_colsep(document):
1758     i = find_token(document.header, "\\columnsep", 0)
1759     if i == -1:
1760         return
1761     colsepline = document.header[i]
1762     r = re.compile(r'\\columnsep (.*)')
1763     m = r.match(colsepline)
1764     if not m:
1765         document.warning("Malformed column separation line!")
1766         return
1767     colsep = m.group(1)
1768     del document.header[i]
1769     #it seems to be safe to add the package even if it is already used
1770     pretext = ["\\usepackage{geometry}", "\\geometry{columnsep=" + colsep + "}"]
1771
1772     add_to_preamble(document, pretext)
1773
1774
1775 def revert_framed_notes(document):
1776     "Revert framed boxes to notes. "
1777     i = 0
1778     while 1:
1779         i = find_tokens(document.body, ["\\begin_inset Box Framed", "\\begin_inset Box Shaded"], i)
1780
1781         if i == -1:
1782             return
1783         j = find_end_of_inset(document.body, i + 1)
1784         if j == -1:
1785             # should not happen
1786             document.warning("Malformed LyX document: Could not find end of Box inset.")
1787         k = find_token(document.body, "status", i + 1, j)
1788         if k == -1:
1789             document.warning("Malformed LyX document: Missing `status' tag in Box inset.")
1790             return
1791         status = document.body[k]
1792         l = find_default_layout(document, i + 1, j)
1793         if l == -1:
1794             document.warning("Malformed LyX document: Missing `\\begin_layout' in Box inset.")
1795             return
1796         m = find_token(document.body, "\\end_layout", i + 1, j)
1797         if m == -1:
1798             document.warning("Malformed LyX document: Missing `\\end_layout' in Box inset.")
1799             return
1800         ibox = find_token(document.body, "has_inner_box 1", i + 1, k)
1801         pbox = find_token(document.body, "use_parbox 1", i + 1, k)
1802         if ibox == -1 and pbox == -1:
1803             document.body[i] = document.body[i].replace("\\begin_inset Box", "\\begin_inset Note")
1804             del document.body[i+1:k]
1805         else:
1806             document.body[i] = document.body[i].replace("\\begin_inset Box Shaded", "\\begin_inset Box Frameless")
1807             subst1 = [document.body[l],
1808                       "\\begin_inset Note Shaded",
1809                       status,
1810                       '\\begin_layout Standard']
1811             document.body[l:l + 1] = subst1
1812             subst2 = [document.body[m], "\\end_layout", "\\end_inset"]
1813             document.body[m:m + 1] = subst2
1814         i = i + 1
1815
1816
1817 def revert_slash(document):
1818     'Revert \\SpecialChar \\slash{} to ERT'
1819     i = 0
1820     while i < len(document.body):
1821         m = re.match(r'(.*)\\SpecialChar \\slash{}(.*)', document.body[i])
1822         if m:
1823             before = m.group(1)
1824             after = m.group(2)
1825             subst = [before,
1826                      '\\begin_inset ERT',
1827                      'status collapsed', '',
1828                      '\\begin_layout Standard',
1829                      '', '', '\\backslash',
1830                      'slash{}',
1831                      '\\end_layout', '',
1832                      '\\end_inset', '',
1833                      after]
1834             document.body[i: i+1] = subst
1835             i = i + len(subst)
1836         else:
1837             i = i + 1
1838
1839
1840 def revert_nobreakdash(document):
1841     'Revert \\SpecialChar \\nobreakdash- to ERT'
1842     i = 0
1843     while i < len(document.body):
1844         m = re.match(r'(.*)\\SpecialChar \\nobreakdash-(.*)', document.body[i])
1845         if m:
1846             before = m.group(1)
1847             after = m.group(2)
1848             subst = [before,
1849                      '\\begin_inset ERT',
1850                     'status collapsed', '',
1851                     '\\begin_layout Standard', '', '',
1852                     '\\backslash',
1853                     'nobreakdash-',
1854                     '\\end_layout', '',
1855                     '\\end_inset', '',
1856                      after]
1857             document.body[i: i+1] = subst
1858             i = i + len(subst)
1859             j = find_token(document.header, "\\use_amsmath", 0)
1860             if j == -1:
1861                 document.warning("Malformed LyX document: Missing '\\use_amsmath'.")
1862                 return
1863             document.header[j] = "\\use_amsmath 2"
1864         else:
1865             i = i + 1
1866
1867
1868 #Returns number of lines added/removed
1869 def revert_nocite_key(body, start, end):
1870     'key "..." -> \nocite{...}'
1871     r = re.compile(r'^key "(.*)"')
1872     i = start
1873     j = end
1874     while i < j:
1875         m = r.match(body[i])
1876         if m:
1877             body[i:i+1] = ["\\backslash", "nocite{" + m.group(1) + "}"]
1878             j += 1     # because we added a line
1879             i += 2     # skip that line
1880         else:
1881             del body[i]
1882             j -= 1     # because we deleted a line
1883             # no need to change i, since it now points to the next line
1884     return j - end
1885
1886
1887 def revert_nocite(document):
1888     "Revert LatexCommand nocite to ERT"
1889     i = 0
1890     while 1:
1891         i = find_token(document.body, "\\begin_inset CommandInset citation", i)
1892         if i == -1:
1893             return
1894         if (document.body[i+1] != "LatexCommand nocite"):
1895             # note that we already incremented i
1896             i = i + 1
1897             continue
1898         insetEnd = find_end_of_inset(document.body, i)
1899         if insetEnd == -1:
1900             #this should not happen
1901             document.warning("End of CommandInset citation not found in revert_nocite!")
1902             return
1903
1904         paramLocation = i + 2 #start of the inset's parameters
1905         addedLines = 0
1906         document.body[i:i+2] = \
1907             ["\\begin_inset ERT", "status collapsed", "", "\\begin_layout Standard"]
1908         # that added two lines
1909         paramLocation += 2
1910         insetEnd += 2
1911         #print insetEnd, document.body[i: insetEnd + 1]
1912         insetEnd += revert_nocite_key(document.body, paramLocation, insetEnd)
1913         #print insetEnd, document.body[i: insetEnd + 1]
1914         document.body.insert(insetEnd, "\\end_layout")
1915         document.body.insert(insetEnd + 1, "")
1916         i = insetEnd + 1
1917
1918
1919 def revert_btprintall(document):
1920     "Revert (non-bibtopic) btPrintAll option to ERT \nocite{*}"
1921     i = find_token(document.header, '\\use_bibtopic', 0)
1922     if i == -1:
1923         document.warning("Malformed lyx document: Missing '\\use_bibtopic'.")
1924         return
1925     if get_value(document.header, '\\use_bibtopic', 0) == "false":
1926         i = 0
1927         while i < len(document.body):
1928             i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
1929             if i == -1:
1930                 return
1931             j = find_end_of_inset(document.body, i + 1)
1932             if j == -1:
1933                 #this should not happen
1934                 document.warning("End of CommandInset bibtex not found in revert_btprintall!")
1935                 j = len(document.body)
1936             # this range isn't really right, but it should be OK, since we shouldn't
1937             # see more than one matching line in each inset
1938             addedlines = 0
1939             for k in range(i, j):
1940                 if (document.body[k] == 'btprint "btPrintAll"'):
1941                     del document.body[k]
1942                     subst = ["\\begin_inset ERT",
1943                              "status collapsed", "",
1944                              "\\begin_layout Standard", "",
1945                              "\\backslash",
1946                              "nocite{*}",
1947                              "\\end_layout",
1948                              "\\end_inset"]
1949                     document.body[i:i] = subst
1950                     addlines = addedlines + len(subst) - 1
1951             i = j + addedlines
1952
1953
1954 def revert_bahasam(document):
1955     "Set language Bahasa Malaysia to Bahasa Indonesia"
1956     i = 0
1957     if document.language == "bahasam":
1958         document.language = "bahasa"
1959         i = find_token(document.header, "\\language", 0)
1960         if i != -1:
1961             document.header[i] = "\\language bahasa"
1962     j = 0
1963     while True:
1964         j = find_token(document.body, "\\lang bahasam", j)
1965         if j == -1:
1966             return
1967         document.body[j] = document.body[j].replace("\\lang bahasam", "\\lang bahasa")
1968         j = j + 1
1969
1970
1971 def revert_interlingua(document):
1972     "Set language Interlingua to English"
1973     i = 0
1974     if document.language == "interlingua":
1975         document.language = "english"
1976         i = find_token(document.header, "\\language", 0)
1977         if i != -1:
1978             document.header[i] = "\\language english"
1979     j = 0
1980     while True:
1981         j = find_token(document.body, "\\lang interlingua", j)
1982         if j == -1:
1983             return
1984         document.body[j] = document.body[j].replace("\\lang interlingua", "\\lang english")
1985         j = j + 1
1986
1987
1988 def revert_serbianlatin(document):
1989     "Set language Serbian-Latin to Croatian"
1990     i = 0
1991     if document.language == "serbian-latin":
1992         document.language = "croatian"
1993         i = find_token(document.header, "\\language", 0)
1994         if i != -1:
1995             document.header[i] = "\\language croatian"
1996     j = 0
1997     while True:
1998         j = find_token(document.body, "\\lang serbian-latin", j)
1999         if j == -1:
2000             return
2001         document.body[j] = document.body[j].replace("\\lang serbian-latin", "\\lang croatian")
2002         j = j + 1
2003
2004
2005 def revert_rotfloat(document):
2006     " Revert sideways custom floats. "
2007     i = 0
2008     while 1:
2009         # whitespace intended (exclude \\begin_inset FloatList)
2010         i = find_token(document.body, "\\begin_inset Float ", i)
2011         if i == -1:
2012             return
2013         line = document.body[i]
2014         r = re.compile(r'\\begin_inset Float (.*)$')
2015         m = r.match(line)
2016         if m == None:
2017             document.warning("Unable to match line " + str(i) + " of body!")
2018             i += 1
2019             continue
2020         floattype = m.group(1)
2021         if floattype == "figure" or floattype == "table":
2022             i += 1
2023             continue
2024         j = find_end_of_inset(document.body, i)
2025         if j == -1:
2026             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_rotfloat.")
2027             i += 1
2028             continue
2029         addedLines = 0
2030         if get_value(document.body, 'sideways', i, j) == "false":
2031             i += 1
2032             continue
2033         l = find_default_layout(document, i + 1, j)
2034         if l == -1:
2035             document.warning("Malformed LyX document: Missing `\\begin_layout' in Float inset.")
2036             return
2037         subst = ['\\begin_layout Standard',
2038                   '\\begin_inset ERT',
2039                   'status collapsed', '',
2040                   '\\begin_layout Standard', '', '',
2041                   '\\backslash', '',
2042                   'end{sideways' + floattype + '}',
2043                   '\\end_layout', '', '\\end_inset']
2044         document.body[j : j+1] = subst
2045         addedLines = len(subst) - 1
2046         del document.body[i+1 : l]
2047         addedLines -= (l-1) - (i+1)
2048         subst = ['\\begin_inset ERT', 'status collapsed', '',
2049                   '\\begin_layout Standard', '', '', '\\backslash',
2050                   'begin{sideways' + floattype + '}',
2051                   '\\end_layout', '', '\\end_inset', '',
2052                   '\\end_layout', '']
2053         document.body[i : i+1] = subst
2054         addedLines += len(subst) - 1
2055         if floattype == "algorithm":
2056             add_to_preamble(document,
2057                             ['% Commands inserted by lyx2lyx for sideways algorithm float',
2058                               '\\usepackage{rotfloat}',
2059                               '\\floatstyle{ruled}',
2060                               '\\newfloat{algorithm}{tbp}{loa}',
2061                               '\\floatname{algorithm}{Algorithm}'])
2062         else:
2063             document.warning("Cannot create preamble definition for custom float" + floattype + ".")
2064         i += addedLines + 1
2065
2066
2067 def revert_widesideways(document):
2068     " Revert wide sideways floats. "
2069     i = 0
2070     while 1:
2071         # whitespace intended (exclude \\begin_inset FloatList)
2072         i = find_token(document.body, '\\begin_inset Float ', i)
2073         if i == -1:
2074             return
2075         line = document.body[i]
2076         r = re.compile(r'\\begin_inset Float (.*)$')
2077         m = r.match(line)
2078         if m == None:
2079             document.warning("Unable to match line " + str(i) + " of body!")
2080             i += 1
2081             continue
2082         floattype = m.group(1)
2083         if floattype != "figure" and floattype != "table":
2084             i += 1
2085             continue
2086         j = find_end_of_inset(document.body, i)
2087         if j == -1:
2088             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_widesideways.")
2089             i += 1
2090             continue
2091         if get_value(document.body, 'sideways', i, j) == "false" or \
2092            get_value(document.body, 'wide', i, j) == "false":
2093              i += 1
2094              continue
2095         l = find_default_layout(document, i + 1, j)
2096         if l == -1:
2097             document.warning("Malformed LyX document: Missing `\\begin_layout' in Float inset.")
2098             return
2099         subst = ['\\begin_layout Standard', '\\begin_inset ERT',
2100                   'status collapsed', '',
2101                   '\\begin_layout Standard', '', '', '\\backslash',
2102                   'end{sideways' + floattype + '*}',
2103                   '\\end_layout', '', '\\end_inset']
2104         document.body[j : j+1] = subst
2105         addedLines = len(subst) - 1
2106         del document.body[i+1:l-1]
2107         addedLines -= (l-1) - (i+1)
2108         subst = ['\\begin_inset ERT', 'status collapsed', '',
2109                  '\\begin_layout Standard', '', '', '\\backslash',
2110                  'begin{sideways' + floattype + '*}', '\\end_layout', '',
2111                  '\\end_inset', '', '\\end_layout', '']
2112         document.body[i : i+1] = subst
2113         addedLines += len(subst) - 1
2114         add_to_preamble(document, ['\\usepackage{rotfloat}\n'])
2115         i += addedLines + 1
2116
2117
2118 def revert_inset_embedding(document, type):
2119     ' Remove embed tag from certain type of insets'
2120     i = 0
2121     while 1:
2122         i = find_token(document.body, "\\begin_inset %s" % type, i)
2123         if i == -1:
2124             return
2125         j = find_end_of_inset(document.body, i)
2126         if j == -1:
2127             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_inset_embedding.")
2128             i = i + 1
2129             continue
2130         k = find_token(document.body, "\tembed", i, j)
2131         if k == -1:
2132             k = find_token(document.body, "embed", i, j)
2133         if k != -1:
2134             del document.body[k]
2135         i = i + 1
2136
2137
2138 def revert_external_embedding(document):
2139     ' Remove embed tag from external inset '
2140     revert_inset_embedding(document, 'External')
2141
2142
2143 def convert_subfig(document):
2144     " Convert subfigures to subfloats. "
2145     i = 0
2146     while 1:
2147         addedLines = 0
2148         i = find_token(document.body, '\\begin_inset Graphics', i)
2149         if i == -1:
2150             return
2151         endInset = find_end_of_inset(document.body, i)
2152         if endInset == -1:
2153             document.warning("Malformed lyx document: Missing '\\end_inset' in convert_subfig.")
2154             i += 1
2155             continue
2156         k = find_token(document.body, '\tsubcaption', i, endInset)
2157         if k == -1:
2158             i = endInset
2159             continue
2160         l = find_token(document.body, '\tsubcaptionText', i, endInset)
2161         if l == -1:
2162             caption = ""
2163         else:
2164             caption = document.body[l][16:].strip('"')
2165             del document.body[l]
2166             addedLines -= 1
2167         del document.body[k]
2168         addedLines -= 1
2169         subst = ['\\begin_inset Float figure', 'wide false', 'sideways false',
2170                  'status open', '', '\\begin_layout Plain Layout', '\\begin_inset Caption',
2171                  '', '\\begin_layout Plain Layout'] + latex2lyx(caption) + \
2172                  [ '\\end_layout', '', '\\end_inset', '',
2173                  '\\end_layout', '', '\\begin_layout Plain Layout']
2174         document.body[i : i] = subst
2175         addedLines += len(subst)
2176         endInset += addedLines
2177         subst = ['', '\\end_inset', '', '\\end_layout']
2178         document.body[endInset : endInset] = subst
2179         addedLines += len(subst)
2180         i += addedLines + 1
2181
2182
2183 def revert_subfig(document):
2184     " Revert subfloats. "
2185     i = 0
2186     while 1:
2187         # whitespace intended (exclude \\begin_inset FloatList)
2188         i = find_tokens(document.body, ['\\begin_inset Float ', '\\begin_inset Wrap'], i)
2189         if i == -1:
2190             return
2191         j = 0
2192         addedLines = 0
2193         while j != -1:
2194             j = find_end_of_inset(document.body, i)
2195             if j == -1:
2196                 document.warning("Malformed lyx document: Missing '\\end_inset' (float) at line " + str(i + len(document.header)) + ".\n\t" + document.body[i])
2197                 # document.warning(document.body[i-1] + "\n" + document.body[i+1])
2198                 i += 1
2199                 continue # this will get us back to the outer loop, since j == -1
2200             # look for embedded float (= subfloat)
2201             # whitespace intended (exclude \\begin_inset FloatList)
2202             k = find_token(document.body, '\\begin_inset Float ', i + 1, j)
2203             if k == -1:
2204                 break
2205             # is the subfloat aligned?
2206             al = find_token(document.body, '\\align ', k - 1, j)
2207             alignment_beg = ""
2208             alignment_end = ""
2209             if al != -1:
2210                 if get_value(document.body, '\\align', al) == "center":
2211                     alignment_beg = "\\backslash\nbegin{centering}"
2212                     alignment_end = "\\backslash\npar\\backslash\nend{centering}"
2213                 elif get_value(document.body, '\\align', al) == "left":
2214                     alignment_beg = "\\backslash\nbegin{raggedright}"
2215                     alignment_end = "\\backslash\npar\\backslash\nend{raggedright}"
2216                 elif get_value(document.body, '\\align', al) == "right":
2217                     alignment_beg = "\\backslash\nbegin{raggedleft}"
2218                     alignment_end = "\\backslash\npar\\backslash\nend{raggedleft}"
2219             l = find_end_of_inset(document.body, k)
2220             if l == -1:
2221                 document.warning("Malformed lyx document: Missing '\\end_inset' (embedded float).")
2222                 i += 1
2223                 j == -1
2224                 continue # escape to the outer loop
2225             m = find_default_layout(document, k + 1, l)
2226             # caption?
2227             cap = find_token(document.body, '\\begin_inset Caption', k + 1, l)
2228             caption = ''
2229             shortcap = ''
2230             capend = cap
2231             if cap != -1:
2232                 capend = find_end_of_inset(document.body, cap)
2233                 if capend == -1:
2234                     document.warning("Malformed lyx document: Missing '\\end_inset' (caption).")
2235                     return
2236                 # label?
2237                 label = ''
2238                 lbl = find_token(document.body, '\\begin_inset CommandInset label', cap, capend)
2239                 if lbl != -1:
2240                     lblend = find_end_of_inset(document.body, lbl + 1)
2241                     if lblend == -1:
2242                         document.warning("Malformed lyx document: Missing '\\end_inset' (label).")
2243                         return
2244                     for line in document.body[lbl:lblend + 1]:
2245                         if line.startswith('name '):
2246                             label = line.split()[1].strip('"')
2247                             break
2248                 else:
2249                     lbl = capend
2250                     lblend = capend
2251                     label = ''
2252                 # opt arg?
2253                 opt = find_token(document.body, '\\begin_inset OptArg', cap, capend)
2254                 if opt != -1:
2255                     optend = find_end_of_inset(document.body, opt)
2256                     if optend == -1:
2257                         document.warning("Malformed lyx document: Missing '\\end_inset' (OptArg).")
2258                         return
2259                     optc = find_default_layout(document, opt, optend)
2260                     if optc == -1:
2261                         document.warning("Malformed LyX document: Missing `\\begin_layout' in Float inset.")
2262                         return
2263                     optcend = find_end_of(document.body, optc, "\\begin_layout", "\\end_layout")
2264                     for line in document.body[optc:optcend]:
2265                         if not line.startswith('\\'):
2266                             shortcap += line.strip()
2267                 else:
2268                     opt = capend
2269                     optend = capend
2270                 for line in document.body[cap:capend]:
2271                     if line in document.body[lbl:lblend]:
2272                         continue
2273                     elif line in document.body[opt:optend]:
2274                         continue
2275                     elif not line.startswith('\\'):
2276                         caption += line.strip()
2277                 if len(label) > 0:
2278                     caption += "\\backslash\nlabel{" + label + "}"
2279             subst = '\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus collapsed\n\n' \
2280                       '\\begin_layout Plain Layout\n\n}' + alignment_end + \
2281                       '\n\\end_layout\n\n\\end_inset\n\n' \
2282                       '\\end_layout\n\n\\begin_layout Plain Layout\n'
2283             subst = subst.split('\n')
2284             document.body[l : l+1] = subst
2285             addedLines = len(subst) - 1
2286             # this is before l and so is unchanged by the multiline insertion
2287             if cap != capend:
2288                 del document.body[cap:capend+1]
2289                 addedLines -= (capend + 1 - cap)
2290             del document.body[k+1:m-1]
2291             addedLines -= (m - 1 - (k + 1))
2292             insertion = '\\begin_inset ERT\nstatus collapsed\n\n' \
2293                         '\\begin_layout Plain Layout\n\n' + alignment_beg + '\\backslash\n' \
2294                         'subfloat'
2295             if len(shortcap) > 0:
2296                 insertion = insertion + "[" + shortcap + "]"
2297             if len(caption) > 0:
2298                 insertion = insertion + "[" + caption + "]"
2299             insertion = insertion + '{%\n\\end_layout\n\n\\end_inset\n\n\\end_layout\n'
2300             insertion = insertion.split('\n')
2301             document.body[k : k + 1] = insertion
2302             addedLines += len(insertion) - 1
2303             if al != -1:
2304                 del document.body[al]
2305                 addedLines -= 1
2306             add_to_preamble(document, ['\\usepackage{subfig}\n'])
2307         i += addedLines + 1
2308
2309
2310 def revert_wrapplacement(document):
2311     " Revert placement options wrap floats (wrapfig). "
2312     i = 0
2313     while True:
2314         i = find_token(document.body, "\\begin_inset Wrap figure", i)
2315         if i == -1:
2316             return
2317         e = find_end_of_inset(document.body, i)
2318         j = find_token(document.body, "placement", i + 1, e)
2319         if j == -1:
2320             document.warning("Malformed LyX document: Couldn't find placement parameter of wrap float.")
2321             i += 1
2322             continue
2323         r = re.compile("placement (o|i|l|r|O|I|L|R)")
2324         m = r.match(document.body[j])
2325         if m == None:
2326             document.warning("Malformed LyX document: Placement option isn't O|I|R|L!")
2327         else:
2328             document.body[j] = "placement " + m.group(1).lower()
2329         i = j
2330
2331
2332 def remove_extra_embedded_files(document):
2333     " Remove \extra_embedded_files from buffer params "
2334     i = find_token(document.header, '\\extra_embedded_files', 0)
2335     if i == -1:
2336         return
2337     document.header.pop(i)
2338
2339
2340 def convert_spaceinset(document):
2341     " Convert '\\InsetSpace foo' to '\\begin_inset Space foo\n\\end_inset' "
2342     i = 0
2343     while i < len(document.body):
2344         m = re.match(r'(.*)\\InsetSpace (.*)', document.body[i])
2345         if m:
2346             before = m.group(1)
2347             after = m.group(2)
2348             subst = [before, "\\begin_inset Space " + after, "\\end_inset"]
2349             document.body[i: i+1] = subst
2350             i = i + len(subst)
2351         else:
2352             i = i + 1
2353
2354
2355 def revert_spaceinset(document):
2356     " Revert '\\begin_inset Space foo\n\\end_inset' to '\\InsetSpace foo' "
2357     i = 0
2358     while True:
2359         i = find_token(document.body, "\\begin_inset Space", i)
2360         if i == -1:
2361             return
2362         j = find_end_of_inset(document.body, i)
2363         if j == -1:
2364             document.warning("Malformed LyX document: Could not find end of space inset.")
2365             continue
2366         document.body[i] = document.body[i].replace('\\begin_inset Space', '\\InsetSpace')
2367         del document.body[j]
2368
2369
2370 def convert_hfill(document):
2371     " Convert hfill to space inset "
2372     i = 0
2373     while True:
2374         i = find_token(document.body, "\\hfill", i)
2375         if i == -1:
2376             return
2377         subst = document.body[i].replace('\\hfill', \
2378                   '\n\\begin_inset Space \\hfill{}\n\\end_inset')
2379         subst = subst.split('\n')
2380         document.body[i : i+1] = subst
2381         i += len(subst)
2382
2383
2384 def revert_hfills(document):
2385     ' Revert \\hfill commands '
2386     hfill = re.compile(r'\\hfill')
2387     dotfill = re.compile(r'\\dotfill')
2388     hrulefill = re.compile(r'\\hrulefill')
2389     i = 0
2390     while True:
2391         i = find_token(document.body, "\\InsetSpace", i)
2392         if i == -1:
2393             return
2394         if hfill.search(document.body[i]):
2395             document.body[i] = \
2396               document.body[i].replace('\\InsetSpace \\hfill{}', '\\hfill')
2397             i += 1
2398             continue
2399         if dotfill.search(document.body[i]):
2400             subst = document.body[i].replace('\\InsetSpace \\dotfill{}', \
2401               '\\begin_inset ERT\nstatus collapsed\n\n' \
2402               '\\begin_layout Standard\n\n\n\\backslash\n' \
2403               'dotfill{}\n\\end_layout\n\n\\end_inset\n\n')
2404             subst = subst.split('\n')
2405             document.body[i : i+1] = subst
2406             i += len(subst)
2407             continue
2408         if hrulefill.search(document.body[i]):
2409             subst = document.body[i].replace('\\InsetSpace \\hrulefill{}', \
2410               '\\begin_inset ERT\nstatus collapsed\n\n' \
2411               '\\begin_layout Standard\n\n\n\\backslash\n' \
2412               'hrulefill{}\n\\end_layout\n\n\\end_inset\n\n')
2413             subst = subst.split('\n')
2414             document.body[i : i+1] = subst
2415             i += len(subst)
2416             continue
2417         i += 1
2418
2419 def revert_hspace(document):
2420     ' Revert \\InsetSpace \\hspace{} to ERT '
2421     i = 0
2422     hspace = re.compile(r'\\hspace{}')
2423     hstar  = re.compile(r'\\hspace\*{}')
2424     while True:
2425         i = find_token(document.body, "\\InsetSpace \\hspace", i)
2426         if i == -1:
2427             return
2428         length = get_value(document.body, '\\length', i+1)
2429         if length == '':
2430             document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
2431             return
2432         del document.body[i+1]
2433         addedLines = -1
2434         if hstar.search(document.body[i]):
2435             subst = document.body[i].replace('\\InsetSpace \\hspace*{}', \
2436               '\\begin_inset ERT\nstatus collapsed\n\n' \
2437               '\\begin_layout Standard\n\n\n\\backslash\n' \
2438               'hspace*{' + length + '}\n\\end_layout\n\n\\end_inset\n\n')
2439             subst = subst.split('\n')
2440             document.body[i : i+1] = subst
2441             addedLines += len(subst) - 1
2442             i += addedLines + 1
2443             continue
2444         if hspace.search(document.body[i]):
2445             subst = document.body[i].replace('\\InsetSpace \\hspace{}', \
2446               '\\begin_inset ERT\nstatus collapsed\n\n' \
2447               '\\begin_layout Standard\n\n\n\\backslash\n' \
2448               'hspace{' + length + '}\n\\end_layout\n\n\\end_inset\n\n')
2449             subst = subst.split('\n')
2450             document.body[i : i+1] = subst
2451             addedLines += len(subst) - 1
2452             i += addedLines + 1
2453             continue
2454         i += 1
2455
2456
2457 def revert_protected_hfill(document):
2458     ' Revert \\begin_inset Space \\hspace*{\\fill} to ERT '
2459     i = 0
2460     while True:
2461         i = find_token(document.body, '\\begin_inset Space \\hspace*{\\fill}', i)
2462         if i == -1:
2463             return
2464         j = find_end_of_inset(document.body, i)
2465         if j == -1:
2466             document.warning("Malformed LyX document: Could not find end of space inset.")
2467             continue
2468         del document.body[j]
2469         subst = document.body[i].replace('\\begin_inset Space \\hspace*{\\fill}', \
2470           '\\begin_inset ERT\nstatus collapsed\n\n' \
2471           '\\begin_layout Standard\n\n\n\\backslash\n' \
2472           'hspace*{\n\\backslash\nfill}\n\\end_layout\n\n\\end_inset\n\n')
2473         subst = subst.split('\n')
2474         document.body[i : i+1] = subst
2475         i += len(subst)
2476
2477
2478 def revert_leftarrowfill(document):
2479     ' Revert \\begin_inset Space \\leftarrowfill{} to ERT '
2480     i = 0
2481     while True:
2482         i = find_token(document.body, '\\begin_inset Space \\leftarrowfill{}', i)
2483         if i == -1:
2484             return
2485         j = find_end_of_inset(document.body, i)
2486         if j == -1:
2487             document.warning("Malformed LyX document: Could not find end of space inset.")
2488             continue
2489         del document.body[j]
2490         subst = document.body[i].replace('\\begin_inset Space \\leftarrowfill{}', \
2491           '\\begin_inset ERT\nstatus collapsed\n\n' \
2492           '\\begin_layout Standard\n\n\n\\backslash\n' \
2493           'leftarrowfill{}\n\\end_layout\n\n\\end_inset\n\n')
2494         subst = subst.split('\n')
2495         document.body[i : i+1] = subst
2496         i += len(subst)
2497
2498
2499 def revert_rightarrowfill(document):
2500     ' Revert \\begin_inset Space \\rightarrowfill{} to ERT '
2501     i = 0
2502     while True:
2503         i = find_token(document.body, '\\begin_inset Space \\rightarrowfill{}', i)
2504         if i == -1:
2505             return
2506         j = find_end_of_inset(document.body, i)
2507         if j == -1:
2508             document.warning("Malformed LyX document: Could not find end of space inset.")
2509             continue
2510         del document.body[j]
2511         subst = document.body[i].replace('\\begin_inset Space \\rightarrowfill{}', \
2512           '\\begin_inset ERT\nstatus collapsed\n\n' \
2513           '\\begin_layout Standard\n\n\n\\backslash\n' \
2514           'rightarrowfill{}\n\\end_layout\n\n\\end_inset\n\n')
2515         subst = subst.split('\n')
2516         document.body[i : i+1] = subst
2517         i += len(subst)
2518
2519
2520 def revert_upbracefill(document):
2521     ' Revert \\begin_inset Space \\upbracefill{} to ERT '
2522     i = 0
2523     while True:
2524         i = find_token(document.body, '\\begin_inset Space \\upbracefill{}', i)
2525         if i == -1:
2526             return
2527         j = find_end_of_inset(document.body, i)
2528         if j == -1:
2529             document.warning("Malformed LyX document: Could not find end of space inset.")
2530             continue
2531         del document.body[j]
2532         subst = document.body[i].replace('\\begin_inset Space \\upbracefill{}', \
2533           '\\begin_inset ERT\nstatus collapsed\n\n' \
2534           '\\begin_layout Standard\n\n\n\\backslash\n' \
2535           'upbracefill{}\n\\end_layout\n\n\\end_inset\n\n')
2536         subst = subst.split('\n')
2537         document.body[i : i+1] = subst
2538         i += len(subst)
2539
2540
2541 def revert_downbracefill(document):
2542     ' Revert \\begin_inset Space \\downbracefill{} to ERT '
2543     i = 0
2544     while True:
2545         i = find_token(document.body, '\\begin_inset Space \\downbracefill{}', i)
2546         if i == -1:
2547             return
2548         j = find_end_of_inset(document.body, i)
2549         if j == -1:
2550             document.warning("Malformed LyX document: Could not find end of space inset.")
2551             continue
2552         del document.body[j]
2553         subst = document.body[i].replace('\\begin_inset Space \\downbracefill{}', \
2554           '\\begin_inset ERT\nstatus collapsed\n\n' \
2555           '\\begin_layout Standard\n\n\n\\backslash\n' \
2556           'downbracefill{}\n\\end_layout\n\n\\end_inset\n\n')
2557         subst = subst.split('\n')
2558         document.body[i : i+1] = subst
2559         i += len(subst)
2560
2561
2562 def revert_local_layout(document):
2563     ' Revert local layout headers.'
2564     i = 0
2565     while True:
2566         i = find_token(document.header, "\\begin_local_layout", i)
2567         if i == -1:
2568             return
2569         j = find_end_of(document.header, i, "\\begin_local_layout", "\\end_local_layout")
2570         if j == -1:
2571             # this should not happen
2572             break
2573         document.header[i : j + 1] = []
2574
2575
2576 def convert_pagebreaks(document):
2577     ' Convert inline Newpage insets to new format '
2578     i = 0
2579     while True:
2580         i = find_token(document.body, '\\newpage', i)
2581         if i == -1:
2582             break
2583         document.body[i:i+1] = ['\\begin_inset Newpage newpage',
2584                                 '\\end_inset']
2585     i = 0
2586     while True:
2587         i = find_token(document.body, '\\pagebreak', i)
2588         if i == -1:
2589             break
2590         document.body[i:i+1] = ['\\begin_inset Newpage pagebreak',
2591                                 '\\end_inset']
2592     i = 0
2593     while True:
2594         i = find_token(document.body, '\\clearpage', i)
2595         if i == -1:
2596             break
2597         document.body[i:i+1] = ['\\begin_inset Newpage clearpage',
2598                                 '\\end_inset']
2599     i = 0
2600     while True:
2601         i = find_token(document.body, '\\cleardoublepage', i)
2602         if i == -1:
2603             break
2604         document.body[i:i+1] = ['\\begin_inset Newpage cleardoublepage',
2605                                 '\\end_inset']
2606
2607
2608 def revert_pagebreaks(document):
2609     ' Revert \\begin_inset Newpage to previous inline format '
2610     i = 0
2611     while True:
2612         i = find_token(document.body, '\\begin_inset Newpage', i)
2613         if i == -1:
2614             return
2615         j = find_end_of_inset(document.body, i)
2616         if j == -1:
2617             document.warning("Malformed LyX document: Could not find end of Newpage inset.")
2618             continue
2619         del document.body[j]
2620         document.body[i] = document.body[i].replace('\\begin_inset Newpage newpage', '\\newpage')
2621         document.body[i] = document.body[i].replace('\\begin_inset Newpage pagebreak', '\\pagebreak')
2622         document.body[i] = document.body[i].replace('\\begin_inset Newpage clearpage', '\\clearpage')
2623         document.body[i] = document.body[i].replace('\\begin_inset Newpage cleardoublepage', '\\cleardoublepage')
2624
2625
2626 def convert_linebreaks(document):
2627     ' Convert inline Newline insets to new format '
2628     i = 0
2629     while True:
2630         i = find_token(document.body, '\\newline', i)
2631         if i == -1:
2632             break
2633         document.body[i:i+1] = ['\\begin_inset Newline newline',
2634                                 '\\end_inset']
2635     i = 0
2636     while True:
2637         i = find_token(document.body, '\\linebreak', i)
2638         if i == -1:
2639             break
2640         document.body[i:i+1] = ['\\begin_inset Newline linebreak',
2641                                 '\\end_inset']
2642
2643
2644 def revert_linebreaks(document):
2645     ' Revert \\begin_inset Newline to previous inline format '
2646     i = 0
2647     while True:
2648         i = find_token(document.body, '\\begin_inset Newline', i)
2649         if i == -1:
2650             return
2651         j = find_end_of_inset(document.body, i)
2652         if j == -1:
2653             document.warning("Malformed LyX document: Could not find end of Newline inset.")
2654             continue
2655         del document.body[j]
2656         document.body[i] = document.body[i].replace('\\begin_inset Newline newline', '\\newline')
2657         document.body[i] = document.body[i].replace('\\begin_inset Newline linebreak', '\\linebreak')
2658
2659
2660 def convert_japanese_plain(document):
2661     ' Set language japanese-plain to japanese '
2662     i = 0
2663     if document.language == "japanese-plain":
2664         document.language = "japanese"
2665         i = find_token(document.header, "\\language", 0)
2666         if i != -1:
2667             document.header[i] = "\\language japanese"
2668     j = 0
2669     while True:
2670         j = find_token(document.body, "\\lang japanese-plain", j)
2671         if j == -1:
2672             return
2673         document.body[j] = document.body[j].replace("\\lang japanese-plain", "\\lang japanese")
2674         j = j + 1
2675
2676
2677 def revert_pdfpages(document):
2678     ' Revert pdfpages external inset to ERT '
2679     i = 0
2680     while 1:
2681         i = find_token(document.body, "\\begin_inset External", i)
2682         if i == -1:
2683             return
2684         j = find_end_of_inset(document.body, i)
2685         if j == -1:
2686             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_pdfpages.")
2687             i = i + 1
2688             continue
2689         if get_value(document.body, 'template', i, j) == "PDFPages":
2690             filename = get_value(document.body, 'filename', i, j)
2691             extra = ''
2692             r = re.compile(r'\textra PDFLaTeX \"(.*)\"$')
2693             for k in range(i, j):
2694                 m = r.match(document.body[k])
2695                 if m:
2696                     extra = m.group(1)
2697             angle = get_value(document.body, 'rotateAngle', i, j)
2698             width = get_value(document.body, 'width', i, j)
2699             height = get_value(document.body, 'height', i, j)
2700             scale = get_value(document.body, 'scale', i, j)
2701             keepAspectRatio = find_token(document.body, "\tkeepAspectRatio", i, j)
2702             options = extra
2703             if angle != '':
2704                  if options != '':
2705                      options += ",angle=" + angle
2706                  else:
2707                      options += "angle=" + angle
2708             if width != '':
2709                  if options != '':
2710                      options += ",width=" + convert_len(width)
2711                  else:
2712                      options += "width=" + convert_len(width)
2713             if height != '':
2714                  if options != '':
2715                      options += ",height=" + convert_len(height)
2716                  else:
2717                      options += "height=" + convert_len(height)
2718             if scale != '':
2719                  if options != '':
2720                      options += ",scale=" + scale
2721                  else:
2722                      options += "scale=" + scale
2723             if keepAspectRatio != '':
2724                  if options != '':
2725                      options += ",keepaspectratio"
2726                  else:
2727                      options += "keepaspectratio"
2728             if options != '':
2729                      options = '[' + options + ']'
2730             del document.body[i+1:j+1]
2731             document.body[i:i+1] = ['\\begin_inset ERT',
2732                                 'status collapsed',
2733                                 '',
2734                                 '\\begin_layout Standard',
2735                                 '',
2736                                 '\\backslash',
2737                                 'includepdf' + options + '{' + filename + '}',
2738                                 '\\end_layout',
2739                                 '',
2740                                 '\\end_inset']
2741             add_to_preamble(document, ['\\usepackage{pdfpages}\n'])
2742             i = i + 1
2743             continue
2744         i = i + 1
2745
2746
2747 def revert_mexican(document):
2748     ' Set language Spanish(Mexico) to Spanish '
2749     i = 0
2750     if document.language == "spanish-mexico":
2751         document.language = "spanish"
2752         i = find_token(document.header, "\\language", 0)
2753         if i != -1:
2754             document.header[i] = "\\language spanish"
2755     j = 0
2756     while True:
2757         j = find_token(document.body, "\\lang spanish-mexico", j)
2758         if j == -1:
2759             return
2760         document.body[j] = document.body[j].replace("\\lang spanish-mexico", "\\lang spanish")
2761         j = j + 1
2762
2763
2764 def remove_embedding(document):
2765     ' Remove embed tag from all insets '
2766     revert_inset_embedding(document, 'Graphics')
2767     revert_inset_embedding(document, 'External')
2768     revert_inset_embedding(document, 'CommandInset include')
2769     revert_inset_embedding(document, 'CommandInset bibtex')
2770
2771
2772 def revert_master(document):
2773     ' Remove master param '
2774     i = find_token(document.header, "\\master", 0)
2775     if i != -1:
2776         del document.header[i]
2777
2778
2779 def revert_graphics_group(document):
2780     ' Revert group information from graphics insets '
2781     i = 0
2782     while 1:
2783         i = find_token(document.body, "\\begin_inset Graphics", i)
2784         if i == -1:
2785             return
2786         j = find_end_of_inset(document.body, i)
2787         if j == -1:
2788             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_graphics_group.")
2789             i = i + 1
2790             continue
2791         k = find_token(document.body, " groupId", i, j)
2792         if k == -1:
2793             i = i + 1
2794             continue
2795         del document.body[k]
2796         i = i + 1
2797
2798
2799 def update_apa_styles(document):
2800     ' Replace obsolete styles '
2801
2802     if document.textclass != "apa":
2803         return
2804
2805     obsoletedby = { "Acknowledgments": "Acknowledgements",
2806                     "Section*":        "Section",
2807                     "Subsection*":     "Subsection",
2808                     "Subsubsection*":  "Subsubsection",
2809                     "Paragraph*":      "Paragraph",
2810                     "Subparagraph*":   "Subparagraph"}
2811     i = 0
2812     while 1:
2813         i = find_token(document.body, "\\begin_layout", i)
2814         if i == -1:
2815             return
2816
2817         layout = document.body[i][14:]
2818         if layout in obsoletedby:
2819             document.body[i] = "\\begin_layout " + obsoletedby[layout]
2820
2821         i += 1
2822
2823
2824 def convert_paper_sizes(document):
2825     ' exchange size options legalpaper and executivepaper to correct order '
2826     # routine is needed to fix http://bugzilla.lyx.org/show_bug.cgi?id=4868
2827     i = 0
2828     j = 0
2829     i = find_token(document.header, "\\papersize executivepaper", 0)
2830     if i != -1:
2831         document.header[i] = "\\papersize legalpaper"
2832         return
2833     j = find_token(document.header, "\\papersize legalpaper", 0)
2834     if j != -1:
2835         document.header[j] = "\\papersize executivepaper"
2836
2837
2838 def revert_paper_sizes(document):
2839     ' exchange size options legalpaper and executivepaper to correct order '
2840     i = 0
2841     j = 0
2842     i = find_token(document.header, "\\papersize executivepaper", 0)
2843     if i != -1:
2844         document.header[i] = "\\papersize legalpaper"
2845         return
2846     j = find_token(document.header, "\\papersize legalpaper", 0)
2847     if j != -1:
2848         document.header[j] = "\\papersize executivepaper"
2849
2850
2851 def convert_InsetSpace(document):
2852     " Convert '\\begin_inset Space foo' to '\\begin_inset space foo'"
2853     i = 0
2854     while True:
2855         i = find_token(document.body, "\\begin_inset Space", i)
2856         if i == -1:
2857             return
2858         document.body[i] = document.body[i].replace('\\begin_inset Space', '\\begin_inset space')
2859
2860
2861 def revert_InsetSpace(document):
2862     " Revert '\\begin_inset space foo' to '\\begin_inset Space foo'"
2863     i = 0
2864     while True:
2865         i = find_token(document.body, "\\begin_inset space", i)
2866         if i == -1:
2867             return
2868         document.body[i] = document.body[i].replace('\\begin_inset space', '\\begin_inset Space')
2869
2870
2871 def convert_display_enum(document):
2872     " Convert 'display foo' to 'display false/true'"
2873     i = 0
2874     while True:
2875         i = find_token(document.body, "\tdisplay", i)
2876         if i == -1:
2877             return
2878         val = get_value(document.body, 'display', i)
2879         if val == "none":
2880             document.body[i] = document.body[i].replace('none', 'false')
2881         if val == "default":
2882             document.body[i] = document.body[i].replace('default', 'true')
2883         if val == "monochrome":
2884             document.body[i] = document.body[i].replace('monochrome', 'true')
2885         if val == "grayscale":
2886             document.body[i] = document.body[i].replace('grayscale', 'true')
2887         if val == "color":
2888             document.body[i] = document.body[i].replace('color', 'true')
2889         if val == "preview":
2890             document.body[i] = document.body[i].replace('preview', 'true')
2891         i += 1
2892
2893
2894 def revert_display_enum(document):
2895     " Revert 'display false/true' to 'display none/color'"
2896     i = 0
2897     while True:
2898         i = find_token(document.body, "\tdisplay", i)
2899         if i == -1:
2900             return
2901         val = get_value(document.body, 'display', i)
2902         if val == "false":
2903             document.body[i] = document.body[i].replace('false', 'none')
2904         if val == "true":
2905             document.body[i] = document.body[i].replace('true', 'default')
2906         i += 1
2907
2908
2909 def remove_fontsCJK(document):
2910     ' Remove font_cjk param '
2911     i = find_token(document.header, "\\font_cjk", 0)
2912     if i != -1:
2913         del document.header[i]
2914
2915
2916 def convert_plain_layout(document):
2917     " Convert 'PlainLayout' to 'Plain Layout'"
2918     i = 0
2919     while True:
2920         i = find_token(document.body, '\\begin_layout PlainLayout', i)
2921         if i == -1:
2922             return
2923         document.body[i] = document.body[i].replace('\\begin_layout PlainLayout', \
2924           '\\begin_layout Plain Layout')
2925         i += 1
2926
2927
2928 def revert_plain_layout(document):
2929     " Convert 'PlainLayout' to 'Plain Layout'"
2930     i = 0
2931     while True:
2932         i = find_token(document.body, '\\begin_layout Plain Layout', i)
2933         if i == -1:
2934             return
2935         document.body[i] = document.body[i].replace('\\begin_layout Plain Layout', \
2936           '\\begin_layout PlainLayout')
2937         i += 1
2938
2939
2940 def revert_plainlayout(document):
2941     " Convert 'PlainLayout' to 'Plain Layout'"
2942     i = 0
2943     while True:
2944         i = find_token(document.body, '\\begin_layout PlainLayout', i)
2945         if i == -1:
2946             return
2947         # This will be incorrect for some document classes, since Standard is not always
2948         # the default. But (a) it is probably the best we can do and (b) it will actually
2949         # work, in fact, since an unknown layout will be converted to default.
2950         document.body[i] = document.body[i].replace('\\begin_layout PlainLayout', \
2951           '\\begin_layout Standard')
2952         i += 1
2953
2954
2955 def revert_polytonicgreek(document):
2956     "Set language polytonic Greek to Greek"
2957     i = 0
2958     if document.language == "polutonikogreek":
2959         document.language = "greek"
2960         i = find_token(document.header, "\\language", 0)
2961         if i != -1:
2962             document.header[i] = "\\language greek"
2963     j = 0
2964     while True:
2965         j = find_token(document.body, "\\lang polutonikogreek", j)
2966         if j == -1:
2967             return
2968         document.body[j] = document.body[j].replace("\\lang polutonikogreek", "\\lang greek")
2969         j = j + 1
2970
2971
2972 def revert_removed_modules(document):
2973     i = 0
2974     while True:
2975         i = find_token(document.header, "\\begin_remove_modules", i)
2976         if i == -1:
2977             return
2978         j = find_end_of(document.header, i, "\\begin_remove_modules", "\\end_remove_modules")
2979         if j == -1:
2980             # this should not happen
2981             break
2982         document.header[i : j + 1] = []
2983
2984
2985 def add_plain_layout(document):
2986     i = 0
2987     while True:
2988         i = find_token(document.body, "\\begin_layout", i)
2989         if i == -1:
2990             return
2991         if len(document.body[i].split()) == 1:
2992             document.body[i] = "\\begin_layout Plain Layout"
2993         i += 1
2994
2995
2996 def revert_tabulators(document):
2997     "Revert tabulators to 4 spaces"
2998     i = 0
2999     while True:
3000         i = find_token(document.body, "\t", i)
3001         if i == -1:
3002             return
3003         document.body[i] = document.body[i].replace("\t", "    ")
3004         i += 1
3005
3006
3007 def revert_tabsize(document):
3008     "Revert the tabsize parameter of listings"
3009     i = 0
3010     j = 0
3011     while True:
3012         # either it is the only parameter
3013         i = find_token(document.body, 'lstparams "tabsize=4"', i)
3014         if i != -1:
3015             del document.body[i]
3016         # or the last one
3017         j = find_token(document.body, "lstparams", j)
3018         if j == -1:
3019             return
3020         pos = document.body[j].find(",tabsize=")
3021         document.body[j] = document.body[j][:pos] + '"'
3022         i += 1
3023         j += 1
3024
3025
3026 def revert_mongolian(document):
3027     "Set language Mongolian to English"
3028     i = 0
3029     if document.language == "mongolian":
3030         document.language = "english"
3031         i = find_token(document.header, "\\language", 0)
3032         if i != -1:
3033             document.header[i] = "\\language english"
3034     j = 0
3035     while True:
3036         j = find_token(document.body, "\\lang mongolian", j)
3037         if j == -1:
3038             return
3039         document.body[j] = document.body[j].replace("\\lang mongolian", "\\lang english")
3040         j = j + 1
3041
3042
3043 def revert_default_options(document):
3044     ' Remove param use_default_options '
3045     i = find_token(document.header, "\\use_default_options", 0)
3046     if i != -1:
3047         del document.header[i]
3048
3049
3050 def convert_default_options(document):
3051     ' Add param use_default_options and set it to false '
3052     i = find_token(document.header, "\\textclass", 0)
3053     if i == -1:
3054         document.warning("Malformed LyX document: Missing `\\textclass'.")
3055         return
3056     document.header.insert(i, '\\use_default_options false')
3057
3058
3059 def revert_backref_options(document):
3060     ' Revert option pdf_backref=page to pagebackref '
3061     i = find_token(document.header, "\\pdf_backref page", 0)
3062     if i != -1:
3063         document.header[i] = "\\pdf_pagebackref true"
3064
3065
3066 def convert_backref_options(document):
3067     ' We have changed the option pagebackref to backref=true '
3068     i = find_token(document.header, "\\pdf_pagebackref true", 0)
3069     if i != -1:
3070         document.header[i] = "\\pdf_backref page"
3071     j = find_token(document.header, "\\pdf_pagebackref false", 0)
3072     if j != -1:
3073         del document.header[j]
3074     # backref=true was not a valid option, we meant backref=section
3075     k = find_token(document.header, "\\pdf_backref true", 0)
3076     if k != -1 and i != -1:
3077         del document.header[k]
3078     elif k != -1 and j != -1:
3079         document.header[k] = "\\pdf_backref section"
3080
3081
3082 def convert_charstyle_element(document):
3083     "Convert CharStyle to Element for docbook backend"
3084     if document.backend != "docbook":
3085         return
3086     i = 0
3087     while True:
3088         i = find_token(document.body, "\\begin_inset Flex CharStyle:", i)
3089         if i == -1:
3090             return
3091         document.body[i] = document.body[i].replace('\\begin_inset Flex CharStyle:',
3092                                                     '\\begin_inset Flex Element:')
3093
3094 def revert_charstyle_element(document):
3095     "Convert Element to CharStyle for docbook backend"
3096     if document.backend != "docbook":
3097         return
3098     i = 0
3099     while True:
3100         i = find_token(document.body, "\\begin_inset Flex Element:", i)
3101         if i == -1:
3102             return
3103         document.body[i] = document.body[i].replace('\\begin_inset Flex Element:',
3104                                                     '\\begin_inset Flex CharStyle:')
3105
3106 ##
3107 # Conversion hub
3108 #
3109
3110 supported_versions = ["1.6.0","1.6"]
3111 convert = [[277, [fix_wrong_tables]],
3112            [278, [close_begin_deeper]],
3113            [279, [long_charstyle_names]],
3114            [280, [axe_show_label]],
3115            [281, []],
3116            [282, []],
3117            [283, [convert_flex]],
3118            [284, []],
3119            [285, []],
3120            [286, []],
3121            [287, [convert_wrapfig_options]],
3122            [288, [convert_inset_command]],
3123            [289, [convert_latexcommand_index]],
3124            [290, []],
3125            [291, []],
3126            [292, [convert_japanese_cjk]],
3127            [293, []],
3128            [294, [convert_pdf_options]],
3129            [295, [convert_htmlurl, convert_url]],
3130            [296, [convert_include]],
3131            [297, [convert_usorbian]],
3132            [298, [convert_macro_global]],
3133            [299, []],
3134            [300, []],
3135            [301, []],
3136            [302, []],
3137            [303, [convert_serbocroatian]],
3138            [304, [convert_framed_notes]],
3139            [305, []],
3140            [306, []],
3141            [307, []],
3142            [308, []],
3143            [309, []],
3144            [310, []],
3145            [311, [convert_ams_classes]],
3146            [312, []],
3147            [313, [convert_module_names]],
3148            [314, []],
3149            [315, []],
3150            [316, [convert_subfig]],
3151            [317, []],
3152            [318, []],
3153            [319, [convert_spaceinset, convert_hfill]],
3154            [320, []],
3155            [321, [convert_tablines]],
3156            [322, [convert_plain_layout]],
3157            [323, [convert_pagebreaks]],
3158            [324, [convert_linebreaks]],
3159            [325, [convert_japanese_plain]],
3160            [326, []],
3161            [327, []],
3162            [328, [remove_embedding, remove_extra_embedded_files, remove_inzip_options]],
3163            [329, []],
3164            [330, []],
3165            [331, [convert_ltcaption]],
3166            [332, []],
3167            [333, [update_apa_styles]],
3168            [334, [convert_paper_sizes]],
3169            [335, [convert_InsetSpace]],
3170            [336, []],
3171            [337, [convert_display_enum]],
3172            [338, []],
3173            [339, []],
3174            [340, [add_plain_layout]],
3175            [341, []],
3176            [342, []],
3177            [343, [convert_default_options]],
3178            [344, [convert_backref_options]],
3179            [345, [convert_charstyle_element]]
3180           ]
3181
3182 revert =  [[344, [revert_charstyle_element]],
3183            [343, [revert_backref_options]],
3184            [342, [revert_default_options]],
3185            [341, [revert_mongolian]],
3186            [340, [revert_tabulators, revert_tabsize]],
3187            [339, []],
3188            [338, [revert_removed_modules]],
3189            [337, [revert_polytonicgreek]],
3190            [336, [revert_display_enum]],
3191            [335, [remove_fontsCJK]],
3192            [334, [revert_InsetSpace]],
3193            [333, [revert_paper_sizes]],
3194            [332, []],
3195            [331, [revert_graphics_group]],
3196            [330, [revert_ltcaption]],
3197            [329, [revert_leftarrowfill, revert_rightarrowfill, revert_upbracefill, revert_downbracefill]],
3198            [328, [revert_master]],
3199            [327, []],
3200            [326, [revert_mexican]],
3201            [325, [revert_pdfpages]],
3202            [324, []],
3203            [323, [revert_linebreaks]],
3204            [322, [revert_pagebreaks]],
3205            [321, [revert_local_layout, revert_plain_layout]],
3206            [320, [revert_tablines]],
3207            [319, [revert_protected_hfill]],
3208            [318, [revert_spaceinset, revert_hfills, revert_hspace]],
3209            [317, [remove_extra_embedded_files]],
3210            [316, [revert_wrapplacement]],
3211            [315, [revert_subfig]],
3212            [314, [revert_colsep, revert_plainlayout]],
3213            [313, []],
3214            [312, [revert_module_names]],
3215            [311, [revert_rotfloat, revert_widesideways]],
3216            [310, [revert_external_embedding]],
3217            [309, [revert_btprintall]],
3218            [308, [revert_nocite]],
3219            [307, [revert_serbianlatin]],
3220            [306, [revert_slash, revert_nobreakdash]],
3221            [305, [revert_interlingua]],
3222            [304, [revert_bahasam]],
3223            [303, [revert_framed_notes]],
3224            [302, []],
3225            [301, [revert_latin, revert_samin]],
3226            [300, [revert_linebreak]],
3227            [299, [revert_pagebreak]],
3228            [298, [revert_hyperlinktype]],
3229            [297, [revert_macro_optional_params]],
3230            [296, [revert_albanian, revert_lowersorbian, revert_uppersorbian]],
3231            [295, [revert_include]],
3232            [294, [revert_href, revert_url]],
3233            [293, [revert_pdf_options_2]],
3234            [292, [revert_inset_info]],
3235            [291, [revert_japanese, revert_japanese_encoding, revert_japanese_cjk]],
3236            [290, [revert_vietnamese]],
3237            [289, [revert_wraptable]],
3238            [288, [revert_latexcommand_index]],
3239            [287, [revert_inset_command]],
3240            [286, [revert_wrapfig_options]],
3241            [285, [revert_pdf_options]],
3242            [284, [remove_inzip_options]],
3243            [283, []],
3244            [282, [revert_flex]],
3245            [281, []],
3246            [280, [revert_begin_modules]],
3247            [279, [revert_show_label]],
3248            [278, [revert_long_charstyle_names]],
3249            [277, []],
3250            [276, []]
3251           ]
3252
3253
3254 if __name__ == "__main__":
3255     pass