]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_1_6.py
3fda0029fba7c421e9c5e9bc180295a28da6df1b
[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   [r'\\\'a', u'á'],
772   [r'\\\'e', u'é'],
773   [r'\\\'i', u'í'],
774   [r'\\\'o', u'ó'],
775   [r'\\\'u', u'ú']
776 ]
777
778 def convert_latexcommand_index(document):
779     "Convert from LatexCommand form to collapsable form."
780     i = 0
781     while True:
782         i = find_token(document.body, "\\begin_inset CommandInset index", i)
783         if i == -1:
784             return
785         if document.body[i + 1] != "LatexCommand index": # Might also be index_print
786             return
787         fullcontent = document.body[i + 2][5:]
788         fullcontent.strip()
789         fullcontent = fullcontent[1:-1]
790         document.body[i:i + 3] = ["\\begin_inset Index",
791           "status collapsed",
792           "\\begin_layout Standard"]
793         i += 3
794         # We are now on the blank line preceding "\end_inset"
795         # We will write the content here, into the inset.
796
797         # Do the LaTeX --> LyX text conversion
798         for rep in replacements:
799             fullcontent = fullcontent.replace(rep[0], rep[1])
800         # Generic, \" -> ":
801         fullcontent = wrap_into_ert(fullcontent, r'\"', '"')
802         # Math:
803         r = re.compile('^(.*?)(\$.*?\$)(.*)')
804         lines = fullcontent.split('\n')
805         for line in lines:
806           #document.warning("LINE: " + line)
807           #document.warning(str(i) + ":" + document.body[i])
808           #document.warning("LAST: " + document.body[-1])
809           g = line
810           while r.match(g):
811             m = r.match(g)
812             s = m.group(1)
813             f = m.group(2).replace('\\\\', '\\')
814             g = m.group(3)
815             if s:
816               # this is non-math!
817               s = wrap_into_ert(s, r'\\', '\\backslash')
818               s = wrap_into_ert(s, '{', '{')
819               s = wrap_into_ert(s, '}', '}')
820               subst = s.split('\n')
821               document.body[i:i] = subst
822               i += len(subst)
823             document.body.insert(i + 1, "\\begin_inset Formula " + f)
824             document.body.insert(i + 2, "\\end_inset")
825             i += 2
826           # Generic, \\ -> \backslash:
827           g = wrap_into_ert(g, r'\\', '\\backslash')
828           g = wrap_into_ert(g, '{', '{')
829           g = wrap_into_ert(g, '}', '}')
830           subst = g.split('\n')
831           document.body[i+1:i+1] = subst
832           i += len(subst)
833         document.body.insert(i + 1, "\\end_layout")
834
835
836 def revert_latexcommand_index(document):
837     "Revert from collapsable form to LatexCommand form."
838     i = 0
839     while True:
840         i = find_token(document.body, "\\begin_inset Index", i)
841         if i == -1:
842           return
843         j = find_end_of_inset(document.body, i + 1)
844         if j == -1:
845           return
846         del document.body[j - 1]
847         del document.body[j - 2] # \end_layout
848         document.body[i] =  "\\begin_inset CommandInset index"
849         document.body[i + 1] =  "LatexCommand index"
850         # clean up multiline stuff
851         content = ""
852         ert_end = 0
853         for k in range(i + 3, j - 2):
854           line = document.body[k]
855           if line.startswith("\\begin_inset ERT"):
856               ert_end = find_end_of_inset(document.body, k + 1)
857               line = line[16:]
858           if line.startswith("\\begin_inset Formula"):
859             line = line[20:]
860           if line.startswith("\\begin_layout Standard"):
861             line = line[22:]
862           if line.startswith("\\begin_layout Plain Layout"):
863             line = line[26:]
864           if line.startswith("\\end_layout"):
865             line = line[11:]
866           if line.startswith("\\end_inset"):
867             line = line[10:]
868           if line.startswith("status collapsed"):
869             line = line[16:]
870           if line.startswith("status open"):
871             line = line[11:]
872           # a lossless reversion is not possible
873           # try at least to handle some common insets and settings
874           # do not replace inside ERTs
875           if ert_end < k:
876               # Do the LyX text --> LaTeX conversion
877               for rep in replacements:
878                 line = line.replace(rep[1], rep[0])
879               line = line.replace(r'\backslash', r'\textbackslash{}')
880               line = line.replace(r'\series bold', r'\bfseries{}').replace(r'\series default', r'\mdseries{}')
881               line = line.replace(r'\shape italic', r'\itshape{}').replace(r'\shape smallcaps', r'\scshape{}')
882               line = line.replace(r'\shape slanted', r'\slshape{}').replace(r'\shape default', r'\upshape{}')
883               line = line.replace(r'\emph on', r'\em{}').replace(r'\emph default', r'\em{}')
884               line = line.replace(r'\noun on', r'\scshape{}').replace(r'\noun default', r'\upshape{}')
885               line = line.replace(r'\bar under', r'\underbar{').replace(r'\bar default', r'}')
886               line = line.replace(r'\family sans', r'\sffamily{}').replace(r'\family default', r'\normalfont{}')
887               line = line.replace(r'\family typewriter', r'\ttfamily{}').replace(r'\family roman', r'\rmfamily{}')
888               line = line.replace(r'\InsetSpace ', r'').replace(r'\SpecialChar ', r'')
889           else:
890               line = line.replace(r'\backslash', r'\\')
891           content = content + line;
892         document.body[i + 3] = "name " + '"' + content + '"'
893         for k in range(i + 4, j - 2):
894           del document.body[i + 4]
895         document.body.insert(i + 4, "")
896         del document.body[i + 2] # \begin_layout standard
897         i = i + 5
898
899
900 def revert_wraptable(document):
901     "Revert wrap table to wrap figure."
902     i = 0
903     while True:
904         i = find_token(document.body, "\\begin_inset Wrap table", i)
905         if i == -1:
906             return
907         document.body[i] = document.body[i].replace('\\begin_inset Wrap table', '\\begin_inset Wrap figure')
908         i = i + 1
909
910
911 def revert_vietnamese(document):
912     "Set language Vietnamese to English"
913     # Set document language from Vietnamese to English
914     i = 0
915     if document.language == "vietnamese":
916         document.language = "english"
917         i = find_token(document.header, "\\language", 0)
918         if i != -1:
919             document.header[i] = "\\language english"
920     j = 0
921     while True:
922         j = find_token(document.body, "\\lang vietnamese", j)
923         if j == -1:
924             return
925         document.body[j] = document.body[j].replace("\\lang vietnamese", "\\lang english")
926         j = j + 1
927
928
929 def revert_japanese(document):
930     "Set language japanese-plain to japanese"
931     # Set document language from japanese-plain to japanese
932     i = 0
933     if document.language == "japanese-plain":
934         document.language = "japanese"
935         i = find_token(document.header, "\\language", 0)
936         if i != -1:
937             document.header[i] = "\\language japanese"
938     j = 0
939     while True:
940         j = find_token(document.body, "\\lang japanese-plain", j)
941         if j == -1:
942             return
943         document.body[j] = document.body[j].replace("\\lang japanese-plain", "\\lang japanese")
944         j = j + 1
945
946
947 def revert_japanese_encoding(document):
948     "Set input encoding form EUC-JP-plain to EUC-JP etc."
949     # Set input encoding form EUC-JP-plain to EUC-JP etc.
950     i = 0
951     i = find_token(document.header, "\\inputencoding EUC-JP-plain", 0)
952     if i != -1:
953         document.header[i] = "\\inputencoding EUC-JP"
954     j = 0
955     j = find_token(document.header, "\\inputencoding JIS-plain", 0)
956     if j != -1:
957         document.header[j] = "\\inputencoding JIS"
958     k = 0
959     k = find_token(document.header, "\\inputencoding SJIS-plain", 0)
960     if k != -1: # convert to UTF8 since there is currently no SJIS encoding
961         document.header[k] = "\\inputencoding UTF8"
962
963
964 def revert_inset_info(document):
965     'Replace info inset with its content'
966     i = 0
967     while 1:
968         i = find_token(document.body, '\\begin_inset Info', i)
969         if i == -1:
970             return
971         j = find_end_of_inset(document.body, i + 1)
972         if j == -1:
973             # should not happen
974             document.warning("Malformed LyX document: Could not find end of Info inset.")
975         type = 'unknown'
976         arg = ''
977         for k in range(i, j+1):
978             if document.body[k].startswith("arg"):
979                 arg = document.body[k][3:].strip().strip('"')
980             if document.body[k].startswith("type"):
981                 type = document.body[k][4:].strip().strip('"')
982         # I think there is a newline after \\end_inset, which should be removed.
983         if document.body[j + 1].strip() == "":
984             document.body[i : (j + 2)] = [type + ':' + arg]
985         else:
986             document.body[i : (j + 1)] = [type + ':' + arg]
987
988
989 def convert_pdf_options(document):
990     # Set the pdfusetitle tag, delete the pdf_store_options,
991     # set quotes for bookmarksopenlevel"
992     has_hr = get_value(document.header, "\\use_hyperref", 0, default = "0")
993     if has_hr == "1":
994         k = find_token(document.header, "\\use_hyperref", 0)
995         document.header.insert(k + 1, "\\pdf_pdfusetitle true")
996     k = find_token(document.header, "\\pdf_store_options", 0)
997     if k != -1:
998         del document.header[k]
999     i = find_token(document.header, "\\pdf_bookmarksopenlevel", k)
1000     if i == -1: return
1001     document.header[i] = document.header[i].replace('"', '')
1002
1003
1004 def revert_pdf_options_2(document):
1005     # reset the pdfusetitle tag, set quotes for bookmarksopenlevel"
1006     k = find_token(document.header, "\\use_hyperref", 0)
1007     i = find_token(document.header, "\\pdf_pdfusetitle", k)
1008     if i != -1:
1009         del document.header[i]
1010     i = find_token(document.header, "\\pdf_bookmarksopenlevel", k)
1011     if i == -1: return
1012     values = document.header[i].split()
1013     values[1] = ' "' + values[1] + '"'
1014     document.header[i] = ''.join(values)
1015
1016
1017 def convert_htmlurl(document):
1018     'Convert "htmlurl" to "href" insets for docbook'
1019     if document.backend != "docbook":
1020       return
1021     i = 0
1022     while True:
1023       i = find_token(document.body, "\\begin_inset CommandInset url", i)
1024       if i == -1:
1025         return
1026       document.body[i] = "\\begin_inset CommandInset href"
1027       document.body[i + 1] = "LatexCommand href"
1028       i = i + 1
1029
1030
1031 def convert_url(document):
1032     'Convert url insets to url charstyles'
1033     if document.backend == "docbook":
1034       return
1035     i = 0
1036     while True:
1037       i = find_token(document.body, "\\begin_inset CommandInset url", i)
1038       if i == -1:
1039         break
1040       n = find_token(document.body, "name", i)
1041       if n == i + 2:
1042         # place the URL name in typewriter before the new URL insert
1043         # grab the name 'bla' from the e.g. the line 'name "bla"',
1044         # therefore start with the 6th character
1045         name = document.body[n][6:-1]
1046         newname = [name + " "]
1047         document.body[i:i] = newname
1048         i = i + 1
1049       j = find_token(document.body, "target", i)
1050       if j == -1:
1051         document.warning("Malformed LyX document: Can't find target for url inset")
1052         i = j
1053         continue
1054       target = document.body[j][8:-1]
1055       k = find_token(document.body, "\\end_inset", j)
1056       if k == -1:
1057         document.warning("Malformed LyX document: Can't find end of url inset")
1058         i = k
1059         continue
1060       newstuff = ["\\begin_inset Flex URL",
1061         "status collapsed", "",
1062         "\\begin_layout Standard",
1063         "",
1064         target,
1065         "\\end_layout",
1066         ""]
1067       document.body[i:k] = newstuff
1068       i = k
1069
1070 def convert_ams_classes(document):
1071   tc = document.textclass
1072   if (tc != "amsart" and tc != "amsart-plain" and
1073       tc != "amsart-seq" and tc != "amsbook"):
1074     return
1075   if tc == "amsart-plain":
1076     document.textclass = "amsart"
1077     document.set_textclass()
1078     document.add_module("Theorems (Starred)")
1079     return
1080   if tc == "amsart-seq":
1081     document.textclass = "amsart"
1082     document.set_textclass()
1083   document.add_module("Theorems (AMS)")
1084
1085   #Now we want to see if any of the environments in the extended theorems
1086   #module were used in this document. If so, we'll add that module, too.
1087   layouts = ["Criterion", "Algorithm", "Axiom", "Condition", "Note",  \
1088     "Notation", "Summary", "Acknowledgement", "Conclusion", "Fact", \
1089     "Assumption"]
1090
1091   r = re.compile(r'^\\begin_layout (.*?)\*?\s*$')
1092   i = 0
1093   while True:
1094     i = find_token(document.body, "\\begin_layout", i)
1095     if i == -1:
1096       return
1097     m = r.match(document.body[i])
1098     if m == None:
1099       document.warning("Weirdly formed \\begin_layout at line %d of body!" % i)
1100       i += 1
1101       continue
1102     m = m.group(1)
1103     if layouts.count(m) != 0:
1104       document.add_module("Theorems (AMS-Extended)")
1105       return
1106     i += 1
1107
1108 def revert_href(document):
1109     'Reverts hyperlink insets (href) to url insets (url)'
1110     i = 0
1111     while True:
1112       i = find_token(document.body, "\\begin_inset CommandInset href", i)
1113       if i == -1:
1114           return
1115       document.body[i : i + 2] = \
1116         ["\\begin_inset CommandInset url", "LatexCommand url"]
1117       i = i + 2
1118
1119
1120 def convert_include(document):
1121   'Converts include insets to new format.'
1122   i = 0
1123   r = re.compile(r'\\begin_inset Include\s+\\([^{]+){([^}]*)}(?:\[(.*)\])?')
1124   while True:
1125     i = find_token(document.body, "\\begin_inset Include", i)
1126     if i == -1:
1127       return
1128     line = document.body[i]
1129     previewline = document.body[i + 1]
1130     m = r.match(line)
1131     if m == None:
1132       document.warning("Unable to match line " + str(i) + " of body!")
1133       i += 1
1134       continue
1135     cmd = m.group(1)
1136     fn  = m.group(2)
1137     opt = m.group(3)
1138     insertion = ["\\begin_inset CommandInset include",
1139        "LatexCommand " + cmd, previewline,
1140        "filename \"" + fn + "\""]
1141     newlines = 2
1142     if opt:
1143       insertion.append("lstparams " + '"' + opt + '"')
1144       newlines += 1
1145     document.body[i : i + 2] = insertion
1146     i += newlines
1147
1148
1149 def revert_include(document):
1150   'Reverts include insets to old format.'
1151   i = 0
1152   r1 = re.compile('LatexCommand (.+)')
1153   r2 = re.compile('filename (.+)')
1154   r3 = re.compile('options (.*)')
1155   while True:
1156     i = find_token(document.body, "\\begin_inset CommandInset include", i)
1157     if i == -1:
1158       return
1159     previewline = document.body[i + 1]
1160     m = r1.match(document.body[i + 2])
1161     if m == None:
1162       document.warning("Malformed LyX document: No LatexCommand line for `" +
1163         document.body[i] + "' on line " + str(i) + ".")
1164       i += 1
1165       continue
1166     cmd = m.group(1)
1167     m = r2.match(document.body[i + 3])
1168     if m == None:
1169       document.warning("Malformed LyX document: No filename line for `" + \
1170         document.body[i] + "' on line " + str(i) + ".")
1171       i += 2
1172       continue
1173     fn = m.group(1)
1174     options = ""
1175     numlines = 4
1176     if (cmd == "lstinputlisting"):
1177       m = r3.match(document.body[i + 4])
1178       if m != None:
1179         options = m.group(1)
1180         numlines = 5
1181     newline = "\\begin_inset Include \\" + cmd + "{" + fn + "}"
1182     if options:
1183       newline += ("[" + options + "]")
1184     insertion = [newline, previewline]
1185     document.body[i : i + numlines] = insertion
1186     i += 2
1187
1188
1189 def revert_albanian(document):
1190     "Set language Albanian to English"
1191     i = 0
1192     if document.language == "albanian":
1193         document.language = "english"
1194         i = find_token(document.header, "\\language", 0)
1195         if i != -1:
1196             document.header[i] = "\\language english"
1197     j = 0
1198     while True:
1199         j = find_token(document.body, "\\lang albanian", j)
1200         if j == -1:
1201             return
1202         document.body[j] = document.body[j].replace("\\lang albanian", "\\lang english")
1203         j = j + 1
1204
1205
1206 def revert_lowersorbian(document):
1207     "Set language lower Sorbian to English"
1208     i = 0
1209     if document.language == "lowersorbian":
1210         document.language = "english"
1211         i = find_token(document.header, "\\language", 0)
1212         if i != -1:
1213             document.header[i] = "\\language english"
1214     j = 0
1215     while True:
1216         j = find_token(document.body, "\\lang lowersorbian", j)
1217         if j == -1:
1218             return
1219         document.body[j] = document.body[j].replace("\\lang lowersorbian", "\\lang english")
1220         j = j + 1
1221
1222
1223 def revert_uppersorbian(document):
1224     "Set language uppersorbian to usorbian as this was used in LyX 1.5"
1225     i = 0
1226     if document.language == "uppersorbian":
1227         document.language = "usorbian"
1228         i = find_token(document.header, "\\language", 0)
1229         if i != -1:
1230             document.header[i] = "\\language usorbian"
1231     j = 0
1232     while True:
1233         j = find_token(document.body, "\\lang uppersorbian", j)
1234         if j == -1:
1235             return
1236         document.body[j] = document.body[j].replace("\\lang uppersorbian", "\\lang usorbian")
1237         j = j + 1
1238
1239
1240 def convert_usorbian(document):
1241     "Set language usorbian to uppersorbian"
1242     i = 0
1243     if document.language == "usorbian":
1244         document.language = "uppersorbian"
1245         i = find_token(document.header, "\\language", 0)
1246         if i != -1:
1247             document.header[i] = "\\language uppersorbian"
1248     j = 0
1249     while True:
1250         j = find_token(document.body, "\\lang usorbian", j)
1251         if j == -1:
1252             return
1253         document.body[j] = document.body[j].replace("\\lang usorbian", "\\lang uppersorbian")
1254         j = j + 1
1255
1256
1257 def revert_macro_optional_params(document):
1258     "Convert macro definitions with optional parameters into ERTs"
1259     # Stub to convert macro definitions with one or more optional parameters
1260     # into uninterpreted ERT insets
1261
1262
1263 def revert_hyperlinktype(document):
1264     'Reverts hyperlink type'
1265     i = 0
1266     j = 0
1267     while True:
1268       i = find_token(document.body, "target", i)
1269       if i == -1:
1270           return
1271       j = find_token(document.body, "type", i)
1272       if j == -1:
1273           return
1274       if j == i + 1:
1275           del document.body[j]
1276       i = i + 1
1277
1278
1279 def revert_pagebreak(document):
1280     'Reverts pagebreak to ERT'
1281     i = 0
1282     while True:
1283       i = find_token(document.body, "\\pagebreak", i)
1284       if i == -1:
1285           return
1286       document.body[i] = '\\begin_inset ERT\nstatus collapsed\n\n' \
1287       '\\begin_layout Standard\n\n\n\\backslash\n' \
1288       'pagebreak{}\n\\end_layout\n\n\\end_inset\n\n'
1289       i = i + 1
1290
1291
1292 def revert_linebreak(document):
1293     'Reverts linebreak to ERT'
1294     i = 0
1295     while True:
1296       i = find_token(document.body, "\\linebreak", i)
1297       if i == -1:
1298           return
1299       document.body[i] = '\\begin_inset ERT\nstatus collapsed\n\n' \
1300       '\\begin_layout Standard\n\n\n\\backslash\n' \
1301       'linebreak{}\n\\end_layout\n\n\\end_inset\n\n'
1302       i = i + 1
1303
1304
1305 def revert_latin(document):
1306     "Set language Latin to English"
1307     i = 0
1308     if document.language == "latin":
1309         document.language = "english"
1310         i = find_token(document.header, "\\language", 0)
1311         if i != -1:
1312             document.header[i] = "\\language english"
1313     j = 0
1314     while True:
1315         j = find_token(document.body, "\\lang latin", j)
1316         if j == -1:
1317             return
1318         document.body[j] = document.body[j].replace("\\lang latin", "\\lang english")
1319         j = j + 1
1320
1321
1322 def revert_samin(document):
1323     "Set language North Sami to English"
1324     i = 0
1325     if document.language == "samin":
1326         document.language = "english"
1327         i = find_token(document.header, "\\language", 0)
1328         if i != -1:
1329             document.header[i] = "\\language english"
1330     j = 0
1331     while True:
1332         j = find_token(document.body, "\\lang samin", j)
1333         if j == -1:
1334             return
1335         document.body[j] = document.body[j].replace("\\lang samin", "\\lang english")
1336         j = j + 1
1337
1338
1339 def convert_serbocroatian(document):
1340     "Set language Serbocroatian to Croatian as this was really Croatian in LyX 1.5"
1341     i = 0
1342     if document.language == "serbocroatian":
1343         document.language = "croatian"
1344         i = find_token(document.header, "\\language", 0)
1345         if i != -1:
1346             document.header[i] = "\\language croatian"
1347     j = 0
1348     while True:
1349         j = find_token(document.body, "\\lang serbocroatian", j)
1350         if j == -1:
1351             return
1352         document.body[j] = document.body[j].replace("\\lang serbocroatian", "\\lang croatian")
1353         j = j + 1
1354
1355
1356 def convert_framed_notes(document):
1357     "Convert framed notes to boxes. "
1358     i = 0
1359     while 1:
1360         i = find_tokens(document.body, ["\\begin_inset Note Framed", "\\begin_inset Note Shaded"], i)
1361         if i == -1:
1362             return
1363         subst = [document.body[i].replace("\\begin_inset Note", "\\begin_inset Box"),
1364                  'position "t"',
1365                  'hor_pos "c"',
1366                  'has_inner_box 0',
1367                  'inner_pos "t"', 
1368                  'use_parbox 0',
1369                  'width "100col%"',
1370                  'special "none"',
1371                  'height "1in"',
1372                  'height_special "totalheight"']
1373         document.body[i:i+1] = subst
1374         i = i + 9
1375
1376
1377 def convert_module_names(document):
1378   modulemap = { 'Braille' : 'braille', 'Endnote' : 'endnotes', 'Foot to End' : 'foottoend',\
1379     'Hanging' : 'hanging', 'Linguistics' : 'linguistics', 'Logical Markup' : 'logicalmkup', \
1380     'Theorems (AMS-Extended)' : 'theorems-ams-extended', 'Theorems (AMS)' : 'theorems-ams', \
1381     'Theorems (Order By Chapter)' : 'theorems-chap', 'Theorems (Order By Section)' : 'theorems-sec', \
1382     'Theorems (Starred)' : 'theorems-starred', 'Theorems' : 'theorems-std' }
1383   modlist = document.get_module_list()
1384   if len(modlist) == 0:
1385     return
1386   newmodlist = []
1387   for mod in modlist:
1388     if modulemap.has_key(mod):
1389       newmodlist.append(modulemap[mod])
1390     else:
1391       document.warning("Can't find module %s in the module map!" % mod)
1392       newmodlist.append(mod)
1393   document.set_module_list(newmodlist)
1394
1395
1396 def revert_module_names(document):
1397   modulemap = { 'braille' : 'Braille', 'endnotes' : 'Endnote', 'foottoend' : 'Foot to End',\
1398     'hanging' : 'Hanging', 'linguistics' : 'Linguistics', 'logicalmkup' : 'Logical Markup', \
1399     'theorems-ams-extended' : 'Theorems (AMS-Extended)', 'theorems-ams' : 'Theorems (AMS)', \
1400     'theorems-chap' : 'Theorems (Order By Chapter)', 'theorems-sec' : 'Theorems (Order By Section)', \
1401     'theorems-starred' : 'Theorems (Starred)', 'theorems-std' : 'Theorems'}
1402   modlist = document.get_module_list()
1403   if len(modlist) == 0:
1404     return
1405   newmodlist = []
1406   for mod in modlist:
1407     if modulemap.has_key(mod):
1408       newmodlist.append(modulemap[mod])
1409     else:
1410       document.warning("Can't find module %s in the module map!" % mod)
1411       newmodlist.append(mod)
1412   document.set_module_list(newmodlist)
1413
1414
1415 def revert_colsep(document):
1416     i = find_token(document.header, "\\columnsep", 0)
1417     if i == -1:
1418         return
1419     colsepline = document.header[i]
1420     r = re.compile(r'\\columnsep (.*)')
1421     m = r.match(colsepline)
1422     if not m:
1423         document.warning("Malformed column separation line!")
1424         return
1425     colsep = m.group(1)
1426     del document.header[i]
1427     #it seems to be safe to add the package even if it is already used
1428     pretext = ["\\usepackage{geometry}", "\\geometry{columnsep=" + colsep + "}"]
1429
1430     add_to_preamble(document, pretext)
1431
1432
1433 def revert_framed_notes(document):
1434     "Revert framed boxes to notes. "
1435     i = 0
1436     while 1:
1437         i = find_tokens(document.body, ["\\begin_inset Box Framed", "\\begin_inset Box Shaded"], i)
1438
1439         if i == -1:
1440             return
1441         j = find_end_of_inset(document.body, i + 1)
1442         if j == -1:
1443             # should not happen
1444             document.warning("Malformed LyX document: Could not find end of Box inset.")
1445         k = find_token(document.body, "status", i + 1, j)
1446         if k == -1:
1447             document.warning("Malformed LyX document: Missing `status' tag in Box inset.")
1448             return
1449         status = document.body[k]
1450         l = find_default_layout(document, i + 1, j)
1451         if l == -1:
1452             document.warning("Malformed LyX document: Missing `\\begin_layout' in Box inset.")
1453             return
1454         m = find_token(document.body, "\\end_layout", i + 1, j)
1455         if m == -1:
1456             document.warning("Malformed LyX document: Missing `\\end_layout' in Box inset.")
1457             return
1458         ibox = find_token(document.body, "has_inner_box 1", i + 1, k)
1459         pbox = find_token(document.body, "use_parbox 1", i + 1, k)
1460         if ibox == -1 and pbox == -1:
1461             document.body[i] = document.body[i].replace("\\begin_inset Box", "\\begin_inset Note")
1462             del document.body[i+1:k]
1463         else:
1464             document.body[i] = document.body[i].replace("\\begin_inset Box Shaded", "\\begin_inset Box Frameless")
1465             subst1 = [document.body[l],
1466                       "\\begin_inset Note Shaded",
1467                       status,
1468                       '\\begin_layout Standard']
1469             document.body[l:l + 1] = subst1
1470             subst2 = [document.body[m], "\\end_layout", "\\end_inset"]
1471             document.body[m:m + 1] = subst2
1472         i = i + 1
1473
1474
1475 def revert_slash(document):
1476     'Revert \\SpecialChar \\slash{} to ERT'
1477     r = re.compile(r'\\SpecialChar \\slash{}')
1478     i = 0
1479     while i < len(document.body):
1480         m = r.match(document.body[i])
1481         if m:
1482           subst = ['\\begin_inset ERT',
1483                    'status collapsed', '',
1484                    '\\begin_layout Standard',
1485                    '', '', '\\backslash',
1486                    'slash{}',
1487                    '\\end_layout', '',
1488                    '\\end_inset', '']
1489           document.body[i: i+1] = subst
1490           i = i + len(subst)
1491         else:
1492           i = i + 1
1493
1494
1495 def revert_nobreakdash(document):
1496     'Revert \\SpecialChar \\nobreakdash- to ERT'
1497     i = 0
1498     while i < len(document.body):
1499         line = document.body[i]
1500         r = re.compile(r'\\SpecialChar \\nobreakdash-')
1501         m = r.match(line)
1502         if m:
1503             subst = ['\\begin_inset ERT',
1504                     'status collapsed', '',
1505                     '\\begin_layout Standard', '', '',
1506                     '\\backslash',
1507                     'nobreakdash-',
1508                     '\\end_layout', '',
1509                     '\\end_inset', '']
1510             document.body[i:i+1] = subst
1511             i = i + len(subst)
1512             j = find_token(document.header, "\\use_amsmath", 0)
1513             if j == -1:
1514                 document.warning("Malformed LyX document: Missing '\\use_amsmath'.")
1515                 return
1516             document.header[j] = "\\use_amsmath 2"
1517         else:
1518             i = i + 1
1519
1520
1521 #Returns number of lines added/removed
1522 def revert_nocite_key(body, start, end):
1523     'key "..." -> \nocite{...}' 
1524     r = re.compile(r'^key "(.*)"')
1525     i = start
1526     j = end
1527     while i < j:
1528         m = r.match(body[i])
1529         if m:
1530             body[i:i+1] = ["\\backslash", "nocite{" + m.group(1) + "}"]
1531             j += 1     # because we added a line
1532             i += 2     # skip that line
1533         else:
1534             del body[i]
1535             j -= 1     # because we deleted a line
1536             # no need to change i, since it now points to the next line
1537     return j - end
1538
1539
1540 def revert_nocite(document):
1541     "Revert LatexCommand nocite to ERT"
1542     i = 0
1543     while 1:
1544         i = find_token(document.body, "\\begin_inset CommandInset citation", i)
1545         if i == -1:
1546             return
1547         if (document.body[i+1] != "LatexCommand nocite"):
1548             # note that we already incremented i
1549             i = i + 1
1550             continue
1551         insetEnd = find_end_of_inset(document.body, i)
1552         if insetEnd == -1:
1553             #this should not happen
1554             document.warning("End of CommandInset citation not found in revert_nocite!")
1555             return
1556
1557         paramLocation = i + 2 #start of the inset's parameters
1558         addedLines = 0
1559         document.body[i:i+2] = \
1560             ["\\begin_inset ERT", "status collapsed", "", "\\begin_layout Standard"]
1561         # that added two lines
1562         paramLocation += 2
1563         insetEnd += 2
1564         #print insetEnd, document.body[i: insetEnd + 1]
1565         insetEnd += revert_nocite_key(document.body, paramLocation, insetEnd)
1566         #print insetEnd, document.body[i: insetEnd + 1]
1567         document.body.insert(insetEnd, "\\end_layout")
1568         document.body.insert(insetEnd + 1, "")
1569         i = insetEnd + 1
1570
1571
1572 def revert_btprintall(document):
1573     "Revert (non-bibtopic) btPrintAll option to ERT \nocite{*}"
1574     i = find_token(document.header, '\\use_bibtopic', 0)
1575     if i == -1:
1576         document.warning("Malformed lyx document: Missing '\\use_bibtopic'.")
1577         return
1578     if get_value(document.header, '\\use_bibtopic', 0) == "false":
1579         i = 0
1580         while i < len(document.body):
1581             i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
1582             if i == -1:
1583                 return
1584             j = find_end_of_inset(document.body, i + 1)
1585             if j == -1:
1586                 #this should not happen
1587                 document.warning("End of CommandInset bibtex not found in revert_btprintall!")
1588                 j = len(document.body)
1589             # this range isn't really right, but it should be OK, since we shouldn't
1590             # see more than one matching line in each inset
1591             addedlines = 0
1592             for k in range(i, j):
1593                 if (document.body[k] == 'btprint "btPrintAll"'):
1594                     del document.body[k]
1595                     subst = ["\\begin_inset ERT",
1596                              "status collapsed", "",
1597                              "\\begin_layout Standard", "",
1598                              "\\backslash",
1599                              "nocite{*}",
1600                              "\\end_layout",
1601                              "\\end_inset"]
1602                     document.body[i:i] = subst
1603                     addlines = addedlines + len(subst) - 1
1604             i = j + addedlines
1605
1606
1607 def revert_bahasam(document):
1608     "Set language Bahasa Malaysia to Bahasa Indonesia"
1609     i = 0
1610     if document.language == "bahasam":
1611         document.language = "bahasa"
1612         i = find_token(document.header, "\\language", 0)
1613         if i != -1:
1614             document.header[i] = "\\language bahasa"
1615     j = 0
1616     while True:
1617         j = find_token(document.body, "\\lang bahasam", j)
1618         if j == -1:
1619             return
1620         document.body[j] = document.body[j].replace("\\lang bahasam", "\\lang bahasa")
1621         j = j + 1
1622
1623
1624 def revert_interlingua(document):
1625     "Set language Interlingua to English"
1626     i = 0
1627     if document.language == "interlingua":
1628         document.language = "english"
1629         i = find_token(document.header, "\\language", 0)
1630         if i != -1:
1631             document.header[i] = "\\language english"
1632     j = 0
1633     while True:
1634         j = find_token(document.body, "\\lang interlingua", j)
1635         if j == -1:
1636             return
1637         document.body[j] = document.body[j].replace("\\lang interlingua", "\\lang english")
1638         j = j + 1
1639
1640
1641 def revert_serbianlatin(document):
1642     "Set language Serbian-Latin to Croatian"
1643     i = 0
1644     if document.language == "serbian-latin":
1645         document.language = "croatian"
1646         i = find_token(document.header, "\\language", 0)
1647         if i != -1:
1648             document.header[i] = "\\language croatian"
1649     j = 0
1650     while True:
1651         j = find_token(document.body, "\\lang serbian-latin", j)
1652         if j == -1:
1653             return
1654         document.body[j] = document.body[j].replace("\\lang serbian-latin", "\\lang croatian")
1655         j = j + 1
1656
1657
1658 def revert_rotfloat(document):
1659     " Revert sideways custom floats. "
1660     i = 0
1661     while 1:
1662         # whitespace intended (exclude \\begin_inset FloatList)
1663         i = find_token(document.body, "\\begin_inset Float ", i)
1664         if i == -1:
1665             return
1666         line = document.body[i]
1667         r = re.compile(r'\\begin_inset Float (.*)$')
1668         m = r.match(line)
1669         if m == None:
1670             document.warning("Unable to match line " + str(i) + " of body!")
1671             i += 1
1672             continue
1673         floattype = m.group(1)
1674         if floattype == "figure" or floattype == "table":
1675             i += 1
1676             continue
1677         j = find_end_of_inset(document.body, i)
1678         if j == -1:
1679             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_rotfloat.")
1680             i += 1
1681             continue
1682         addedLines = 0
1683         if get_value(document.body, 'sideways', i, j) == "false":
1684             i += 1
1685             continue
1686         l = find_default_layout(document, i + 1, j)
1687         if l == -1:
1688             document.warning("Malformed LyX document: Missing `\\begin_layout' in Float inset.")
1689             return
1690         subst = ['\\begin_layout Standard',
1691                   '\\begin_inset ERT',
1692                   'status collapsed', '',
1693                   '\\begin_layout Standard', '', '', 
1694                   '\\backslash', '',
1695                   'end{sideways' + floattype + '}',
1696                   '\\end_layout', '', '\\end_inset']
1697         document.body[j : j+1] = subst
1698         addedLines = len(subst) - 1
1699         del document.body[i+1 : l]
1700         addedLines -= (l-1) - (i+1) 
1701         subst = ['\\begin_inset ERT', 'status collapsed', '',
1702                   '\\begin_layout Standard', '', '', '\\backslash', 
1703                   'begin{sideways' + floattype + '}', 
1704                   '\\end_layout', '', '\\end_inset', '',
1705                   '\\end_layout', '']
1706         document.body[i : i+1] = subst
1707         addedLines += len(subst) - 1
1708         if floattype == "algorithm":
1709             add_to_preamble(document,
1710                             ['% Commands inserted by lyx2lyx for sideways algorithm float',
1711                               '\\usepackage{rotfloat}',
1712                               '\\floatstyle{ruled}',
1713                               '\\newfloat{algorithm}{tbp}{loa}',
1714                               '\\floatname{algorithm}{Algorithm}'])
1715         else:
1716             document.warning("Cannot create preamble definition for custom float" + floattype + ".")
1717         i += addedLines + 1
1718
1719
1720 def revert_widesideways(document):
1721     " Revert wide sideways floats. "
1722     i = 0
1723     while 1:
1724         # whitespace intended (exclude \\begin_inset FloatList)
1725         i = find_token(document.body, '\\begin_inset Float ', i)
1726         if i == -1:
1727             return
1728         line = document.body[i]
1729         r = re.compile(r'\\begin_inset Float (.*)$')
1730         m = r.match(line)
1731         if m == None:
1732             document.warning("Unable to match line " + str(i) + " of body!")
1733             i += 1
1734             continue
1735         floattype = m.group(1)
1736         if floattype != "figure" and floattype != "table":
1737             i += 1
1738             continue
1739         j = find_end_of_inset(document.body, i)
1740         if j == -1:
1741             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_widesideways.")
1742             i += 1
1743             continue
1744         if get_value(document.body, 'sideways', i, j) == "false" or \
1745            get_value(document.body, 'wide', i, j) == "false":
1746              i += 1
1747              continue
1748         l = find_default_layout(document, i + 1, j)
1749         if l == -1:
1750             document.warning("Malformed LyX document: Missing `\\begin_layout' in Float inset.")
1751             return
1752         subst = ['\\begin_layout Standard', '\\begin_inset ERT', 
1753                   'status collapsed', '', 
1754                   '\\begin_layout Standard', '', '', '\\backslash',
1755                   'end{sideways' + floattype + '*}', 
1756                   '\\end_layout', '', '\\end_inset']
1757         document.body[j : j+1] = subst
1758         addedLines = len(subst) - 1
1759         del document.body[i+1:l-1]
1760         addedLines -= (l-1) - (i+1)
1761         subst = ['\\begin_inset ERT', 'status collapsed', '',
1762                  '\\begin_layout Standard', '', '', '\\backslash',
1763                  'begin{sideways' + floattype + '*}', '\\end_layout', '',
1764                  '\\end_inset', '', '\\end_layout', '']
1765         document.body[i : i+1] = subst
1766         addedLines += len(subst) - 1
1767         add_to_preamble(document, ['\\usepackage{rotfloat}\n'])
1768         i += addedLines + 1
1769
1770
1771 def revert_inset_embedding(document, type):
1772     ' Remove embed tag from certain type of insets'
1773     i = 0
1774     while 1:
1775         i = find_token(document.body, "\\begin_inset %s" % type, i)
1776         if i == -1:
1777             return
1778         j = find_end_of_inset(document.body, i)
1779         if j == -1:
1780             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_inset_embedding.")
1781             i = i + 1
1782             continue
1783         k = find_token(document.body, "\tembed", i, j)
1784         if k == -1:
1785             k = find_token(document.body, "embed", i, j)
1786         if k != -1:
1787             del document.body[k]
1788         i = i + 1
1789
1790
1791 def revert_external_embedding(document):
1792     ' Remove embed tag from external inset '
1793     revert_inset_embedding(document, 'External')
1794
1795
1796 # FIXME This code can still be cleaned up a fair bit.
1797 def convert_subfig(document):
1798     " Convert subfigures to subfloats. "
1799     i = 0
1800     while 1:
1801         i = find_token(document.body, '\\begin_inset Graphics', i)
1802         if i == -1:
1803             return
1804         endInset = find_end_of_inset(document.body, i)
1805         if endInset == -1:
1806             document.warning("Malformed lyx document: Missing '\\end_inset' in convert_subfig.")
1807             i += 1
1808             continue
1809         k = find_token(document.body, '\tsubcaption', i, endInset)
1810         if k == -1:
1811             i += 1
1812             continue
1813         l = find_token(document.body, '\tsubcaptionText', i, endInset)
1814         caption = document.body[l][16:].strip('"')
1815         savestr = document.body[i]
1816         laststr = document.body[endInset]
1817         del document.body[l]
1818         del document.body[k]
1819         addedLines = -2
1820         # savestr should no longer be needed here.
1821         subst = ['\\begin_inset Float figure', 'wide false', 'sideways false', 
1822                  'status open', '', '\\begin_layout Plain Layout', '\\begin_inset Caption', 
1823                  '', '\\begin_layout Plain Layout',
1824                  caption, '\\end_layout', '', '\\end_inset', '', 
1825                  '\\end_layout', '', '\\begin_layout Plain Layout', savestr]
1826         document.body[i : i+1] = subst
1827         addedLines += len(subst) - 1
1828         endInset += addedLines
1829         # There should be an easier way to do this.
1830         subst = ['', '\\end_inset', '', '\\end_layout', laststr]
1831         document.body[endInset : endInset+1] = subst
1832         addedLines += len(subst) - 1
1833         i += addedLines + 1
1834
1835
1836 def revert_subfig(document):
1837     " Revert subfloats. "
1838     i = 0
1839     while 1:
1840         # whitespace intended (exclude \\begin_inset FloatList)
1841         i = find_token(document.body, '\\begin_inset Float ', i)
1842         if i == -1:
1843             return
1844         j = 0
1845         addedLines = 0
1846         while j != -1:
1847             j = find_end_of_inset(document.body, i)
1848             if j == -1:
1849                 document.warning("Malformed lyx document: Missing '\\end_inset' (float) at line " + str(i + len(document.header)) + ".\n\t" + document.body[i])
1850                 # document.warning(document.body[i-1] + "\n" + document.body[i+1])
1851                 i += 1
1852                 continue # this will get us back to the outer loop, since j == -1
1853             # look for embedded float (= subfloat)
1854             # whitespace intended (exclude \\begin_inset FloatList)
1855             k = find_token(document.body, '\\begin_inset Float ', i + 1, j)
1856             if k == -1:
1857                 break
1858             l = find_end_of_inset(document.body, k)
1859             if l == -1:
1860                 document.warning("Malformed lyx document: Missing '\\end_inset' (embedded float).")
1861                 i += 1
1862                 j == -1
1863                 continue # escape to the outer loop
1864             m = find_default_layout(document, k + 1, l)
1865             # caption?
1866             cap = find_token(document.body, '\\begin_inset Caption', k + 1, l)
1867             caption = ''
1868             shortcap = ''
1869             capend = cap
1870             if cap != -1:
1871                 capend = find_end_of_inset(document.body, cap)
1872                 if capend == -1:
1873                     document.warning("Malformed lyx document: Missing '\\end_inset' (caption).")
1874                     return
1875                 # label?
1876                 label = ''
1877                 lbl = find_token(document.body, '\\begin_inset CommandInset label', cap, capend)
1878                 if lbl != -1:
1879                     lblend = find_end_of_inset(document.body, lbl + 1)
1880                     if lblend == -1:
1881                         document.warning("Malformed lyx document: Missing '\\end_inset' (label).")
1882                         return
1883                     for line in document.body[lbl:lblend + 1]:
1884                         if line.startswith('name '):
1885                             label = line.split()[1].strip('"')
1886                             break
1887                 else:
1888                     lbl = capend
1889                     lblend = capend
1890                     label = ''
1891                 # opt arg?
1892                 opt = find_token(document.body, '\\begin_inset OptArg', cap, capend)
1893                 if opt != -1:
1894                     optend = find_end_of_inset(document.body, opt)
1895                     if optend == -1:
1896                         document.warning("Malformed lyx document: Missing '\\end_inset' (OptArg).")
1897                         return
1898                     optc = find_default_layout(document, opt, optend)
1899                     if optc == -1:
1900                         document.warning("Malformed LyX document: Missing `\\begin_layout' in Float inset.")
1901                         return
1902                     optcend = find_end_of(document.body, optc, "\\begin_layout", "\\end_layout")
1903                     for line in document.body[optc:optcend]:
1904                         if not line.startswith('\\'):
1905                             shortcap += line.strip()
1906                 else:
1907                     opt = capend
1908                     optend = capend
1909                 for line in document.body[cap:capend]:
1910                     if line in document.body[lbl:lblend]:
1911                         continue
1912                     elif line in document.body[opt:optend]:
1913                         continue
1914                     elif not line.startswith('\\'):
1915                         caption += line.strip()
1916                 if len(label) > 0:
1917                     caption += "\\backslash\nlabel{" + label + "}"
1918             subst = '\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus collapsed\n\n' \
1919                       '\\begin_layout Plain Layout\n\n}\n\\end_layout\n\n\\end_inset\n\n' \
1920                       '\\end_layout\n\n\\begin_layout Plain Layout\n'
1921             subst = subst.split('\n')
1922             document.body[l : l+1] = subst
1923             addedLines = len(subst) - 1
1924             # this is before l and so is unchanged by the multiline insertion
1925             if cap != capend:
1926                 del document.body[cap:capend+1]
1927                 addedLines -= (capend + 1 - cap)
1928             del document.body[k+1:m-1]
1929             addedLines -= (m - 1 - (k + 1))
1930             insertion = '\\begin_inset ERT\nstatus collapsed\n\n' \
1931                         '\\begin_layout Plain Layout\n\n\\backslash\n' \
1932                         'subfloat'
1933             if len(shortcap) > 0:
1934                 insertion = insertion + "[" + shortcap + "]"
1935             if len(caption) > 0:
1936                 insertion = insertion + "[" + caption + "]"
1937             insertion = insertion + '{%\n\\end_layout\n\n\\end_inset\n\n\\end_layout\n'
1938             insertion = insertion.split('\n')
1939             document.body[k : k + 1] = insertion
1940             addedLines += len(insertion) - 1
1941             add_to_preamble(document,
1942                             ['\\usepackage{subfig}\n'])
1943         i += addedLines + 1
1944
1945
1946 def revert_wrapplacement(document):
1947     " Revert placement options wrap floats (wrapfig). "
1948     i = 0
1949     while True:
1950         i = find_token(document.body, "lines", i)
1951         if i == -1:
1952             return
1953         j = find_token(document.body, "placement", i+1)
1954         if j != i + 1:
1955             document.warning("Malformed LyX document: Couldn't find placement parameter of wrap float.")
1956             return
1957         document.body[j] = document.body[j].replace("placement O", "placement o")
1958         document.body[j] = document.body[j].replace("placement I", "placement i")
1959         document.body[j] = document.body[j].replace("placement L", "placement l")
1960         document.body[j] = document.body[j].replace("placement R", "placement r")
1961         i = i + 1
1962
1963
1964 def remove_extra_embedded_files(document):
1965     " Remove \extra_embedded_files from buffer params "
1966     i = find_token(document.header, '\\extra_embedded_files', 0)
1967     if i == -1:
1968         return
1969     document.header.pop(i)
1970
1971
1972 def convert_spaceinset(document):
1973     " Convert '\\InsetSpace foo' to '\\begin_inset Space foo\n\\end_inset' "
1974     i = 0
1975     while i < len(document.body):
1976         m = re.match(r'(.*)\\InsetSpace (.*)', document.body[i])
1977         if m:
1978             before = m.group(1)
1979             after = m.group(2)
1980             subst = [before, "\\begin_inset Space " + after, "\\end_inset"]
1981             document.body[i: i+1] = subst
1982             i = i + len(subst)
1983         else:
1984             i = i + 1
1985
1986
1987 def revert_spaceinset(document):
1988     " Revert '\\begin_inset Space foo\n\\end_inset' to '\\InsetSpace foo' "
1989     i = 0
1990     while True:
1991         i = find_token(document.body, "\\begin_inset Space", i)
1992         if i == -1:
1993             return
1994         j = find_end_of_inset(document.body, i)
1995         if j == -1:
1996             document.warning("Malformed LyX document: Could not find end of space inset.")
1997             continue
1998         document.body[i] = document.body[i].replace('\\begin_inset Space', '\\InsetSpace')
1999         del document.body[j]
2000
2001
2002 def convert_hfill(document):
2003     " Convert hfill to space inset "
2004     i = 0
2005     while True:
2006         i = find_token(document.body, "\\hfill", i)
2007         if i == -1:
2008             return
2009         subst = document.body[i].replace('\\hfill', \
2010                   '\n\\begin_inset Space \\hfill{}\n\\end_inset')
2011         subst = subst.split('\n')
2012         document.body[i : i+1] = subst
2013         i += len(subst)
2014
2015
2016 def revert_hfills(document):
2017     ' Revert \\hfill commands '
2018     hfill = re.compile(r'\\hfill')
2019     dotfill = re.compile(r'\\dotfill')
2020     hrulefill = re.compile(r'\\hrulefill')
2021     i = 0
2022     while True:
2023         i = find_token(document.body, "\\InsetSpace", i)
2024         if i == -1:
2025             return
2026         if hfill.search(document.body[i]):
2027             document.body[i] = \
2028               document.body[i].replace('\\InsetSpace \\hfill{}', '\\hfill')
2029             i += 1
2030             continue
2031         if dotfill.search(document.body[i]):
2032             subst = document.body[i].replace('\\InsetSpace \\dotfill{}', \
2033               '\\begin_inset ERT\nstatus collapsed\n\n' \
2034               '\\begin_layout Standard\n\n\n\\backslash\n' \
2035               'dotfill{}\n\\end_layout\n\n\\end_inset\n\n')
2036             subst = subst.split('\n')
2037             document.body[i : i+1] = subst
2038             i += len(subst)
2039             continue
2040         if hrulefill.search(document.body[i]):
2041             subst = document.body[i].replace('\\InsetSpace \\hrulefill{}', \
2042               '\\begin_inset ERT\nstatus collapsed\n\n' \
2043               '\\begin_layout Standard\n\n\n\\backslash\n' \
2044               'hrulefill{}\n\\end_layout\n\n\\end_inset\n\n')
2045             subst = subst.split('\n')
2046             document.body[i : i+1] = subst
2047             i += len(subst)
2048             continue
2049         i += 1
2050
2051 def revert_hspace(document):
2052     ' Revert \\InsetSpace \\hspace{} to ERT '
2053     i = 0
2054     hspace = re.compile(r'\\hspace{}')
2055     hstar  = re.compile(r'\\hspace\*{}')
2056     while True:
2057         i = find_token(document.body, "\\InsetSpace \\hspace", i)
2058         if i == -1:
2059             return
2060         length = get_value(document.body, '\\length', i+1)
2061         if length == '':
2062             document.warning("Malformed lyx document: Missing '\\length' in Space inset.")
2063             return
2064         del document.body[i+1]
2065         addedLines = -1
2066         if hstar.search(document.body[i]):
2067             subst = document.body[i].replace('\\InsetSpace \\hspace*{}', \
2068               '\\begin_inset ERT\nstatus collapsed\n\n' \
2069               '\\begin_layout Standard\n\n\n\\backslash\n' \
2070               'hspace*{' + length + '}\n\\end_layout\n\n\\end_inset\n\n')
2071             subst = subst.split('\n')
2072             document.body[i : i+1] = subst
2073             addedLines += len(subst) - 1
2074             i += addedLines + 1
2075             continue
2076         if hspace.search(document.body[i]):
2077             subst = document.body[i].replace('\\InsetSpace \\hspace{}', \
2078               '\\begin_inset ERT\nstatus collapsed\n\n' \
2079               '\\begin_layout Standard\n\n\n\\backslash\n' \
2080               'hspace{' + length + '}\n\\end_layout\n\n\\end_inset\n\n')
2081             subst = subst.split('\n')
2082             document.body[i : i+1] = subst
2083             addedLines += len(subst) - 1
2084             i += addedLines + 1
2085             continue
2086         i += 1
2087
2088
2089 def revert_protected_hfill(document):
2090     ' Revert \\begin_inset Space \\hspace*{\\fill} to ERT '
2091     i = 0
2092     while True:
2093         i = find_token(document.body, '\\begin_inset Space \\hspace*{\\fill}', i)
2094         if i == -1:
2095             return
2096         j = find_end_of_inset(document.body, i)
2097         if j == -1:
2098             document.warning("Malformed LyX document: Could not find end of space inset.")
2099             continue
2100         del document.body[j]
2101         subst = document.body[i].replace('\\begin_inset Space \\hspace*{\\fill}', \
2102           '\\begin_inset ERT\nstatus collapsed\n\n' \
2103           '\\begin_layout Standard\n\n\n\\backslash\n' \
2104           'hspace*{\n\\backslash\nfill}\n\\end_layout\n\n\\end_inset\n\n')
2105         subst = subst.split('\n')
2106         document.body[i : i+1] = subst
2107         i += len(subst)
2108
2109
2110 def revert_leftarrowfill(document):
2111     ' Revert \\begin_inset Space \\leftarrowfill{} to ERT '
2112     i = 0
2113     while True:
2114         i = find_token(document.body, '\\begin_inset Space \\leftarrowfill{}', i)
2115         if i == -1:
2116             return
2117         j = find_end_of_inset(document.body, i)
2118         if j == -1:
2119             document.warning("Malformed LyX document: Could not find end of space inset.")
2120             continue
2121         del document.body[j]
2122         subst = document.body[i].replace('\\begin_inset Space \\leftarrowfill{}', \
2123           '\\begin_inset ERT\nstatus collapsed\n\n' \
2124           '\\begin_layout Standard\n\n\n\\backslash\n' \
2125           'leftarrowfill{}\n\\end_layout\n\n\\end_inset\n\n')
2126         subst = subst.split('\n')
2127         document.body[i : i+1] = subst
2128         i += len(subst)
2129
2130
2131 def revert_rightarrowfill(document):
2132     ' Revert \\begin_inset Space \\rightarrowfill{} to ERT '
2133     i = 0
2134     while True:
2135         i = find_token(document.body, '\\begin_inset Space \\rightarrowfill{}', i)
2136         if i == -1:
2137             return
2138         j = find_end_of_inset(document.body, i)
2139         if j == -1:
2140             document.warning("Malformed LyX document: Could not find end of space inset.")
2141             continue
2142         del document.body[j]
2143         subst = document.body[i].replace('\\begin_inset Space \\rightarrowfill{}', \
2144           '\\begin_inset ERT\nstatus collapsed\n\n' \
2145           '\\begin_layout Standard\n\n\n\\backslash\n' \
2146           'rightarrowfill{}\n\\end_layout\n\n\\end_inset\n\n')
2147         subst = subst.split('\n')
2148         document.body[i : i+1] = subst
2149         i += len(subst)
2150
2151
2152 def revert_upbracefill(document):
2153     ' Revert \\begin_inset Space \\upbracefill{} to ERT '
2154     i = 0
2155     while True:
2156         i = find_token(document.body, '\\begin_inset Space \\upbracefill{}', i)
2157         if i == -1:
2158             return
2159         j = find_end_of_inset(document.body, i)
2160         if j == -1:
2161             document.warning("Malformed LyX document: Could not find end of space inset.")
2162             continue
2163         del document.body[j]
2164         subst = document.body[i].replace('\\begin_inset Space \\upbracefill{}', \
2165           '\\begin_inset ERT\nstatus collapsed\n\n' \
2166           '\\begin_layout Standard\n\n\n\\backslash\n' \
2167           'upbracefill{}\n\\end_layout\n\n\\end_inset\n\n')
2168         subst = subst.split('\n')
2169         document.body[i : i+1] = subst
2170         i += len(subst)
2171
2172
2173 def revert_downbracefill(document):
2174     ' Revert \\begin_inset Space \\downbracefill{} to ERT '
2175     i = 0
2176     while True:
2177         i = find_token(document.body, '\\begin_inset Space \\downbracefill{}', i)
2178         if i == -1:
2179             return
2180         j = find_end_of_inset(document.body, i)
2181         if j == -1:
2182             document.warning("Malformed LyX document: Could not find end of space inset.")
2183             continue
2184         del document.body[j]
2185         subst = document.body[i].replace('\\begin_inset Space \\downbracefill{}', \
2186           '\\begin_inset ERT\nstatus collapsed\n\n' \
2187           '\\begin_layout Standard\n\n\n\\backslash\n' \
2188           'downbracefill{}\n\\end_layout\n\n\\end_inset\n\n')
2189         subst = subst.split('\n')
2190         document.body[i : i+1] = subst
2191         i += len(subst)
2192
2193
2194 def revert_local_layout(document):
2195     ' Revert local layout headers.'
2196     i = 0
2197     while True:
2198         i = find_token(document.header, "\\begin_local_layout", i)
2199         if i == -1:
2200             return
2201         j = find_end_of(document.header, i, "\\begin_local_layout", "\\end_local_layout")
2202         if j == -1:
2203             # this should not happen
2204             break
2205         document.header[i : j + 1] = []
2206
2207
2208 def convert_pagebreaks(document):
2209     ' Convert inline Newpage insets to new format '
2210     i = 0
2211     while True:
2212         i = find_token(document.body, '\\newpage', i)
2213         if i == -1:
2214             break
2215         document.body[i:i+1] = ['\\begin_inset Newpage newpage',
2216                                 '\\end_inset']
2217     i = 0
2218     while True:
2219         i = find_token(document.body, '\\pagebreak', i)
2220         if i == -1:
2221             break
2222         document.body[i:i+1] = ['\\begin_inset Newpage pagebreak',
2223                                 '\\end_inset']
2224     i = 0
2225     while True:
2226         i = find_token(document.body, '\\clearpage', i)
2227         if i == -1:
2228             break
2229         document.body[i:i+1] = ['\\begin_inset Newpage clearpage',
2230                                 '\\end_inset']
2231     i = 0
2232     while True:
2233         i = find_token(document.body, '\\cleardoublepage', i)
2234         if i == -1:
2235             break
2236         document.body[i:i+1] = ['\\begin_inset Newpage cleardoublepage',
2237                                 '\\end_inset']
2238
2239
2240 def revert_pagebreaks(document):
2241     ' Revert \\begin_inset Newpage to previous inline format '
2242     i = 0
2243     while True:
2244         i = find_token(document.body, '\\begin_inset Newpage', i)
2245         if i == -1:
2246             return
2247         j = find_end_of_inset(document.body, i)
2248         if j == -1:
2249             document.warning("Malformed LyX document: Could not find end of Newpage inset.")
2250             continue
2251         del document.body[j]
2252         document.body[i] = document.body[i].replace('\\begin_inset Newpage newpage', '\\newpage')
2253         document.body[i] = document.body[i].replace('\\begin_inset Newpage pagebreak', '\\pagebreak')
2254         document.body[i] = document.body[i].replace('\\begin_inset Newpage clearpage', '\\clearpage')
2255         document.body[i] = document.body[i].replace('\\begin_inset Newpage cleardoublepage', '\\cleardoublepage')
2256
2257
2258 def convert_linebreaks(document):
2259     ' Convert inline Newline insets to new format '
2260     i = 0
2261     while True:
2262         i = find_token(document.body, '\\newline', i)
2263         if i == -1:
2264             break
2265         document.body[i:i+1] = ['\\begin_inset Newline newline',
2266                                 '\\end_inset']
2267     i = 0
2268     while True:
2269         i = find_token(document.body, '\\linebreak', i)
2270         if i == -1:
2271             break
2272         document.body[i:i+1] = ['\\begin_inset Newline linebreak',
2273                                 '\\end_inset']
2274
2275
2276 def revert_linebreaks(document):
2277     ' Revert \\begin_inset Newline to previous inline format '
2278     i = 0
2279     while True:
2280         i = find_token(document.body, '\\begin_inset Newline', i)
2281         if i == -1:
2282             return
2283         j = find_end_of_inset(document.body, i)
2284         if j == -1:
2285             document.warning("Malformed LyX document: Could not find end of Newline inset.")
2286             continue
2287         del document.body[j]
2288         document.body[i] = document.body[i].replace('\\begin_inset Newline newline', '\\newline')
2289         document.body[i] = document.body[i].replace('\\begin_inset Newline linebreak', '\\linebreak')
2290
2291
2292 def convert_japanese_plain(document):
2293     ' Set language japanese-plain to japanese '
2294     i = 0
2295     if document.language == "japanese-plain":
2296         document.language = "japanese"
2297         i = find_token(document.header, "\\language", 0)
2298         if i != -1:
2299             document.header[i] = "\\language japanese"
2300     j = 0
2301     while True:
2302         j = find_token(document.body, "\\lang japanese-plain", j)
2303         if j == -1:
2304             return
2305         document.body[j] = document.body[j].replace("\\lang japanese-plain", "\\lang japanese")
2306         j = j + 1
2307
2308
2309 def revert_pdfpages(document):
2310     ' Revert pdfpages external inset to ERT '
2311     i = 0
2312     while 1:
2313         i = find_token(document.body, "\\begin_inset External", i)
2314         if i == -1:
2315             return
2316         j = find_end_of_inset(document.body, i)
2317         if j == -1:
2318             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_pdfpages.")
2319             i = i + 1
2320             continue
2321         if get_value(document.body, 'template', i, j) == "PDFPages":
2322             filename = get_value(document.body, 'filename', i, j)
2323             extra = ''
2324             r = re.compile(r'\textra PDFLaTeX \"(.*)\"$')
2325             for k in range(i, j):
2326                 m = r.match(document.body[k])
2327                 if m:
2328                     extra = m.group(1)
2329             angle = get_value(document.body, 'rotateAngle', i, j)
2330             width = get_value(document.body, 'width', i, j)
2331             height = get_value(document.body, 'height', i, j)
2332             scale = get_value(document.body, 'scale', i, j)
2333             keepAspectRatio = find_token(document.body, "\tkeepAspectRatio", i, j)
2334             options = extra
2335             if angle != '':
2336                  if options != '':
2337                      options += ",angle=" + angle
2338                  else:
2339                      options += "angle=" + angle
2340             if width != '':
2341                  if options != '':
2342                      options += ",width=" + convert_len(width)
2343                  else:
2344                      options += "width=" + convert_len(width)
2345             if height != '':
2346                  if options != '':
2347                      options += ",height=" + convert_len(height)
2348                  else:
2349                      options += "height=" + convert_len(height)
2350             if scale != '':
2351                  if options != '':
2352                      options += ",scale=" + scale
2353                  else:
2354                      options += "scale=" + scale
2355             if keepAspectRatio != '':
2356                  if options != '':
2357                      options += ",keepaspectratio"
2358                  else:
2359                      options += "keepaspectratio"
2360             if options != '':
2361                      options = '[' + options + ']'
2362             del document.body[i+1:j+1]
2363             document.body[i:i+1] = ['\\begin_inset ERT',
2364                                 'status collapsed',
2365                                 '',
2366                                 '\\begin_layout Standard',
2367                                 '',
2368                                 '\\backslash',
2369                                 'includepdf' + options + '{' + filename + '}',
2370                                 '\\end_layout',
2371                                 '',
2372                                 '\\end_inset']
2373             add_to_preamble(document, ['\\usepackage{pdfpages}\n'])
2374             i = i + 1
2375             continue
2376         i = i + 1
2377
2378
2379 def revert_mexican(document):
2380     ' Set language Spanish(Mexico) to Spanish '
2381     i = 0
2382     if document.language == "spanish-mexico":
2383         document.language = "spanish"
2384         i = find_token(document.header, "\\language", 0)
2385         if i != -1:
2386             document.header[i] = "\\language spanish"
2387     j = 0
2388     while True:
2389         j = find_token(document.body, "\\lang spanish-mexico", j)
2390         if j == -1:
2391             return
2392         document.body[j] = document.body[j].replace("\\lang spanish-mexico", "\\lang spanish")
2393         j = j + 1
2394
2395
2396 def remove_embedding(document):
2397     ' Remove embed tag from all insets '
2398     revert_inset_embedding(document, 'Graphics')
2399     revert_inset_embedding(document, 'External')
2400     revert_inset_embedding(document, 'CommandInset include')
2401     revert_inset_embedding(document, 'CommandInset bibtex')
2402
2403
2404 def revert_master(document):
2405     ' Remove master param '
2406     i = find_token(document.header, "\\master", 0)
2407     if i != -1:
2408         del document.header[i]
2409
2410
2411 def revert_graphics_group(document):
2412     ' Revert group information from graphics insets '
2413     i = 0
2414     while 1:
2415         i = find_token(document.body, "\\begin_inset Graphics", i)
2416         if i == -1:
2417             return
2418         j = find_end_of_inset(document.body, i)
2419         if j == -1:
2420             document.warning("Malformed lyx document: Missing '\\end_inset' in revert_graphics_group.")
2421             i = i + 1
2422             continue
2423         k = find_token(document.body, " groupId", i, j)
2424         if k == -1:
2425             i = i + 1
2426             continue
2427         del document.body[k]
2428         i = i + 1
2429
2430
2431 def update_apa_styles(document):
2432     ' Replace obsolete styles '
2433
2434     if document.textclass != "apa":
2435         return
2436
2437     obsoletedby = { "Acknowledgments": "Acknowledgements",
2438                     "Section*":        "Section",
2439                     "Subsection*":     "Subsection",
2440                     "Subsubsection*":  "Subsubsection",
2441                     "Paragraph*":      "Paragraph",
2442                     "Subparagraph*":   "Subparagraph"}
2443     i = 0
2444     while 1:
2445         i = find_token(document.body, "\\begin_layout", i)
2446         if i == -1:
2447             return
2448
2449         layout = document.body[i][14:]
2450         if layout in obsoletedby:
2451             document.body[i] = "\\begin_layout " + obsoletedby[layout]
2452
2453         i += 1
2454
2455
2456 def convert_paper_sizes(document):
2457     ' exchange size options legalpaper and executivepaper to correct order '
2458     # routine is needed to fix http://bugzilla.lyx.org/show_bug.cgi?id=4868
2459     i = 0
2460     j = 0
2461     i = find_token(document.header, "\\papersize executivepaper", 0)
2462     if i != -1:
2463         document.header[i] = "\\papersize legalpaper"
2464         return
2465     j = find_token(document.header, "\\papersize legalpaper", 0)
2466     if j != -1:
2467         document.header[j] = "\\papersize executivepaper"
2468
2469
2470 def revert_paper_sizes(document):
2471     ' exchange size options legalpaper and executivepaper to correct order '
2472     i = 0
2473     j = 0
2474     i = find_token(document.header, "\\papersize executivepaper", 0)
2475     if i != -1:
2476         document.header[i] = "\\papersize legalpaper"
2477         return
2478     j = find_token(document.header, "\\papersize legalpaper", 0)
2479     if j != -1:
2480         document.header[j] = "\\papersize executivepaper"
2481
2482
2483 def convert_InsetSpace(document):
2484     " Convert '\\begin_inset Space foo' to '\\begin_inset space foo'"
2485     i = 0
2486     while True:
2487         i = find_token(document.body, "\\begin_inset Space", i)
2488         if i == -1:
2489             return
2490         document.body[i] = document.body[i].replace('\\begin_inset Space', '\\begin_inset space')
2491
2492
2493 def revert_InsetSpace(document):
2494     " Revert '\\begin_inset space foo' to '\\begin_inset Space foo'"
2495     i = 0
2496     while True:
2497         i = find_token(document.body, "\\begin_inset space", i)
2498         if i == -1:
2499             return
2500         document.body[i] = document.body[i].replace('\\begin_inset space', '\\begin_inset Space')
2501
2502
2503 def convert_plain_layout(document):
2504         " Convert 'PlainLayout' to 'Plain Layout'" 
2505         i = 0
2506         while True:
2507                 i = find_token(document.body, '\\begin_layout PlainLayout', i)
2508                 if i == -1:
2509                         return
2510                 document.body[i] = document.body[i].replace('\\begin_layout PlainLayout', \
2511                                                             '\\begin_layout Plain Layout')
2512                 i += 1
2513
2514
2515 def revert_plain_layout(document):
2516         " Convert 'PlainLayout' to 'Plain Layout'" 
2517         i = 0
2518         while True:
2519                 i = find_token(document.body, '\\begin_layout Plain Layout', i)
2520                 if i == -1:
2521                         return
2522                 document.body[i] = document.body[i].replace('\\begin_layout Plain Layout', \
2523                                                             '\\begin_layout PlainLayout')
2524                 i += 1
2525
2526 def revert_plainlayout(document):
2527         " Convert 'PlainLayout' to 'Plain Layout'" 
2528         i = 0
2529         while True:
2530                 i = find_token(document.body, '\\begin_layout PlainLayout', i)
2531                 if i == -1:
2532                         return
2533                 # This will be incorrect for some document classes, since Standard is not always
2534                 # the default. But (a) it is probably the best we can do and (b) it will actually
2535                 # work, in fact, since an unknown layout will be converted to default.
2536                 document.body[i] = document.body[i].replace('\\begin_layout PlainLayout', \
2537                                                             '\\begin_layout Standard')
2538                 i += 1
2539
2540
2541 ##
2542 # Conversion hub
2543 #
2544
2545 supported_versions = ["1.6.0","1.6"]
2546 convert = [[277, [fix_wrong_tables]],
2547            [278, [close_begin_deeper]],
2548            [279, [long_charstyle_names]],
2549            [280, [axe_show_label]],
2550            [281, []],
2551            [282, []],
2552            [283, [convert_flex]],
2553            [284, []],
2554            [285, []],
2555            [286, []],
2556            [287, [convert_wrapfig_options]],
2557            [288, [convert_inset_command]],
2558            [289, [convert_latexcommand_index]],
2559            [290, []],
2560            [291, []],
2561            [292, []],
2562            [293, []],
2563            [294, [convert_pdf_options]],
2564            [295, [convert_htmlurl, convert_url]],
2565            [296, [convert_include]],
2566            [297, [convert_usorbian]],
2567            [298, []],
2568            [299, []],
2569            [300, []],
2570            [301, []],
2571            [302, []],
2572            [303, [convert_serbocroatian]],
2573            [304, [convert_framed_notes]],
2574            [305, []],
2575            [306, []],
2576            [307, []],
2577            [308, []],
2578            [309, []],
2579            [310, []],
2580            [311, [convert_ams_classes]],
2581            [312, []],
2582            [313, [convert_module_names]],
2583            [314, []],
2584            [315, []],
2585            [316, [convert_subfig]],
2586            [317, []],
2587            [318, []],
2588            [319, [convert_spaceinset, convert_hfill]],
2589            [320, []],
2590            [321, [convert_tablines]],
2591            [322, [convert_plain_layout]],
2592            [323, [convert_pagebreaks]],
2593            [324, [convert_linebreaks]],
2594            [325, [convert_japanese_plain]],
2595            [326, []],
2596            [327, []],
2597            [328, [remove_embedding, remove_extra_embedded_files, remove_inzip_options]],
2598            [329, []],
2599            [330, []],
2600            [331, [convert_ltcaption]],
2601            [332, []],
2602            [333, [update_apa_styles]],
2603            [334, [convert_paper_sizes]],
2604            [335, [convert_InsetSpace]],
2605           ]
2606
2607 revert =  [[334, [revert_InsetSpace]],
2608            [333, [revert_paper_sizes]],
2609            [332, []],
2610            [331, [revert_graphics_group]],
2611            [330, [revert_ltcaption]],
2612            [329, [revert_leftarrowfill, revert_rightarrowfill, revert_upbracefill, revert_downbracefill]],
2613            [328, [revert_master]],
2614            [327, []],
2615            [326, [revert_mexican]],
2616            [325, [revert_pdfpages]],
2617            [324, []],
2618            [323, [revert_linebreaks]],
2619            [322, [revert_pagebreaks]],
2620            [321, [revert_local_layout, revert_plain_layout]],
2621            [320, [revert_tablines]],
2622            [319, [revert_protected_hfill]],
2623            [318, [revert_spaceinset, revert_hfills, revert_hspace]],
2624            [317, [remove_extra_embedded_files]],
2625            [316, [revert_wrapplacement]],
2626            [315, [revert_subfig]],
2627            [314, [revert_colsep, revert_plainlayout]],
2628            [313, []],
2629            [312, [revert_module_names]],
2630            [311, [revert_rotfloat, revert_widesideways]],
2631            [310, [revert_external_embedding]],
2632            [309, [revert_btprintall]],
2633            [308, [revert_nocite]],
2634            [307, [revert_serbianlatin]],
2635            [306, [revert_slash, revert_nobreakdash]],
2636            [305, [revert_interlingua]],
2637            [304, [revert_bahasam]],
2638            [303, [revert_framed_notes]],
2639            [302, []],
2640            [301, [revert_latin, revert_samin]],
2641            [300, [revert_linebreak]],
2642            [299, [revert_pagebreak]],
2643            [298, [revert_hyperlinktype]],
2644            [297, [revert_macro_optional_params]],
2645            [296, [revert_albanian, revert_lowersorbian, revert_uppersorbian]],
2646            [295, [revert_include]],
2647            [294, [revert_href]],
2648            [293, [revert_pdf_options_2]],
2649            [292, [revert_inset_info]],
2650            [291, [revert_japanese, revert_japanese_encoding]],
2651            [290, [revert_vietnamese]],
2652            [289, [revert_wraptable]],
2653            [288, [revert_latexcommand_index]],
2654            [287, [revert_inset_command]],
2655            [286, [revert_wrapfig_options]],
2656            [285, [revert_pdf_options]],
2657            [284, [remove_inzip_options]],
2658            [283, []],
2659            [282, [revert_flex]],
2660            [281, []],
2661            [280, [revert_begin_modules]],
2662            [279, [revert_show_label]],
2663            [278, [revert_long_charstyle_names]],
2664            [277, []],
2665            [276, []]
2666           ]
2667
2668
2669 if __name__ == "__main__":
2670     pass