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