]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_1_4.py
fix frameless box -> parbox conversion in lyx2lyx
[lyx.git] / lib / lyx2lyx / lyx_1_4.py
1 # This file is part of lyx2lyx
2 # -*- coding: iso-8859-1 -*-
3 # Copyright (C) 2002 Dekel Tsur <dekel@lyx.org>
4 # Copyright (C) 2002-2004 José Matos <jamatos@lyx.org>
5 # Copyright (C) 2004-2005 Georg Baum  <Georg.Baum@post.rwth-aachen.de>
6 #
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # as published by the Free Software Foundation; either version 2
10 # of the License, or (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20
21 import re
22 from os import access, F_OK
23 import os.path
24 from parser_tools import find_token, find_end_of_inset, get_next_paragraph, \
25                          get_paragraph, get_value, del_token, is_nonempty_line,\
26                          find_tokens, find_end_of, find_token2
27 from sys import stdin
28 from string import replace, split, find, strip, join
29
30 from lyx_0_12 import update_latexaccents
31
32 ##
33 # Remove \color default
34 #
35 def remove_color_default(file):
36     i = 0
37     while 1:
38         i = find_token(file.body, "\\color default", i)
39         if i == -1:
40             return
41         file.body[i] = replace(file.body[i], "\\color default",
42                            "\\color inherit")
43
44
45 ##
46 # Add \end_header
47 #
48 def add_end_header(file):
49     file.header.append("\\end_header");
50
51
52 def rm_end_header(file):
53     i = find_token(file.header, "\\end_header", 0)
54     if i == -1:
55         return
56     del file.header[i]
57
58
59 ##
60 # \SpecialChar ~ -> \InsetSpace ~
61 #
62 def convert_spaces(file):
63     for i in range(len(file.body)):
64         file.body[i] = replace(file.body[i],"\\SpecialChar ~","\\InsetSpace ~")
65
66
67 def revert_spaces(file):
68     for i in range(len(file.body)):
69         file.body[i] = replace(file.body[i],"\\InsetSpace ~", "\\SpecialChar ~")
70
71
72 ##
73 # BibTeX changes
74 #
75 def convert_bibtex(file):
76     for i in range(len(file.body)):
77         file.body[i] = replace(file.body[i],"\\begin_inset LatexCommand \\BibTeX",
78                                   "\\begin_inset LatexCommand \\bibtex")
79
80
81 def revert_bibtex(file):
82     for i in range(len(file.body)):
83         file.body[i] = replace(file.body[i], "\\begin_inset LatexCommand \\bibtex",
84                                   "\\begin_inset LatexCommand \\BibTeX")
85
86
87 ##
88 # Remove \lyxparent
89 #
90 def remove_insetparent(file):
91     i = 0
92     while 1:
93         i = find_token(file.body, "\\begin_inset LatexCommand \\lyxparent", i)
94         if i == -1:
95             break
96         del file.body[i:i+3]
97
98
99 ##
100 #  Inset External
101 #
102 def convert_external(file):
103     external_rexp = re.compile(r'\\begin_inset External ([^,]*),"([^"]*)",')
104     external_header = "\\begin_inset External"
105     i = 0
106     while 1:
107         i = find_token(file.body, external_header, i)
108         if i == -1:
109             break
110         look = external_rexp.search(file.body[i])
111         args = ['','']
112         if look:
113             args[0] = look.group(1)
114             args[1] = look.group(2)
115         #FIXME: if the previous search fails then warn
116
117         if args[0] == "RasterImage":
118             # Convert a RasterImage External Inset to a Graphics Inset.
119             top = "\\begin_inset Graphics"
120             if args[1]:
121                 filename = "\tfilename " + args[1]
122             file.body[i:i+1] = [top, filename]
123             i = i + 1
124         else:
125             # Convert the old External Inset format to the new.
126             top = external_header
127             template = "\ttemplate " + args[0]
128             if args[1]:
129                 filename = "\tfilename " + args[1]
130                 file.body[i:i+1] = [top, template, filename]
131                 i = i + 2
132             else:
133                 file.body[i:i+1] = [top, template]
134                 i = i + 1
135
136
137 def revert_external_1(file):
138     external_header = "\\begin_inset External"
139     i = 0
140     while 1:
141         i = find_token(file.body, external_header, i)
142         if i == -1:
143             break
144
145         template = split(file.body[i+1])
146         template.reverse()
147         del file.body[i+1]
148
149         filename = split(file.body[i+1])
150         filename.reverse()
151         del file.body[i+1]
152
153         params = split(file.body[i+1])
154         params.reverse()
155         if file.body[i+1]: del file.body[i+1]
156
157         file.body[i] = file.body[i] + " " + template[0]+ ', "' + filename[0] + '", " '+ join(params[1:]) + '"'
158         i = i + 1
159
160
161 def revert_external_2(file):
162     draft_token = '\tdraft'
163     i = 0
164     while 1:
165         i = find_token(file.body, '\\begin_inset External', i)
166         if i == -1:
167             break
168         j = find_end_of_inset(file.body, i + 1)
169         if j == -1:
170             #this should not happen
171             break
172         k = find_token(file.body, draft_token, i+1, j-1)
173         if (k != -1 and len(draft_token) == len(file.body[k])):
174             del file.body[k]
175         i = j + 1
176
177
178 ##
179 # Comment
180 #
181 def convert_comment(file):
182     i = 0
183     comment = "\\layout Comment"
184     while 1:
185         i = find_token(file.body, comment, i)
186         if i == -1:
187             return
188
189         file.body[i:i+1] = ["\\layout Standard","","",
190                         "\\begin_inset Comment",
191                         "collapsed true","",
192                         "\\layout Standard"]
193         i = i + 7
194
195         while 1:
196                 old_i = i
197                 i = find_token(file.body, "\\layout", i)
198                 if i == -1:
199                     i = len(file.body) - 1
200                     file.body[i:i] = ["\\end_inset","",""]
201                     return
202
203                 j = find_token(file.body, '\\begin_deeper', old_i, i)
204                 if j == -1: j = i + 1
205                 k = find_token(file.body, '\\begin_inset', old_i, i)
206                 if k == -1: k = i + 1
207
208                 if j < i and j < k:
209                     i = j
210                     del file.body[i]
211                     i = find_end_of( file.body, i, "\\begin_deeper","\\end_deeper")
212                     if i == -1:
213                         #This case should not happen
214                         #but if this happens deal with it greacefully adding
215                         #the missing \end_deeper.
216                         i = len(file.body) - 1
217                         file.body[i:i] = ["\end_deeper",""]
218                         return
219                     else:
220                         del file.body[i]
221                         continue
222
223                 if k < i:
224                     i = k
225                     i = find_end_of( file.body, i, "\\begin_inset","\\end_inset")
226                     if i == -1:
227                         #This case should not happen
228                         #but if this happens deal with it greacefully adding
229                         #the missing \end_inset.
230                         i = len(file.body) - 1
231                         file.body[i:i] = ["\\end_inset","","","\\end_inset","",""]
232                         return
233                     else:
234                         i = i + 1
235                         continue
236
237                 if find(file.body[i], comment) == -1:
238                     file.body[i:i] = ["\\end_inset"]
239                     i = i + 1
240                     break
241                 file.body[i:i+1] = ["\\layout Standard"]
242                 i = i + 1
243
244
245 def revert_comment(file):
246     i = 0
247     while 1:
248         i = find_tokens(file.body, ["\\begin_inset Comment", "\\begin_inset Greyedout"], i)
249
250         if i == -1:
251             return
252         file.body[i] = "\\begin_inset Note"
253         i = i + 1
254
255
256 ##
257 # Add \end_layout
258 #
259 def add_end_layout(file):
260     i = find_token(file.body, '\\layout', 0)
261
262     if i == -1:
263         return
264
265     i = i + 1
266     struct_stack = ["\\layout"]
267
268     while 1:
269         i = find_tokens(file.body, ["\\begin_inset", "\\end_inset", "\\layout",
270                                 "\\begin_deeper", "\\end_deeper", "\\the_end"], i)
271
272         token = split(file.body[i])[0]
273
274         if token == "\\begin_inset":
275             struct_stack.append(token)
276             i = i + 1
277             continue
278
279         if token == "\\end_inset":
280             tail = struct_stack.pop()
281             if tail == "\\layout":
282                 file.body.insert(i,"")
283                 file.body.insert(i,"\\end_layout")
284                 i = i + 2
285                 #Check if it is the correct tag
286                 struct_stack.pop()
287             i = i + 1
288             continue
289
290         if token == "\\layout":
291             tail = struct_stack.pop()
292             if tail == token:
293                 file.body.insert(i,"")
294                 file.body.insert(i,"\\end_layout")
295                 i = i + 3
296             else:
297                 struct_stack.append(tail)
298                 i = i + 1
299             struct_stack.append(token)
300             continue
301
302         if token == "\\begin_deeper":
303             file.body.insert(i,"")
304             file.body.insert(i,"\\end_layout")
305             i = i + 3
306             struct_stack.append(token)
307             continue
308
309         if token == "\\end_deeper":
310             if struct_stack[-1] == '\\layout':
311                 file.body.insert(i, '\\end_layout')
312                 i = i + 1
313                 struct_stack.pop()
314             i = i + 1
315             continue
316
317         #case \end_document
318         file.body.insert(i, "")
319         file.body.insert(i, "\\end_layout")
320         return
321
322
323 def rm_end_layout(file):
324     i = 0
325     while 1:
326         i = find_token(file.body, '\\end_layout', i)
327
328         if i == -1:
329             return
330
331         del file.body[i]
332
333
334 ##
335 # Handle change tracking keywords
336 #
337 def insert_tracking_changes(file):
338     i = find_token(file.header, "\\tracking_changes", 0)
339     if i == -1:
340         file.header.append("\\tracking_changes 0")
341
342
343 def rm_tracking_changes(file):
344     i = find_token(file.header, "\\author", 0)
345     if i != -1:
346         del file.header[i]
347
348     i = find_token(file.header, "\\tracking_changes", 0)
349     if i == -1:
350         return
351     del file.header[i]
352
353
354 def rm_body_changes(file):
355     i = 0
356     while 1:
357         i = find_token(file.body, "\\change_", i)
358         if i == -1:
359             return
360
361         del file.body[i]
362
363
364 ##
365 # \layout -> \begin_layout
366 #
367 def layout2begin_layout(file):
368     i = 0
369     while 1:
370         i = find_token(file.body, '\\layout', i)
371         if i == -1:
372             return
373
374         file.body[i] = replace(file.body[i], '\\layout', '\\begin_layout')
375         i = i + 1
376
377
378 def begin_layout2layout(file):
379     i = 0
380     while 1:
381         i = find_token(file.body, '\\begin_layout', i)
382         if i == -1:
383             return
384
385         file.body[i] = replace(file.body[i], '\\begin_layout', '\\layout')
386         i = i + 1
387
388
389 ##
390 # valignment="center" -> valignment="middle"
391 #
392 def convert_valignment_middle(body, start, end):
393     for i in range(start, end):
394         if re.search('^<(column|cell) .*valignment="center".*>$', body[i]):
395             body[i] = replace(body[i], 'valignment="center"', 'valignment="middle"')
396
397
398 def convert_table_valignment_middle(file):
399     i = 0
400     while 1:
401         i = find_token(file.body, '\\begin_inset  Tabular', i)
402         if i == -1:
403             return
404         j = find_end_of_inset(file.body, i + 1)
405         if j == -1:
406             #this should not happen
407             convert_valignment_middle(file.body, i + 1, len(file.body))
408             return
409         convert_valignment_middle(file.body, i + 1, j)
410         i = j + 1
411
412
413 def revert_table_valignment_middle(body, start, end):
414     for i in range(start, end):
415         if re.search('^<(column|cell) .*valignment="middle".*>$', body[i]):
416             body[i] = replace(body[i], 'valignment="middle"', 'valignment="center"')
417
418
419 def revert_valignment_middle(file):
420     i = 0
421     while 1:
422         i = find_token(file.body, '\\begin_inset  Tabular', i)
423         if i == -1:
424             return
425         j = find_end_of_inset(file.body, i + 1)
426         if j == -1:
427             #this should not happen
428             revert_table_valignment_middle(file.body, i + 1, len(file.body))
429             return
430         revert_table_valignment_middle(file.body, i + 1, j)
431         i = j + 1
432
433
434 ##
435 #  \the_end -> \end_document
436 #
437 def convert_end_document(file):
438     i = find_token(file.body, "\\the_end", 0)
439     if i == -1:
440         file.body.append("\\end_document")
441         return
442     file.body[i] = "\\end_document"
443
444
445 def revert_end_document(file):
446     i = find_token(file.body, "\\end_document", 0)
447     if i == -1:
448         file.body.append("\\the_end")
449         return
450     file.body[i] = "\\the_end"
451
452
453 ##
454 # Convert line and page breaks
455 # Old:
456 #\layout Standard
457 #\line_top \line_bottom \pagebreak_top \pagebreak_bottom \added_space_top xxx \added_space_bottom yyy
458 #0
459 #
460 # New:
461 #\begin layout Standard
462 #
463 #\newpage
464 #
465 #\lyxline
466 #\begin_inset VSpace xxx
467 #\end_inset
468 #
469 #\end_layout
470 #\begin_layout Standard
471 #
472 #0
473 #\end_layout
474 #\begin_layout Standard
475 #
476 #\begin_inset VSpace xxx
477 #\end_inset
478 #\lyxline
479 #
480 #\newpage
481 #
482 #\end_layout
483 def convert_breaks(file):
484     i = 0
485     while 1:
486         i = find_token(file.body, "\\begin_layout", i)
487         if i == -1:
488             return
489         i = i + 1
490         line_top   = find(file.body[i],"\\line_top")
491         line_bot   = find(file.body[i],"\\line_bottom")
492         pb_top     = find(file.body[i],"\\pagebreak_top")
493         pb_bot     = find(file.body[i],"\\pagebreak_bottom")
494         vspace_top = find(file.body[i],"\\added_space_top")
495         vspace_bot = find(file.body[i],"\\added_space_bottom")
496
497         if line_top == -1 and line_bot == -1 and pb_bot == -1 and pb_top == -1 and vspace_top == -1 and vspace_bot == -1:
498             continue
499
500         for tag in "\\line_top", "\\line_bottom", "\\pagebreak_top", "\\pagebreak_bottom":
501             file.body[i] = replace(file.body[i], tag, "")
502
503         if vspace_top != -1:
504             # the position could be change because of the removal of other
505             # paragraph properties above
506             vspace_top = find(file.body[i],"\\added_space_top")
507             tmp_list = split(file.body[i][vspace_top:])
508             vspace_top_value = tmp_list[1]
509             file.body[i] = file.body[i][:vspace_top] + join(tmp_list[2:])
510
511         if vspace_bot != -1:
512             # the position could be change because of the removal of other
513             # paragraph properties above
514             vspace_bot = find(file.body[i],"\\added_space_bottom")
515             tmp_list = split(file.body[i][vspace_bot:])
516             vspace_bot_value = tmp_list[1]
517             file.body[i] = file.body[i][:vspace_bot] + join(tmp_list[2:])
518
519         file.body[i] = strip(file.body[i])
520         i = i + 1
521
522         #  Create an empty paragraph for line and page break that belong
523         # above the paragraph
524         if pb_top !=-1 or line_top != -1 or vspace_bot != -1:
525
526             paragraph_above = ['','\\begin_layout Standard','','']
527
528             if pb_top != -1:
529                 paragraph_above.extend(['\\newpage ',''])
530
531             if vspace_top != -1:
532                 paragraph_above.extend(['\\begin_inset VSpace ' + vspace_top_value,'\\end_inset','',''])
533
534             if line_top != -1:
535                 paragraph_above.extend(['\\lyxline ',''])
536
537             paragraph_above.extend(['\\end_layout',''])
538
539             #inset new paragraph above the current paragraph
540             file.body[i-2:i-2] = paragraph_above
541             i = i + len(paragraph_above)
542
543         # Ensure that nested style are converted later.
544         k = find_end_of(file.body, i, "\\begin_layout", "\\end_layout")
545
546         if k == -1:
547             return
548
549         if pb_top !=-1 or line_top != -1 or vspace_bot != -1:
550
551             paragraph_bellow = ['','\\begin_layout Standard','','']
552
553             if line_bot != -1:
554                 paragraph_bellow.extend(['\\lyxline ',''])
555
556             if vspace_bot != -1:
557                 paragraph_bellow.extend(['\\begin_inset VSpace ' + vspace_bot_value,'\\end_inset','',''])
558
559             if pb_bot != -1:
560                 paragraph_bellow.extend(['\\newpage ',''])
561
562             paragraph_bellow.extend(['\\end_layout',''])
563
564             #inset new paragraph above the current paragraph
565             file.body[k + 1: k + 1] = paragraph_bellow
566
567
568 ##
569 #  Notes
570 #
571 def convert_note(file):
572     i = 0
573     while 1:
574         i = find_tokens(file.body, ["\\begin_inset Note",
575                                 "\\begin_inset Comment",
576                                 "\\begin_inset Greyedout"], i)
577         if i == -1:
578             break
579
580         file.body[i] = file.body[i][0:13] + 'Note ' + file.body[i][13:]
581         i = i + 1
582
583
584 def revert_note(file):
585     note_header = "\\begin_inset Note "
586     i = 0
587     while 1:
588         i = find_token(file.body, note_header, i)
589         if i == -1:
590             break
591
592         file.body[i] = "\\begin_inset " + file.body[i][len(note_header):]
593         i = i + 1
594
595
596 ##
597 # Box
598 #
599 def convert_box(file):
600     i = 0
601     while 1:
602         i = find_tokens(file.body, ["\\begin_inset Boxed",
603                                 "\\begin_inset Doublebox",
604                                 "\\begin_inset Frameless",
605                                 "\\begin_inset ovalbox",
606                                 "\\begin_inset Ovalbox",
607                                 "\\begin_inset Shadowbox"], i)
608         if i == -1:
609             break
610
611         file.body[i] = file.body[i][0:13] + 'Box ' + file.body[i][13:]
612         i = i + 1
613
614
615 def revert_box(file):
616     box_header = "\\begin_inset Box "
617     i = 0
618     while 1:
619         i = find_token(file.body, box_header, i)
620         if i == -1:
621             break
622
623         file.body[i] = "\\begin_inset " + file.body[i][len(box_header):]
624         i = i + 1
625
626
627 ##
628 # Collapse
629 #
630 def convert_collapsable(file):
631     i = 0
632     while 1:
633         i = find_tokens(file.body, ["\\begin_inset Box",
634                                 "\\begin_inset Branch",
635                                 "\\begin_inset CharStyle",
636                                 "\\begin_inset Float",
637                                 "\\begin_inset Foot",
638                                 "\\begin_inset Marginal",
639                                 "\\begin_inset Note",
640                                 "\\begin_inset OptArg",
641                                 "\\begin_inset Wrap"], i)
642         if i == -1:
643             break
644
645         # Seach for a line starting 'collapsed'
646         # If, however, we find a line starting '\begin_layout'
647         # (_always_ present) then break with a warning message
648         i = i + 1
649         while 1:
650             if (file.body[i] == "collapsed false"):
651                 file.body[i] = "status open"
652                 break
653             elif (file.body[i] == "collapsed true"):
654                 file.body[i] = "status collapsed"
655                 break
656             elif (file.body[i][:13] == "\\begin_layout"):
657                 file.warning("Malformed LyX file: Missing 'collapsed'.")
658                 break
659             i = i + 1
660
661         i = i + 1
662
663
664 def revert_collapsable(file):
665     i = 0
666     while 1:
667         i = find_tokens(file.body, ["\\begin_inset Box",
668                                 "\\begin_inset Branch",
669                                 "\\begin_inset CharStyle",
670                                 "\\begin_inset Float",
671                                 "\\begin_inset Foot",
672                                 "\\begin_inset Marginal",
673                                 "\\begin_inset Note",
674                                 "\\begin_inset OptArg",
675                                 "\\begin_inset Wrap"], i)
676         if i == -1:
677             break
678
679         # Seach for a line starting 'status'
680         # If, however, we find a line starting '\begin_layout'
681         # (_always_ present) then break with a warning message
682         i = i + 1
683         while 1:
684             if (file.body[i] == "status open"):
685                 file.body[i] = "collapsed false"
686                 break
687             elif (file.body[i] == "status collapsed" or
688                   file.body[i] == "status inlined"):
689                 file.body[i] = "collapsed true"
690                 break
691             elif (file.body[i][:13] == "\\begin_layout"):
692                 file.warning("Malformed LyX file: Missing 'status'.")
693                 break
694             i = i + 1
695
696         i = i + 1
697
698
699 ##
700 #  ERT
701 #
702 def convert_ert(file):
703     i = 0
704     while 1:
705         i = find_token(file.body, "\\begin_inset ERT", i)
706         if i == -1:
707             break
708
709         # Seach for a line starting 'status'
710         # If, however, we find a line starting '\begin_layout'
711         # (_always_ present) then break with a warning message
712         i = i + 1
713         while 1:
714             if (file.body[i] == "status Open"):
715                 file.body[i] = "status open"
716                 break
717             elif (file.body[i] == "status Collapsed"):
718                 file.body[i] = "status collapsed"
719                 break
720             elif (file.body[i] == "status Inlined"):
721                 file.body[i] = "status inlined"
722                 break
723             elif (file.body[i][:13] == "\\begin_layout"):
724                 file.warning("Malformed LyX file: Missing 'status'.")
725                 break
726             i = i + 1
727
728         i = i + 1
729
730
731 def revert_ert(file):
732     i = 0
733     while 1:
734         i = find_token(file.body, "\\begin_inset ERT", i)
735         if i == -1:
736             break
737
738         # Seach for a line starting 'status'
739         # If, however, we find a line starting '\begin_layout'
740         # (_always_ present) then break with a warning message
741         i = i + 1
742         while 1:
743             if (file.body[i] == "status open"):
744                 file.body[i] = "status Open"
745                 break
746             elif (file.body[i] == "status collapsed"):
747                 file.body[i] = "status Collapsed"
748                 break
749             elif (file.body[i] == "status inlined"):
750                 file.body[i] = "status Inlined"
751                 break
752             elif (file.body[i][:13] == "\\begin_layout"):
753                 file.warning("Malformed LyX file : Missing 'status'.")
754                 break
755             i = i + 1
756
757         i = i + 1
758
759
760 ##
761 # Minipages
762 #
763 def convert_minipage(file):
764     """ Convert minipages to the box inset.
765     We try to use the same order of arguments as lyx does.
766     """
767     pos = ["t","c","b"]
768     inner_pos = ["c","t","b","s"]
769
770     i = 0
771     while 1:
772         i = find_token(file.body, "\\begin_inset Minipage", i)
773         if i == -1:
774             return
775
776         file.body[i] = "\\begin_inset Box Frameless"
777         i = i + 1
778
779         # convert old to new position using the pos list
780         if file.body[i][:8] == "position":
781             file.body[i] = 'position "%s"' % pos[int(file.body[i][9])]
782         else:
783             file.body.insert(i, 'position "%s"' % pos[0])
784         i = i + 1
785
786         file.body.insert(i, 'hor_pos "c"')
787         i = i + 1
788         file.body.insert(i, 'has_inner_box 1')
789         i = i + 1
790
791         # convert the inner_position
792         if file.body[i][:14] == "inner_position":
793             file.body[i] = 'inner_pos "%s"' %  inner_pos[int(file.body[i][15])]
794         else:
795             file.body.insert('inner_pos "%s"' % inner_pos[0])
796         i = i + 1
797
798         # We need this since the new file format has a height and width
799         # in a different order.
800         if file.body[i][:6] == "height":
801             height = file.body[i][6:]
802             # test for default value of 221 and convert it accordingly
803             if height == ' "0pt"':
804                 height = ' "1pt"'
805             del file.body[i]
806         else:
807             height = ' "1pt"'
808
809         if file.body[i][:5] == "width":
810             width = file.body[i][5:]
811             del file.body[i]
812         else:
813             width = ' "0"'
814
815         if file.body[i][:9] == "collapsed":
816             if file.body[i][9:] == "true":
817                 status = "collapsed"
818             else:
819                 status = "open"
820             del file.body[i]
821         else:
822             status = "collapsed"
823
824         file.body.insert(i, 'use_parbox 0')
825         i = i + 1
826         file.body.insert(i, 'width' + width)
827         i = i + 1
828         file.body.insert(i, 'special "none"')
829         i = i + 1
830         file.body.insert(i, 'height' + height)
831         i = i + 1
832         file.body.insert(i, 'height_special "totalheight"')
833         i = i + 1
834         file.body.insert(i, 'status ' + status)
835         i = i + 1
836
837
838 # -------------------------------------------------------------------------------------------
839 # Convert backslashes and '\n' into valid ERT code, append the converted
840 # text to body[i] and return the (maybe incremented) line index i
841 def convert_ertbackslash(body, i, ert):
842     for c in ert:
843         if c == '\\':
844             body[i] = body[i] + '\\backslash '
845             i = i + 1
846             body.insert(i, '')
847         elif c == '\n':
848             body[i+1:i+1] = ['\\newline ', '']
849             i = i + 2
850         else:
851             body[i] = body[i] + c
852     return i
853
854
855 def convert_vspace(file):
856
857     # Get default spaceamount
858     i = find_token(file.header, '\\defskip', 0)
859     if i == -1:
860         defskipamount = 'medskip'
861     else:
862         defskipamount = split(file.header[i])[1]
863
864     # Convert the insets
865     i = 0
866     while 1:
867         i = find_token(file.body, '\\begin_inset VSpace', i)
868         if i == -1:
869             return
870         spaceamount = split(file.body[i])[2]
871
872         # Are we at the beginning or end of a paragraph?
873         paragraph_start = 1
874         start = get_paragraph(file.body, i) + 1
875         for k in range(start, i):
876             if is_nonempty_line(file.body[k]):
877                 paragraph_start = 0
878                 break
879         paragraph_end = 1
880         j = find_end_of_inset(file.body, i)
881         if j == -1:
882             file.warning("Malformed LyX file: Missing '\\end_inset'.")
883             i = i + 1
884             continue
885         end = get_next_paragraph(file.body, i)
886         for k in range(j + 1, end):
887             if is_nonempty_line(file.body[k]):
888                 paragraph_end = 0
889                 break
890
891         # Convert to paragraph formatting if we are at the beginning or end
892         # of a paragraph and the resulting paragraph would not be empty
893         if ((paragraph_start and not paragraph_end) or
894             (paragraph_end   and not paragraph_start)):
895             # The order is important: del and insert invalidate some indices
896             del file.body[j]
897             del file.body[i]
898             if paragraph_start:
899                 file.body.insert(start, '\\added_space_top ' + spaceamount + ' ')
900             else:
901                 file.body.insert(start, '\\added_space_bottom ' + spaceamount + ' ')
902             continue
903
904         # Convert to ERT
905         file.body[i:i+1] = ['\\begin_inset ERT', 'status Collapsed', '',
906                         '\\layout Standard', '', '\\backslash ']
907         i = i + 6
908         if spaceamount[-1] == '*':
909             spaceamount = spaceamount[:-1]
910             keep = 1
911         else:
912             keep = 0
913
914         # Replace defskip by the actual value
915         if spaceamount == 'defskip':
916             spaceamount = defskipamount
917
918         # LaTeX does not know \\smallskip* etc
919         if keep:
920             if spaceamount == 'smallskip':
921                 spaceamount = '\\smallskipamount'
922             elif spaceamount == 'medskip':
923                 spaceamount = '\\medskipamount'
924             elif spaceamount == 'bigskip':
925                 spaceamount = '\\bigskipamount'
926             elif spaceamount == 'vfill':
927                 spaceamount = '\\fill'
928
929         # Finally output the LaTeX code
930         if (spaceamount == 'smallskip' or spaceamount == 'medskip' or
931             spaceamount == 'bigskip'   or spaceamount == 'vfill'):
932             file.body.insert(i, spaceamount)
933         else :
934             if keep:
935                 file.body.insert(i, 'vspace*{')
936             else:
937                 file.body.insert(i, 'vspace{')
938             i = convert_ertbackslash(file.body, i, spaceamount)
939             file.body[i] =  file.body[i] + '}'
940         i = i + 1
941
942
943 # Convert a LyX length into a LaTeX length
944 def convert_len(len, special):
945     units = {"text%":"\\textwidth", "col%":"\\columnwidth",
946              "page%":"\\pagewidth", "line%":"\\linewidth",
947              "theight%":"\\textheight", "pheight%":"\\pageheight"}
948
949     # Convert special lengths
950     if special != 'none':
951         len = '%f\\' % len2value(len) + special
952
953     # Convert LyX units to LaTeX units
954     for unit in units.keys():
955         if find(len, unit) != -1:
956             len = '%f' % (len2value(len) / 100) + units[unit]
957             break
958
959     return len
960
961
962 # Convert a LyX length into valid ERT code and append it to body[i]
963 # Return the (maybe incremented) line index i
964 def convert_ertlen(body, i, len, special):
965     # Convert backslashes and insert the converted length into body
966     return convert_ertbackslash(body, i, convert_len(len, special))
967
968
969 # Return the value of len without the unit in numerical form
970 def len2value(len):
971     result = re.search('([+-]?[0-9.]+)', len)
972     if result:
973         return float(result.group(1))
974     # No number means 1.0
975     return 1.0
976
977
978 # Convert text to ERT and insert it at body[i]
979 # Return the index of the line after the inserted ERT
980 def insert_ert(body, i, status, text):
981     body[i:i] = ['\\begin_inset ERT', 'status ' + status, '',
982                  '\\layout Standard', '']
983     i = i + 5
984     i = convert_ertbackslash(body, i, text) + 1
985     body[i:i] = ['', '\\end_inset', '']
986     i = i + 3
987     return i
988
989
990 # Add text to the preamble if it is not already there.
991 # Only the first line is checked!
992 def add_to_preamble(file, text):
993     i = find_token(file.header, '\\begin_preamble', 0)
994     if i == -1:
995         file.warning("Malformed LyX file: Missing '\\begin_preamble'.")
996         return
997     j = find_token(file.header, '\\end_preamble', i)
998     if j == -1:
999         file.warning("Malformed LyX file: Missing '\\end_preamble'.")
1000         return
1001     if find_token(file.header, text[0], i, j) != -1:
1002         return
1003     file.header[j:j] = text
1004
1005
1006 def convert_frameless_box(file):
1007     pos = ['t', 'c', 'b']
1008     inner_pos = ['c', 't', 'b', 's']
1009     i = 0
1010     while 1:
1011         i = find_token(file.body, '\\begin_inset Frameless', i)
1012         if i == -1:
1013             return
1014         j = find_end_of_inset(file.body, i)
1015         if j == -1:
1016             file.warning("Malformed LyX file: Missing '\\end_inset'.")
1017             i = i + 1
1018             continue
1019         del file.body[i]
1020         j = j - 1
1021
1022         # Gather parameters
1023         params = {'position':'0', 'hor_pos':'c', 'has_inner_box':'1',
1024                   'inner_pos':'1', 'use_parbox':'0', 'width':'100col%',
1025                   'special':'none', 'height':'1in',
1026                   'height_special':'totalheight', 'collapsed':'false'}
1027         for key in params.keys():
1028             value = replace(get_value(file.body, key, i, j), '"', '')
1029             if value != "":
1030                 if key == 'position':
1031                     # convert new to old position: 'position "t"' -> 0
1032                     value = find_token(pos, value, 0)
1033                     if value != -1:
1034                         params[key] = value
1035                 elif key == 'inner_pos':
1036                     # convert inner position
1037                     value = find_token(inner_pos, value, 0)
1038                     if value != -1:
1039                         params[key] = value
1040                 else:
1041                     params[key] = value
1042                 j = del_token(file.body, key, i, j)
1043         i = i + 1
1044
1045         # Convert to minipage or ERT?
1046         # Note that the inner_position and height parameters of a minipage
1047         # inset are ignored and not accessible for the user, although they
1048         # are present in the file format and correctly read in and written.
1049         # Therefore we convert to ERT if they do not have their LaTeX
1050         # defaults. These are:
1051         # - the value of "position" for "inner_pos"
1052         # - "\totalheight"          for "height"
1053         if (params['use_parbox'] != '0' or
1054             params['has_inner_box'] != '1' or
1055             params['special'] != 'none' or
1056             inner_pos[params['inner_pos']] != pos[params['position']] or
1057             params['height_special'] != 'totalheight' or
1058             len2value(params['height']) != 1.0):
1059
1060             # Here we know that this box is not supported in file format 224.
1061             # Therefore we need to convert it to ERT. We can't simply convert
1062             # the beginning and end of the box to ERT, because the
1063             # box inset may contain layouts that are different from the
1064             # surrounding layout. After the conversion the contents of the
1065             # box inset is on the same level as the surrounding text, and
1066             # paragraph layouts and align parameters can get mixed up.
1067
1068             # A possible solution for this problem:
1069             # Convert the box to a minipage and redefine the minipage
1070             # environment in ERT so that the original box is simulated.
1071             # For minipages we could do this in a way that the width and
1072             # position can still be set from LyX, but this did not work well.
1073             # This is not possible for parboxes either, so we convert the
1074             # original box to ERT, put the minipage inset inside the box
1075             # and redefine the minipage environment to be empty.
1076
1077             # Commands that are independant of a particular box can go to
1078             # the preamble.
1079             # We need to define lyxtolyxrealminipage with 3 optional
1080             # arguments although LyX 1.3 uses only the first one.
1081             # Otherwise we will get LaTeX errors if this document is
1082             # converted to format 225 or above again (LyX 1.4 uses all
1083             # optional arguments).
1084             add_to_preamble(file,
1085                 ['% Commands inserted by lyx2lyx for frameless boxes',
1086                  '% Save the original minipage environment',
1087                  '\\let\\lyxtolyxrealminipage\\minipage',
1088                  '\\let\\endlyxtolyxrealminipage\\endminipage',
1089                  '% Define an empty lyxtolyximinipage environment',
1090                  '% with 3 optional arguments',
1091                  '\\newenvironment{lyxtolyxiiiminipage}[4]{}{}',
1092                  '\\newenvironment{lyxtolyxiiminipage}[2][\\lyxtolyxargi]%',
1093                  '  {\\begin{lyxtolyxiiiminipage}{\\lyxtolyxargi}{\\lyxtolyxargii}{#1}{#2}}%',
1094                  '  {\\end{lyxtolyxiiiminipage}}',
1095                  '\\newenvironment{lyxtolyximinipage}[1][\\totalheight]%',
1096                  '  {\\def\\lyxtolyxargii{{#1}}\\begin{lyxtolyxiiminipage}}%',
1097                  '  {\\end{lyxtolyxiiminipage}}',
1098                  '\\newenvironment{lyxtolyxminipage}[1][c]%',
1099                  '  {\\def\\lyxtolyxargi{{#1}}\\begin{lyxtolyximinipage}}',
1100                  '  {\\end{lyxtolyximinipage}}'])
1101
1102             if params['use_parbox'] != '0':
1103                 ert = '\\parbox'
1104             else:
1105                 ert = '\\begin{lyxtolyxrealminipage}'
1106
1107             # convert optional arguments only if not latex default
1108             if (pos[params['position']] != 'c' or
1109                 inner_pos[params['inner_pos']] != pos[params['position']] or
1110                 params['height_special'] != 'totalheight' or
1111                 len2value(params['height']) != 1.0):
1112                 ert = ert + '[' + pos[params['position']] + ']'
1113             if (inner_pos[params['inner_pos']] != pos[params['position']] or
1114                 params['height_special'] != 'totalheight' or
1115                 len2value(params['height']) != 1.0):
1116                 ert = ert + '[' + convert_len(params['height'],
1117                                               params['height_special']) + ']'
1118             if inner_pos[params['inner_pos']] != pos[params['position']]:
1119                 ert = ert + '[' + inner_pos[params['inner_pos']] + ']'
1120
1121             ert = ert + '{' + convert_len(params['width'],
1122                                           params['special']) + '}'
1123
1124             if params['use_parbox'] != '0':
1125                 ert = ert + '{'
1126             ert = ert + '\\let\\minipage\\lyxtolyxminipage%\n'
1127             ert = ert + '\\let\\endminipage\\endlyxtolyxminipage%\n'
1128
1129             old_i = i
1130             i = insert_ert(file.body, i, 'Collapsed', ert)
1131             j = j + i - old_i - 1
1132
1133             file.body[i:i] = ['\\begin_inset Minipage',
1134                               'position %d' % params['position'],
1135                               'inner_position 1',
1136                               'height "1in"',
1137                               'width "' + params['width'] + '"',
1138                               'collapsed ' + params['collapsed']]
1139             i = i + 6
1140             j = j + 6
1141
1142             # Restore the original minipage environment since we may have
1143             # minipages inside this box.
1144             # Start a new paragraph because the following may be nonstandard
1145             file.body[i:i] = ['\\layout Standard', '', '']
1146             i = i + 2
1147             j = j + 3
1148             ert = '\\let\\minipage\\lyxtolyxrealminipage%\n'
1149             ert = ert + '\\let\\endminipage\\lyxtolyxrealendminipage%'
1150             old_i = i
1151             i = insert_ert(file.body, i, 'Collapsed', ert)
1152             j = j + i - old_i - 1
1153
1154             # Redefine the minipage end before the inset end.
1155             # Start a new paragraph because the previous may be nonstandard
1156             file.body[j:j] = ['\\layout Standard', '', '']
1157             j = j + 2
1158             ert = '\\let\\endminipage\\endlyxtolyxminipage'
1159             j = insert_ert(file.body, j, 'Collapsed', ert)
1160             j = j + 1
1161             file.body.insert(j, '')
1162             j = j + 1
1163
1164             # LyX writes '%\n' after each box. Therefore we need to end our
1165             # ERT with '%\n', too, since this may swallow a following space.
1166             if params['use_parbox'] != '0':
1167                 ert = '}%\n'
1168             else:
1169                 ert = '\\end{lyxtolyxrealminipage}%\n'
1170             j = insert_ert(file.body, j, 'Collapsed', ert)
1171
1172             # We don't need to restore the original minipage after the inset
1173             # end because the scope of the redefinition is the original box.
1174
1175         else:
1176
1177             # Convert to minipage
1178             file.body[i:i] = ['\\begin_inset Minipage',
1179                               'position %d' % params['position'],
1180                               'inner_position %d' % params['inner_pos'],
1181                               'height "' + params['height'] + '"',
1182                               'width "' + params['width'] + '"',
1183                               'collapsed ' + params['collapsed']]
1184             i = i + 6
1185
1186 ##
1187 # Convert jurabib
1188 #
1189
1190 def convert_jurabib(file):
1191     i = find_token(file.header, '\\use_numerical_citations', 0)
1192     if i == -1:
1193         file.warning("Malformed lyx file: Missing '\\use_numerical_citations'.")
1194         return
1195     file.header.insert(i + 1, '\\use_jurabib 0')
1196
1197
1198 def revert_jurabib(file):
1199     i = find_token(file.header, '\\use_jurabib', 0)
1200     if i == -1:
1201         file.warning("Malformed lyx file: Missing '\\use_jurabib'.")
1202         return
1203     if get_value(file.header, '\\use_jurabib', 0) != "0":
1204         file.warning("Conversion of '\\use_jurabib = 1' not yet implemented.")
1205         # Don't remove '\\use_jurabib' so that people will get warnings by lyx
1206         return
1207     del file.header[i]
1208
1209 ##
1210 # Convert bibtopic
1211 #
1212
1213 def convert_bibtopic(file):
1214     i = find_token(file.header, '\\use_jurabib', 0)
1215     if i == -1:
1216         file.warning("Malformed lyx file: Missing '\\use_jurabib'.")
1217         return
1218     file.header.insert(i + 1, '\\use_bibtopic 0')
1219
1220
1221 def revert_bibtopic(file):
1222     i = find_token(file.header, '\\use_bibtopic', 0)
1223     if i == -1:
1224         file.warning("Malformed lyx file: Missing '\\use_bibtopic'.")
1225         return
1226     if get_value(file.header, '\\use_bibtopic', 0) != "0":
1227         file.warning("Conversion of '\\use_bibtopic = 1' not yet implemented.")
1228         # Don't remove '\\use_jurabib' so that people will get warnings by lyx
1229     del file.header[i]
1230
1231 ##
1232 # Sideway Floats
1233 #
1234
1235 def convert_float(file):
1236     i = 0
1237     while 1:
1238         i = find_token(file.body, '\\begin_inset Float', i)
1239         if i == -1:
1240             return
1241         # Seach for a line starting 'wide'
1242         # If, however, we find a line starting '\begin_layout'
1243         # (_always_ present) then break with a warning message
1244         i = i + 1
1245         while 1:
1246             if (file.body[i][:4] == "wide"):
1247                 file.body.insert(i + 1, 'sideways false')
1248                 break
1249             elif (file.body[i][:13] == "\\begin_layout"):
1250                 file.warning("Malformed lyx file: Missing 'wide'.")
1251                 break
1252             i = i + 1
1253         i = i + 1
1254
1255
1256 def revert_float(file):
1257     i = 0
1258     while 1:
1259         i = find_token(file.body, '\\begin_inset Float', i)
1260         if i == -1:
1261             return
1262         j = find_end_of_inset(file.body, i)
1263         if j == -1:
1264             file.warning("Malformed lyx file: Missing '\\end_inset'.")
1265             i = i + 1
1266             continue
1267         if get_value(file.body, 'sideways', i, j) != "false":
1268             file.warning("Conversion of 'sideways true' not yet implemented.")
1269             # Don't remove 'sideways' so that people will get warnings by lyx
1270             i = i + 1
1271             continue
1272         del_token(file.body, 'sideways', i, j)
1273         i = i + 1
1274
1275
1276 def convert_graphics(file):
1277     """ Add extension to filenames of insetgraphics if necessary.
1278     """
1279     i = 0
1280     while 1:
1281         i = find_token(file.body, "\\begin_inset Graphics", i)
1282         if i == -1:
1283             return
1284
1285         j = find_token2(file.body, "filename", i)
1286         if j == -1:
1287             return
1288         i = i + 1
1289         filename = split(file.body[j])[1]
1290         absname = os.path.normpath(os.path.join(file.dir, filename))
1291         if file.input == stdin and not os.path.isabs(filename):
1292             # We don't know the directory and cannot check the file.
1293             # We could use a heuristic and take the current directory,
1294             # and we could try to find out if filename has an extension,
1295             # but that would be just guesses and could be wrong.
1296             file.warning("""Warning: Can not determine whether file
1297          %s
1298          needs an extension when reading from standard input.
1299          You may need to correct the file manually or run
1300          lyx2lyx again with the .lyx file as commandline argument.""" % filename)
1301             continue
1302         # This needs to be the same algorithm as in pre 233 insetgraphics
1303         if access(absname, F_OK):
1304             continue
1305         if access(absname + ".ps", F_OK):
1306             file.body[j] = replace(file.body[j], filename, filename + ".ps")
1307             continue
1308         if access(absname + ".eps", F_OK):
1309             file.body[j] = replace(file.body[j], filename, filename + ".eps")
1310
1311
1312 ##
1313 # Convert firstname and surname from styles -> char styles
1314 #
1315 def convert_names(file):
1316     """ Convert in the docbook backend from firstname and surname style
1317     to charstyles.
1318     """
1319     if file.backend != "docbook":
1320         return
1321
1322     i = 0
1323
1324     while 1:
1325         i = find_token(file.body, "\\begin_layout Author", i)
1326         if i == -1:
1327             return
1328
1329         i = i + 1
1330         while file.body[i] == "":
1331             i = i + 1
1332
1333         if file.body[i][:11] != "\\end_layout" or file.body[i+2][:13] != "\\begin_deeper":
1334             i = i + 1
1335             continue
1336
1337         k = i
1338         i = find_end_of( file.body, i+3, "\\begin_deeper","\\end_deeper")
1339         if i == -1:
1340             # something is really wrong, abort
1341             file.warning("Missing \\end_deeper, after style Author.")
1342             file.warning("Aborted attempt to parse FirstName and Surname.")
1343             return
1344         firstname, surname = "", ""
1345
1346         name = file.body[k:i]
1347
1348         j = find_token(name, "\\begin_layout FirstName", 0)
1349         if j != -1:
1350             j = j + 1
1351             while(name[j] != "\\end_layout"):
1352                 firstname = firstname + name[j]
1353                 j = j + 1
1354
1355         j = find_token(name, "\\begin_layout Surname", 0)
1356         if j != -1:
1357             j = j + 1
1358             while(name[j] != "\\end_layout"):
1359                 surname = surname + name[j]
1360                 j = j + 1
1361
1362         # delete name
1363         del file.body[k+2:i+1]
1364
1365         file.body[k-1:k-1] = ["", "",
1366                           "\\begin_inset CharStyle Firstname",
1367                           "status inlined",
1368                           "",
1369                           "\\begin_layout Standard",
1370                           "",
1371                           "%s" % firstname,
1372                           "\end_layout",
1373                           "",
1374                           "\end_inset",
1375                           "",
1376                           "",
1377                           "\\begin_inset CharStyle Surname",
1378                           "status inlined",
1379                           "",
1380                           "\\begin_layout Standard",
1381                           "",
1382                           "%s" % surname,
1383                           "\\end_layout",
1384                           "",
1385                           "\\end_inset",
1386                           ""]
1387
1388
1389 def revert_names(file):
1390     """ Revert in the docbook backend from firstname and surname char style
1391     to styles.
1392     """
1393     if file.backend != "docbook":
1394         return
1395
1396
1397 ##
1398 #    \use_natbib 1                       \cite_engine <style>
1399 #    \use_numerical_citations 0     ->   where <style> is one of
1400 #    \use_jurabib 0                      "basic", "natbib_authoryear",
1401 #                                        "natbib_numerical" or "jurabib"
1402 def convert_cite_engine(file):
1403     a = find_token(file.header, "\\use_natbib", 0)
1404     if a == -1:
1405         file.warning("Malformed lyx file: Missing '\\use_natbib'.")
1406         return
1407
1408     b = find_token(file.header, "\\use_numerical_citations", 0)
1409     if b == -1 or b != a+1:
1410         file.warning("Malformed lyx file: Missing '\\use_numerical_citations'.")
1411         return
1412
1413     c = find_token(file.header, "\\use_jurabib", 0)
1414     if c == -1 or c != b+1:
1415         file.warning("Malformed lyx file: Missing '\\use_jurabib'.")
1416         return
1417
1418     use_natbib = int(split(file.header[a])[1])
1419     use_numerical_citations = int(split(file.header[b])[1])
1420     use_jurabib = int(split(file.header[c])[1])
1421
1422     cite_engine = "basic"
1423     if use_natbib:
1424         if use_numerical_citations:
1425             cite_engine = "natbib_numerical"
1426         else:
1427              cite_engine = "natbib_authoryear"
1428     elif use_jurabib:
1429         cite_engine = "jurabib"
1430
1431     del file.header[a:c+1]
1432     file.header.insert(a, "\\cite_engine " + cite_engine)
1433
1434
1435 def revert_cite_engine(file):
1436     i = find_token(file.header, "\\cite_engine", 0)
1437     if i == -1:
1438         file.warning("Malformed lyx file: Missing '\\cite_engine'.")
1439         return
1440
1441     cite_engine = split(file.header[i])[1]
1442
1443     use_natbib = '0'
1444     use_numerical = '0'
1445     use_jurabib = '0'
1446     if cite_engine == "natbib_numerical":
1447         use_natbib = '1'
1448         use_numerical = '1'
1449     elif cite_engine == "natbib_authoryear":
1450         use_natbib = '1'
1451     elif cite_engine == "jurabib":
1452         use_jurabib = '1'
1453
1454     del file.header[i]
1455     file.header.insert(i, "\\use_jurabib " + use_jurabib)
1456     file.header.insert(i, "\\use_numerical_citations " + use_numerical)
1457     file.header.insert(i, "\\use_natbib " + use_natbib)
1458
1459
1460 ##
1461 # Paper package
1462 #
1463 def convert_paperpackage(file):
1464     i = find_token(file.header, "\\paperpackage", 0)
1465     if i == -1:
1466         file.warning("Malformed lyx file: Missing '\\paperpackage'.")
1467         return
1468
1469     packages = {'a4':'none', 'a4wide':'a4', 'widemarginsa4':'a4wide'}
1470     paperpackage = split(file.header[i])[1]
1471     file.header[i] = replace(file.header[i], paperpackage, packages[paperpackage])
1472
1473
1474 def revert_paperpackage(file):
1475     i = find_token(file.header, "\\paperpackage", 0)
1476     if i == -1:
1477         file.warning("Malformed lyx file: Missing '\\paperpackage'.")
1478         return
1479
1480     packages = {'none':'a4', 'a4':'a4wide', 'a4wide':'widemarginsa4',
1481                 'widemarginsa4':''}
1482     paperpackage = split(file.header[i])[1]
1483     file.header[i] = replace(file.header[i], paperpackage, packages[paperpackage])
1484
1485
1486 ##
1487 # Bullets
1488 #
1489 def convert_bullets(file):
1490     i = 0
1491     while 1:
1492         i = find_token(file.header, "\\bullet", i)
1493         if i == -1:
1494             return
1495         if file.header[i][:12] == '\\bulletLaTeX':
1496             file.header[i] = file.header[i] + ' ' + strip(file.header[i+1])
1497             n = 3
1498         else:
1499             file.header[i] = file.header[i] + ' ' + strip(file.header[i+1]) +\
1500                         ' ' + strip(file.header[i+2]) + ' ' + strip(file.header[i+3])
1501             n = 5
1502         del file.header[i+1:i + n]
1503         i = i + 1
1504
1505
1506 def revert_bullets(file):
1507     i = 0
1508     while 1:
1509         i = find_token(file.header, "\\bullet", i)
1510         if i == -1:
1511             return
1512         if file.header[i][:12] == '\\bulletLaTeX':
1513             n = find(file.header[i], '"')
1514             if n == -1:
1515                 file.warning("Malformed header.")
1516                 return
1517             else:
1518                 file.header[i:i+1] = [file.header[i][:n-1],'\t' + file.header[i][n:], '\\end_bullet']
1519             i = i + 3
1520         else:
1521             frag = split(file.header[i])
1522             if len(frag) != 5:
1523                 file.warning("Malformed header.")
1524                 return
1525             else:
1526                 file.header[i:i+1] = [frag[0] + ' ' + frag[1],
1527                                  '\t' + frag[2],
1528                                  '\t' + frag[3],
1529                                  '\t' + frag[4],
1530                                  '\\end_bullet']
1531                 i = i + 5
1532
1533
1534 ##
1535 # \begin_header and \begin_document
1536 #
1537 def add_begin_header(file):
1538     i = find_token(file.header, '\\lyxformat', 0)
1539     file.header.insert(i+1, '\\begin_header')
1540     file.header.insert(i+1, '\\begin_document')
1541
1542
1543 def remove_begin_header(file):
1544     i = find_token(file.header, "\\begin_document", 0)
1545     if i != -1:
1546         del file.header[i]
1547     i = find_token(file.header, "\\begin_header", 0)
1548     if i != -1:
1549         del file.header[i]
1550
1551
1552 ##
1553 # \begin_file.body and \end_file.body
1554 #
1555 def add_begin_body(file):
1556     file.body.insert(0, '\\begin_body')
1557     file.body.insert(1, '')
1558     i = find_token(file.body, "\\end_document", 0)
1559     file.body.insert(i, '\\end_body')
1560
1561 def remove_begin_body(file):
1562     i = find_token(file.body, "\\begin_body", 0)
1563     if i != -1:
1564         del file.body[i]
1565         if not file.body[i]:
1566             del file.body[i]
1567     i = find_token(file.body, "\\end_body", 0)
1568     if i != -1:
1569         del file.body[i]
1570
1571
1572 ##
1573 # \papersize
1574 #
1575 def normalize_papersize(file):
1576     i = find_token(file.header, '\\papersize', 0)
1577     if i == -1:
1578         return
1579
1580     tmp = split(file.header[i])
1581     if tmp[1] == "Default":
1582         file.header[i] = '\\papersize default'
1583         return
1584     if tmp[1] == "Custom":
1585         file.header[i] = '\\papersize custom'
1586
1587
1588 def denormalize_papersize(file):
1589     i = find_token(file.header, '\\papersize', 0)
1590     if i == -1:
1591         return
1592
1593     tmp = split(file.header[i])
1594     if tmp[1] == "custom":
1595         file.header[i] = '\\papersize Custom'
1596
1597
1598 ##
1599 # Strip spaces at end of command line
1600 #
1601 def strip_end_space(file):
1602     for i in range(len(file.body)):
1603         if file.body[i][:1] == '\\':
1604             file.body[i] = strip(file.body[i])
1605
1606
1607 ##
1608 # Use boolean values for \use_geometry, \use_bibtopic and \tracking_changes
1609 #
1610 def use_x_boolean(file):
1611     bin2bool = {'0': 'false', '1': 'true'}
1612     for use in '\\use_geometry', '\\use_bibtopic', '\\tracking_changes':
1613         i = find_token(file.header, use, 0)
1614         if i == -1:
1615             continue
1616         decompose = split(file.header[i])
1617         file.header[i] = decompose[0] + ' ' + bin2bool[decompose[1]]
1618
1619
1620 def use_x_binary(file):
1621     bool2bin = {'false': '0', 'true': '1'}
1622     for use in '\\use_geometry', '\\use_bibtopic', '\\tracking_changes':
1623         i = find_token(file.header, use, 0)
1624         if i == -1:
1625             continue
1626         decompose = split(file.header[i])
1627         file.header[i] = decompose[0] + ' ' + bool2bin[decompose[1]]
1628
1629 ##
1630 # Place all the paragraph parameters in their own line
1631 #
1632 def normalize_paragraph_params(file):
1633     body = file.body
1634     allowed_parameters = '\\paragraph_spacing', '\\noindent', '\\align', '\\labelwidthstring', "\\start_of_appendix"
1635
1636     i = 0
1637     while 1:
1638         i = find_token(file.body, '\\begin_layout', i)
1639         if i == -1:
1640             return
1641
1642         i = i + 1
1643         while 1:
1644             if strip(body[i]) and split(body[i])[0] not in allowed_parameters:
1645                 break
1646
1647             j = find(body[i],'\\', 1)
1648
1649             if j != -1:
1650                 body[i:i+1] = [strip(body[i][:j]), body[i][j:]]
1651
1652             i = i + 1
1653
1654
1655 ##
1656 # Add/remove output_changes parameter
1657 #
1658 def convert_output_changes (file):
1659     i = find_token(file.header, '\\tracking_changes', 0)
1660     if i == -1:
1661         file.warning("Malformed lyx file: Missing '\\tracking_changes'.")
1662         return
1663     file.header.insert(i+1, '\\output_changes true')
1664
1665
1666 def revert_output_changes (file):
1667     i = find_token(file.header, '\\output_changes', 0)
1668     if i == -1:
1669         return
1670     del file.header[i]
1671
1672
1673 ##
1674 # Convert paragraph breaks and sanitize paragraphs
1675 #
1676 def convert_ert_paragraphs(file):
1677     forbidden_settings = [
1678                           # paragraph parameters
1679                           '\\paragraph_spacing', '\\labelwidthstring',
1680                           '\\start_of_appendix', '\\noindent',
1681                           '\\leftindent', '\\align',
1682                           # font settings
1683                           '\\family', '\\series', '\\shape', '\\size',
1684                           '\\emph', '\\numeric', '\\bar', '\\noun',
1685                           '\\color', '\\lang']
1686     i = 0
1687     while 1:
1688         i = find_token(file.body, '\\begin_inset ERT', i)
1689         if i == -1:
1690             return
1691         j = find_end_of_inset(file.body, i)
1692         if j == -1:
1693             file.warning("Malformed lyx file: Missing '\\end_inset'.")
1694             i = i + 1
1695             continue
1696
1697         # convert non-standard paragraphs to standard
1698         k = i
1699         while 1:
1700             k = find_token(file.body, "\\begin_layout", k, j)
1701             if k == -1:
1702                 break
1703             file.body[k] = "\\begin_layout Standard"
1704             k = k + 1
1705
1706         # remove all paragraph parameters and font settings
1707         k = i
1708         while k < j:
1709             if (strip(file.body[k]) and
1710                 split(file.body[k])[0] in forbidden_settings):
1711                 del file.body[k]
1712                 j = j - 1
1713             else:
1714                 k = k + 1
1715
1716         # insert an empty paragraph before each paragraph but the first
1717         k = i
1718         first_pagraph = 1
1719         while 1:
1720             k = find_token(file.body, "\\begin_layout Standard", k, j)
1721             if k == -1:
1722                 break
1723             if first_pagraph:
1724                 first_pagraph = 0
1725                 k = k + 1
1726                 continue
1727             file.body[k:k] = ["\\begin_layout Standard", "",
1728                               "\\end_layout", ""]
1729             k = k + 5
1730             j = j + 4
1731
1732         # convert \\newline to new paragraph
1733         k = i
1734         while 1:
1735             k = find_token(file.body, "\\newline", k, j)
1736             if k == -1:
1737                 break
1738             file.body[k:k+1] = ["\\end_layout", "", "\\begin_layout Standard"]
1739             k = k + 4
1740             j = j + 3
1741         i = i + 1
1742
1743
1744 ##
1745 # Remove double paragraph breaks
1746 #
1747 def revert_ert_paragraphs(file):
1748     i = 0
1749     while 1:
1750         i = find_token(file.body, '\\begin_inset ERT', i)
1751         if i == -1:
1752             return
1753         j = find_end_of_inset(file.body, i)
1754         if j == -1:
1755             file.warning("Malformed lyx file: Missing '\\end_inset'.")
1756             i = i + 1
1757             continue
1758
1759         # replace paragraph breaks with \newline
1760         k = i
1761         while 1:
1762             k = find_token(file.body, "\\end_layout", k, j)
1763             l = find_token(file.body, "\\begin_layout", k, j)
1764             if k == -1 or l == -1:
1765                 break
1766             file.body[k:l+1] = ["\\newline"]
1767             j = j - l + k
1768             k = k + 1
1769
1770         # replace double \newlines with paragraph breaks
1771         k = i
1772         while 1:
1773             k = find_token(file.body, "\\newline", k, j)
1774             if k == -1:
1775                 break
1776             l = k + 1
1777             while file.body[l] == "":
1778                 l = l + 1
1779             if strip(file.body[l]) and split(file.body[l])[0] == "\\newline":
1780                 file.body[k:l+1] = ["\\end_layout", "",
1781                                     "\\begin_layout Standard"]
1782                 j = j - l + k + 2
1783                 k = k + 3
1784             else:
1785                 k = k + 1
1786         i = i + 1
1787
1788
1789 ##
1790 # Convertion hub
1791 #
1792
1793 convert = [[223, [insert_tracking_changes, add_end_header, remove_color_default,
1794                   convert_spaces, convert_bibtex, remove_insetparent]],
1795            [224, [convert_external, convert_comment]],
1796            [225, [add_end_layout, layout2begin_layout, convert_end_document,
1797                   convert_table_valignment_middle, convert_breaks]],
1798            [226, [convert_note]],
1799            [227, [convert_box]],
1800            [228, [convert_collapsable, convert_ert]],
1801            [229, [convert_minipage]],
1802            [230, [convert_jurabib]],
1803            [231, [convert_float]],
1804            [232, [convert_bibtopic]],
1805            [233, [convert_graphics, convert_names]],
1806            [234, [convert_cite_engine]],
1807            [235, [convert_paperpackage]],
1808            [236, [convert_bullets, add_begin_header, add_begin_body,
1809                   normalize_papersize, strip_end_space]],
1810            [237, [use_x_boolean]],
1811            [238, [update_latexaccents]],
1812            [239, [normalize_paragraph_params]],
1813            [240, [convert_output_changes]],
1814            [241, [convert_ert_paragraphs]]]
1815
1816 revert =  [[240, [revert_ert_paragraphs]],
1817            [239, [revert_output_changes]],
1818            [238, []],
1819            [237, []],
1820            [236, [use_x_binary]],
1821            [235, [denormalize_papersize, remove_begin_body,remove_begin_header,
1822                   revert_bullets]],
1823            [234, [revert_paperpackage]],
1824            [233, [revert_cite_engine]],
1825            [232, [revert_names]],
1826            [231, [revert_bibtopic]],
1827            [230, [revert_float]],
1828            [229, [revert_jurabib]],
1829            [228, []],
1830            [227, [revert_collapsable, revert_ert]],
1831            [226, [revert_box, revert_external_2]],
1832            [225, [revert_note]],
1833            [224, [rm_end_layout, begin_layout2layout, revert_end_document,
1834                   revert_valignment_middle, convert_vspace, convert_frameless_box]],
1835            [223, [revert_external_2, revert_comment]],
1836            [221, [rm_end_header, revert_spaces, revert_bibtex,
1837                   rm_tracking_changes, rm_body_changes]]]
1838
1839
1840 if __name__ == "__main__":
1841     pass