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