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