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