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