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