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