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