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