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