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