1 # This file is part of lyx2lyx
2 # -*- coding: utf-8 -*-
3 # Copyright (C) 2006 José Matos <jamatos@lyx.org>
4 # Copyright (C) 2004-2006 Georg Baum <Georg.Baum@post.rwth-aachen.de>
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 """ Convert files to the file format generated by lyx 1.5"""
23 from parser_tools import find_token, find_token_exact, find_tokens, find_end_of, get_value
24 from LyX import get_encoding
27 ####################################################################
28 # Private helper functions
30 def find_end_of_inset(lines, i):
31 " Find beginning of inset, where lines[i] is included."
32 return find_end_of(lines, i, "\\begin_inset", "\\end_inset")
34 # End of helper functions
35 ####################################################################
39 # Notes: Framed/Shaded
42 def revert_framed(document):
43 "Revert framed notes. "
46 i = find_tokens(document.body, ["\\begin_inset Note Framed", "\\begin_inset Note Shaded"], i)
50 document.body[i] = "\\begin_inset Note"
58 roman_fonts = {'default' : 'default', 'ae' : 'ae',
59 'times' : 'times', 'palatino' : 'palatino',
60 'helvet' : 'default', 'avant' : 'default',
61 'newcent' : 'newcent', 'bookman' : 'bookman',
63 sans_fonts = {'default' : 'default', 'ae' : 'default',
64 'times' : 'default', 'palatino' : 'default',
65 'helvet' : 'helvet', 'avant' : 'avant',
66 'newcent' : 'default', 'bookman' : 'default',
68 typewriter_fonts = {'default' : 'default', 'ae' : 'default',
69 'times' : 'default', 'palatino' : 'default',
70 'helvet' : 'default', 'avant' : 'default',
71 'newcent' : 'default', 'bookman' : 'default',
72 'pslatex' : 'courier'}
74 def convert_font_settings(document):
75 " Convert font settings. "
77 i = find_token_exact(document.header, "\\fontscheme", i)
79 document.warning("Malformed LyX document: Missing `\\fontscheme'.")
81 font_scheme = get_value(document.header, "\\fontscheme", i, i + 1)
83 document.warning("Malformed LyX document: Empty `\\fontscheme'.")
84 font_scheme = 'default'
85 if not font_scheme in roman_fonts.keys():
86 document.warning("Malformed LyX document: Unknown `\\fontscheme' `%s'." % font_scheme)
87 font_scheme = 'default'
88 document.header[i:i+1] = ['\\font_roman %s' % roman_fonts[font_scheme],
89 '\\font_sans %s' % sans_fonts[font_scheme],
90 '\\font_typewriter %s' % typewriter_fonts[font_scheme],
91 '\\font_default_family default',
94 '\\font_sf_scale 100',
95 '\\font_tt_scale 100']
98 def revert_font_settings(document):
99 " Revert font settings. "
102 fonts = {'roman' : 'default', 'sans' : 'default', 'typewriter' : 'default'}
103 for family in 'roman', 'sans', 'typewriter':
104 name = '\\font_%s' % family
105 i = find_token_exact(document.header, name, i)
107 document.warning("Malformed LyX document: Missing `%s'." % name)
110 if (insert_line < 0):
112 fonts[family] = get_value(document.header, name, i, i + 1)
113 del document.header[i]
114 i = find_token_exact(document.header, '\\font_default_family', i)
116 document.warning("Malformed LyX document: Missing `\\font_default_family'.")
117 font_default_family = 'default'
119 font_default_family = get_value(document.header, "\\font_default_family", i, i + 1)
120 del document.header[i]
121 i = find_token_exact(document.header, '\\font_sc', i)
123 document.warning("Malformed LyX document: Missing `\\font_sc'.")
126 font_sc = get_value(document.header, '\\font_sc', i, i + 1)
127 del document.header[i]
128 if font_sc != 'false':
129 document.warning("Conversion of '\\font_sc' not yet implemented.")
130 i = find_token_exact(document.header, '\\font_osf', i)
132 document.warning("Malformed LyX document: Missing `\\font_osf'.")
135 font_osf = get_value(document.header, '\\font_osf', i, i + 1)
136 del document.header[i]
137 i = find_token_exact(document.header, '\\font_sf_scale', i)
139 document.warning("Malformed LyX document: Missing `\\font_sf_scale'.")
140 font_sf_scale = '100'
142 font_sf_scale = get_value(document.header, '\\font_sf_scale', i, i + 1)
143 del document.header[i]
144 if font_sf_scale != '100':
145 document.warning("Conversion of '\\font_sf_scale' not yet implemented.")
146 i = find_token_exact(document.header, '\\font_tt_scale', i)
148 document.warning("Malformed LyX document: Missing `\\font_tt_scale'.")
149 font_tt_scale = '100'
151 font_tt_scale = get_value(document.header, '\\font_tt_scale', i, i + 1)
152 del document.header[i]
153 if font_tt_scale != '100':
154 document.warning("Conversion of '\\font_tt_scale' not yet implemented.")
155 for font_scheme in roman_fonts.keys():
156 if (roman_fonts[font_scheme] == fonts['roman'] and
157 sans_fonts[font_scheme] == fonts['sans'] and
158 typewriter_fonts[font_scheme] == fonts['typewriter']):
159 document.header.insert(insert_line, '\\fontscheme %s' % font_scheme)
160 if font_default_family != 'default':
161 document.preamble.append('\\renewcommand{\\familydefault}{\\%s}' % font_default_family)
162 if font_osf == 'true':
163 document.warning("Ignoring `\\font_osf = true'")
165 font_scheme = 'default'
166 document.header.insert(insert_line, '\\fontscheme %s' % font_scheme)
167 if fonts['roman'] == 'cmr':
168 document.preamble.append('\\renewcommand{\\rmdefault}{cmr}')
169 if font_osf == 'true':
170 document.preamble.append('\\usepackage{eco}')
172 for font in 'lmodern', 'charter', 'utopia', 'beraserif', 'ccfonts', 'chancery':
173 if fonts['roman'] == font:
174 document.preamble.append('\\usepackage{%s}' % font)
175 for font in 'cmss', 'lmss', 'cmbr':
176 if fonts['sans'] == font:
177 document.preamble.append('\\renewcommand{\\sfdefault}{%s}' % font)
178 for font in 'berasans':
179 if fonts['sans'] == font:
180 document.preamble.append('\\usepackage{%s}' % font)
181 for font in 'cmtt', 'lmtt', 'cmtl':
182 if fonts['typewriter'] == font:
183 document.preamble.append('\\renewcommand{\\ttdefault}{%s}' % font)
184 for font in 'courier', 'beramono', 'luximono':
185 if fonts['typewriter'] == font:
186 document.preamble.append('\\usepackage{%s}' % font)
187 if font_default_family != 'default':
188 document.preamble.append('\\renewcommand{\\familydefault}{\\%s}' % font_default_family)
189 if font_osf == 'true':
190 document.warning("Ignoring `\\font_osf = true'")
193 def revert_booktabs(document):
194 " We remove the booktabs flag or everything else will become a mess. "
195 re_row = re.compile(r'^<row.*space="[^"]+".*>$')
196 re_tspace = re.compile(r'\s+topspace="[^"]+"')
197 re_bspace = re.compile(r'\s+bottomspace="[^"]+"')
198 re_ispace = re.compile(r'\s+interlinespace="[^"]+"')
201 i = find_token(document.body, "\\begin_inset Tabular", i)
204 j = find_end_of_inset(document.body, i + 1)
206 document.warning("Malformed LyX document: Could not find end of tabular.")
208 for k in range(i, j):
209 if re.search('^<features.* booktabs="true".*>$', document.body[k]):
210 document.warning("Converting 'booktabs' table to normal table.")
211 document.body[k] = document.body[k].replace(' booktabs="true"', '')
212 if re.search(re_row, document.body[k]):
213 document.warning("Removing extra row space.")
214 document.body[k] = re_tspace.sub('', document.body[k])
215 document.body[k] = re_bspace.sub('', document.body[k])
216 document.body[k] = re_ispace.sub('', document.body[k])
220 def convert_utf8(document):
221 document.encoding = "utf8"
224 def revert_utf8(document):
225 i = find_token(document.header, "\\inputencoding", 0)
227 document.header.append("\\inputencoding auto")
228 elif get_value(document.header, "\\inputencoding", i) == "utf8":
229 document.header[i] = "\\inputencoding auto"
230 document.inputencoding = get_value(document.header, "\\inputencoding", 0)
231 document.encoding = get_encoding(document.language, document.inputencoding, 248)
234 def revert_cs_label(document):
235 " Remove status flag of charstyle label. "
238 i = find_token(document.body, "\\begin_inset CharStyle", i)
241 # Seach for a line starting 'show_label'
242 # If it is not there, break with a warning message
245 if (document.body[i][:10] == "show_label"):
248 elif (document.body[i][:13] == "\\begin_layout"):
249 document.warning("Malformed LyX document: Missing 'show_label'.")
256 def convert_bibitem(document):
258 \bibitem [option]{argument}
262 \begin_inset LatexCommand bibitem
268 This must be called after convert_commandparams.
270 regex = re.compile(r'\S+\s*(\[[^\[\{]*\])?(\{[^}]*\})')
273 i = find_token(document.body, "\\bibitem", i)
276 match = re.match(regex, document.body[i])
277 option = match.group(1)
278 argument = match.group(2)
279 lines = ['\\begin_inset LatexCommand bibitem']
281 lines.append('label "%s"' % option[1:-1].replace('"', '\\"'))
282 lines.append('key "%s"' % argument[1:-1].replace('"', '\\"'))
284 lines.append('\\end_inset')
285 document.body[i:i+1] = lines
289 commandparams_info = {
290 # command : [option1, option2, argument]
291 "bibitem" : ["label", "", "key"],
292 "bibtex" : ["options", "btprint", "bibfiles"],
293 "cite" : ["after", "before", "key"],
294 "citet" : ["after", "before", "key"],
295 "citep" : ["after", "before", "key"],
296 "citealt" : ["after", "before", "key"],
297 "citealp" : ["after", "before", "key"],
298 "citeauthor" : ["after", "before", "key"],
299 "citeyear" : ["after", "before", "key"],
300 "citeyearpar" : ["after", "before", "key"],
301 "citet*" : ["after", "before", "key"],
302 "citep*" : ["after", "before", "key"],
303 "citealt*" : ["after", "before", "key"],
304 "citealp*" : ["after", "before", "key"],
305 "citeauthor*" : ["after", "before", "key"],
306 "Citet" : ["after", "before", "key"],
307 "Citep" : ["after", "before", "key"],
308 "Citealt" : ["after", "before", "key"],
309 "Citealp" : ["after", "before", "key"],
310 "Citeauthor" : ["after", "before", "key"],
311 "Citet*" : ["after", "before", "key"],
312 "Citep*" : ["after", "before", "key"],
313 "Citealt*" : ["after", "before", "key"],
314 "Citealp*" : ["after", "before", "key"],
315 "Citeauthor*" : ["after", "before", "key"],
316 "citefield" : ["after", "before", "key"],
317 "citetitle" : ["after", "before", "key"],
318 "cite*" : ["after", "before", "key"],
319 "hfill" : ["", "", ""],
320 "index" : ["", "", "name"],
321 "printindex" : ["", "", "name"],
322 "label" : ["", "", "name"],
323 "eqref" : ["name", "", "reference"],
324 "pageref" : ["name", "", "reference"],
325 "prettyref" : ["name", "", "reference"],
326 "ref" : ["name", "", "reference"],
327 "vpageref" : ["name", "", "reference"],
328 "vref" : ["name", "", "reference"],
329 "tableofcontents" : ["", "", "type"],
330 "htmlurl" : ["name", "", "target"],
331 "url" : ["name", "", "target"]}
334 def convert_commandparams(document):
337 \begin_inset LatexCommand \cmdname[opt1][opt2]{arg}
342 \begin_inset LatexCommand cmdname
348 name1, name2 and name3 can be different for each command.
350 # \begin_inset LatexCommand bibitem was not the official version (see
351 # convert_bibitem()), but could be read in, so we convert it here, too.
355 i = find_token(document.body, "\\begin_inset LatexCommand", i)
358 command = document.body[i][26:].strip()
360 document.warning("Malformed LyX document: Missing LatexCommand name.")
364 # The following parser is taken from the original InsetCommandParams::scanCommand
370 # Used to handle things like \command[foo[bar]]{foo{bar}}
374 if ((state == "CMDNAME" and c == ' ') or
375 (state == "CMDNAME" and c == '[') or
376 (state == "CMDNAME" and c == '{')):
378 if ((state == "OPTION" and c == ']') or
379 (state == "SECOPTION" and c == ']') or
380 (state == "CONTENT" and c == '}')):
384 nestdepth = nestdepth - 1
385 if ((state == "OPTION" and c == '[') or
386 (state == "SECOPTION" and c == '[') or
387 (state == "CONTENT" and c == '{')):
388 nestdepth = nestdepth + 1
389 if state == "CMDNAME":
391 elif state == "OPTION":
393 elif state == "SECOPTION":
395 elif state == "CONTENT":
400 elif c == '[' and b != ']':
402 nestdepth = 0 # Just to be sure
403 elif c == '[' and b == ']':
405 nestdepth = 0 # Just to be sure
408 nestdepth = 0 # Just to be sure
411 # Now we have parsed the command, output the parameters
412 lines = ["\\begin_inset LatexCommand %s" % name]
414 if commandparams_info[name][0] == "":
415 document.warning("Ignoring invalid option `%s' of command `%s'." % (option1, name))
417 lines.append('%s "%s"' % (commandparams_info[name][0], option1.replace('"', '\\"')))
419 if commandparams_info[name][1] == "":
420 document.warning("Ignoring invalid second option `%s' of command `%s'." % (option2, name))
422 lines.append('%s "%s"' % (commandparams_info[name][1], option2.replace('"', '\\"')))
424 if commandparams_info[name][2] == "":
425 document.warning("Ignoring invalid argument `%s' of command `%s'." % (argument, name))
427 lines.append('%s "%s"' % (commandparams_info[name][2], argument.replace('"', '\\"')))
428 document.body[i:i+1] = lines
432 def revert_commandparams(document):
433 regex = re.compile(r'(\S+)\s+(.+)')
436 i = find_token(document.body, "\\begin_inset LatexCommand", i)
439 name = document.body[i].split()[2]
440 j = find_end_of_inset(document.body, i + 1)
445 for k in range(i + 1, j):
446 match = re.match(regex, document.body[k])
448 pname = match.group(1)
449 pvalue = match.group(2)
450 if pname == "preview":
451 preview_line = document.body[k]
452 elif (commandparams_info[name][0] != "" and
453 pname == commandparams_info[name][0]):
454 option1 = pvalue.strip('"').replace('\\"', '"')
455 elif (commandparams_info[name][1] != "" and
456 pname == commandparams_info[name][1]):
457 option2 = pvalue.strip('"').replace('\\"', '"')
458 elif (commandparams_info[name][2] != "" and
459 pname == commandparams_info[name][2]):
460 argument = pvalue.strip('"').replace('\\"', '"')
461 elif document.body[k].strip() != "":
462 document.warning("Ignoring unknown contents `%s' in command inset %s." % (document.body[k], name))
463 if name == "bibitem":
465 lines = ["\\bibitem {%s}" % argument]
467 lines = ["\\bibitem [%s]{%s}" % (option1, argument)]
471 lines = ["\\begin_inset LatexCommand \\%s{%s}" % (name, argument)]
473 lines = ["\\begin_inset LatexCommand \\%s[][%s]{%s}" % (name, option2, argument)]
476 lines = ["\\begin_inset LatexCommand \\%s[%s]{%s}" % (name, option1, argument)]
478 lines = ["\\begin_inset LatexCommand \\%s[%s][%s]{%s}" % (name, option1, option2, argument)]
479 if name != "bibitem":
480 if preview_line != "":
481 lines.append(preview_line)
483 lines.append('\\end_inset')
484 document.body[i:j+1] = lines
488 def revert_nomenclature(document):
489 " Convert nomenclature entry to ERT. "
490 regex = re.compile(r'(\S+)\s+(.+)')
494 i = find_token(document.body, "\\begin_inset LatexCommand nomenclature", i)
498 j = find_end_of_inset(document.body, i + 1)
503 for k in range(i + 1, j):
504 match = re.match(regex, document.body[k])
506 name = match.group(1)
507 value = match.group(2)
508 if name == "preview":
509 preview_line = document.body[k]
510 elif name == "symbol":
511 symbol = value.strip('"').replace('\\"', '"')
512 elif name == "description":
513 description = value.strip('"').replace('\\"', '"')
514 elif name == "prefix":
515 prefix = value.strip('"').replace('\\"', '"')
516 elif document.body[k].strip() != "":
517 document.warning("Ignoring unknown contents `%s' in nomenclature inset." % document.body[k])
519 command = 'nomenclature{%s}{%s}' % (symbol, description)
521 command = 'nomenclature[%s]{%s}{%s}' % (prefix, symbol, description)
522 document.body[i:j+1] = ['\\begin_inset ERT',
525 '\\begin_layout %s' % document.default_layout,
534 if use_nomencl and find_token(document.preamble, '\\usepackage{nomencl}[2005/09/22]', 0) == -1:
535 document.preamble.append('\\usepackage{nomencl}[2005/09/22]')
536 document.preamble.append('\\makenomenclature')
539 def revert_printnomenclature(document):
540 " Convert printnomenclature to ERT. "
541 regex = re.compile(r'(\S+)\s+(.+)')
545 i = find_token(document.body, "\\begin_inset LatexCommand printnomenclature", i)
549 j = find_end_of_inset(document.body, i + 1)
552 for k in range(i + 1, j):
553 match = re.match(regex, document.body[k])
555 name = match.group(1)
556 value = match.group(2)
557 if name == "preview":
558 preview_line = document.body[k]
559 elif name == "labelwidth":
560 labelwidth = value.strip('"').replace('\\"', '"')
561 elif document.body[k].strip() != "":
562 document.warning("Ignoring unknown contents `%s' in printnomenclature inset." % document.body[k])
564 command = 'nomenclature{}'
566 command = 'nomenclature[%s]' % labelwidth
567 document.body[i:j+1] = ['\\begin_inset ERT',
570 '\\begin_layout %s' % document.default_layout,
579 if use_nomencl and find_token(document.preamble, '\\usepackage{nomencl}[2005/09/22]', 0) == -1:
580 document.preamble.append('\\usepackage{nomencl}[2005/09/22]')
581 document.preamble.append('\\makenomenclature')
584 def convert_esint(document):
585 " Add \\use_esint setting to header. "
586 i = find_token(document.header, "\\cite_engine", 0)
588 document.warning("Malformed LyX document: Missing `\\cite_engine'.")
590 # 0 is off, 1 is auto, 2 is on.
591 document.header.insert(i, '\\use_esint 0')
594 def revert_esint(document):
595 " Remove \\use_esint setting from header. "
596 i = find_token(document.header, "\\use_esint", 0)
598 document.warning("Malformed LyX document: Missing `\\use_esint'.")
600 use_esint = document.header[i].split()[1]
601 del document.header[i]
602 # 0 is off, 1 is auto, 2 is on.
604 document.preamble.append('\\usepackage{esint}')
611 supported_versions = ["1.5.0","1.5"]
612 convert = [[246, []],
613 [247, [convert_font_settings]],
615 [249, [convert_utf8]],
618 [252, [convert_commandparams, convert_bibitem]],
620 [254, [convert_esint]]]
622 revert = [[253, [revert_esint]],
623 [252, [revert_nomenclature, revert_printnomenclature]],
624 [251, [revert_commandparams]],
625 [250, [revert_cs_label]],
627 [248, [revert_utf8]],
628 [247, [revert_booktabs]],
629 [246, [revert_font_settings]],
630 [245, [revert_framed]]]
633 if __name__ == "__main__":