]> git.lyx.org Git - features.git/blob - lib/lyx2lyx/lyx_2_4.py
Amend 366e37c: Add also the conversion routine
[features.git] / lib / lyx2lyx / lyx_2_4.py
1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # Copyright (C) 2018 The LyX team
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 2.4"""
20
21 import re, string
22 import unicodedata
23 import sys, os
24
25 from datetime import (datetime, date, time)
26
27 # Uncomment only what you need to import, please.
28
29 from parser_tools import (count_pars_in_inset, find_end_of_inset, find_end_of_layout,
30                           find_token, find_re, get_bool_value, get_option_value, get_value, get_quoted_value)
31 #    del_token, del_value, del_complete_lines,
32 #    find_complete_lines, find_end_of,
33 #    find_re, find_substring, find_token_backwards,
34 #    get_containing_inset, get_containing_layout,
35 #    is_in_inset, set_bool_value
36 #    find_tokens, find_token_exact, check_token
37
38 from lyx2lyx_tools import (put_cmd_in_ert, add_to_preamble)
39 #  revert_font_attrs, insert_to_preamble, latex_length
40 #  get_ert, lyx2latex, lyx2verbatim, length_in_bp, convert_info_insets
41 #  revert_flex_inset, hex2ratio, str2bool
42
43 ####################################################################
44 # Private helper functions
45
46 def convert_fonts(document, font_list):
47     " Handle font definition to LaTeX "
48
49     font_types = ["\\font_roman", "\\font_sans,sf", "\\font_typewriter,tt"]
50     rpkg = re.compile(r'^\\usepackage(\[scaled=([^\]]*)\])?\{([^\}]+)\}')
51     for ft1 in font_types:
52         fts = ft1.split(",")
53         ft = fts[0]
54         if len(fts) > 1:
55             fontscale = "\\font_" + fts[1] + "_scale"
56         else:
57             fontscale = None
58         i = 0
59         i = find_re(document.preamble, rpkg, i)
60         if i == -1:
61             continue
62         mo = rpkg.search(document.preamble[i])
63         option = mo.group(2)
64         pkg = mo.group(3)
65         if not pkg in font_list:
66             continue
67         del document.preamble[i]
68         if i > 0 and document.preamble[i-1] == "% Added by lyx2lyx":
69             del document.preamble[i-1]
70         if fontscale != None:
71             j = find_token(document.header, fontscale, 0)
72             if j != -1:
73                 val = get_value(document.header, fontscale, j)
74                 vals = val.split()
75                 scale = "100"
76                 if option != None:
77                     scale = "%03d" % int(float(option) * 100)
78                 document.header[j] = fontscale + " " + scale + " " + vals[1]
79         j = find_token(document.header, ft, 0)
80         if j != -1:
81             val = get_value(document.header, ft, j)
82             vals = val.split()
83             document.header[j] = ft + ' "' + pkg + '" ' + vals[1]
84
85 def revert_fonts(document, font_list):
86     " Revert native font definition to LaTeX "
87
88     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
89         font_types = ["\\font_roman", "\\font_sans,sf", "\\font_typewriter,tt"]
90         for ft1 in font_types:
91             fts = ft1.split(",")
92             ft = fts[0]
93             i = find_token(document.header, ft, 0)
94             if i != -1:
95                 val = get_value(document.header, ft, i)
96                 words = val.split()
97                 val = words[0].replace('"', '')
98                 if val in font_list:
99                     xoption = ""
100                     document.header[i] = ft + ' "default" ' + words[1]
101                     if len(fts) > 1:
102                         xval =  get_value(document.header, "\\font_" + fts[1] + "_scale", 0)
103                         # cutoff " 100"
104                         xval = xval[:-4]
105                         if xval != "100":
106                             xoption = "[scaled=" + format(float(xval) / 100, '.2f') + "]"
107                     preamble = "\\usepackage" + xoption + "{%s}" % val
108                     add_to_preamble(document, [preamble])
109
110 ###############################################################################
111 ###
112 ### Conversion and reversion routines
113 ###
114 ###############################################################################
115
116 def convert_dejavu(document):
117     " Handle DejaVu font definition to LaTeX "
118
119     dejavu_fonts = ['DejaVuSerif', 'DejaVuSerifCondensed', 'DejaVuSans',
120                     'DejaVuSansMono', 'DejaVuSansCondensed']
121     convert_fonts(document, dejavu_fonts)
122
123 def revert_dejavu(document):
124     " Revert native DejaVu font definition to LaTeX "
125
126     dejavu_fonts = ['DejaVuSerif', 'DejaVuSerifCondensed', 'DejaVuSans',
127                     'DejaVuSansMono', 'DejaVuSansCondensed']
128     revert_fonts(document, dejavu_fonts)
129
130 def removeFrontMatterStyles(document):
131     " Remove styles Begin/EndFrontmatter"
132
133     layouts = ['BeginFrontmatter', 'EndFrontmatter']
134     for layout in layouts:
135         i = 0
136         while True:
137             i = find_token(document.body, '\\begin_layout ' + layout, i)
138             if i == -1:
139                 break
140             j = find_end_of_layout(document.body, i)
141             if j == -1:
142                 document.warning("Malformed LyX document: Can't find end of layout at line %d" % i)
143                 i += 1
144                 continue
145             while i > 0 and document.body[i-1].strip() == '':
146                 i -= 1
147             while document.body[j+1].strip() == '':
148                 j = j + 1
149             document.body[i:j+1] = ['']
150
151 def addFrontMatterStyles(document):
152     " Use styles Begin/EndFrontmatter for elsarticle"
153
154     def insertFrontmatter(prefix, line):
155         above = line
156         while above > 0 and document.body[above-1].strip() == '':
157             above -= 1
158         below = line
159         while document.body[below].strip() == '':
160             below += 1
161         document.body[above:below] = ['', '\\begin_layout ' + prefix + 'Frontmatter',
162                                     '\\begin_inset Note Note',
163                                     'status open', '',
164                                     '\\begin_layout Plain Layout',
165                                     'Keep this empty!',
166                                     '\\end_layout', '',
167                                     '\\end_inset', '', '',
168                                     '\\end_layout', '']
169
170     if document.textclass == "elsarticle":
171         layouts = ['Title', 'Title footnote', 'Author', 'Author footnote',
172                    'Corresponding author', 'Address', 'Email', 'Abstract', 'Keywords']
173         first = -1
174         last = -1
175         for layout in layouts:
176             i = 0
177             while True:
178                 i = find_token(document.body, '\\begin_layout ' + layout, i)
179                 if i == -1:
180                     break
181                 k = find_end_of_layout(document.body, i)
182                 if k == -1:
183                     document.warning("Malformed LyX document: Can't find end of layout at line %d" % i)
184                     i += 1;
185                     continue
186                 if first == -1 or i < first:
187                     first = i
188                 if last == -1 or last <= k:
189                     last = k+1
190                 i = k+1
191         if first == -1:
192             return
193         insertFrontmatter('End', last)
194         insertFrontmatter('Begin', first)
195
196 def convert_lst_literalparam(document):
197     " Add param literal to include inset "
198
199     i = 0
200     while True:
201         i = find_token(document.body, '\\begin_inset CommandInset include', i)
202         if i == -1:
203             break
204         j = find_end_of_inset(document.body, i)
205         if j == -1:
206             document.warning("Malformed LyX document: Can't find end of command inset at line %d" % i)
207             i += 1
208             continue
209         while i < j and document.body[i].strip() != '':
210             i += 1
211         document.body.insert(i, "literal \"true\"")
212
213
214 def revert_lst_literalparam(document):
215     " Remove param literal from include inset "
216
217     i = 0
218     while True:
219         i = find_token(document.body, '\\begin_inset CommandInset include', i)
220         if i == -1:
221             break
222         j = find_end_of_inset(document.body, i)
223         if j == -1:
224             document.warning("Malformed LyX document: Can't find end of include inset at line %d" % i)
225             i += 1
226             continue
227         k = find_token(document.body, 'literal', i, j)
228         if k == -1:
229             i += 1
230             continue
231         del document.body[k]
232
233
234 def revert_paratype(document):
235     " Revert ParaType font definitions to LaTeX "
236
237     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
238         preamble = ""
239         i1 = find_token(document.header, "\\font_roman \"PTSerif-TLF\"", 0)
240         i2 = find_token(document.header, "\\font_sans \"default\"", 0)
241         i3 = find_token(document.header, "\\font_typewriter \"default\"", 0)
242         j = find_token(document.header, "\\font_sans \"PTSans-TLF\"", 0)
243         sfval = get_value(document.header, "\\font_sf_scale", 0)
244         # cutoff " 100"
245         sfval = sfval[:-4]
246         sfoption = ""
247         if sfval != "100":
248             sfoption = "scaled=" + format(float(sfval) / 100, '.2f')
249         k = find_token(document.header, "\\font_typewriter \"PTMono-TLF\"", 0)
250         ttval = get_value(document.header, "\\font_tt_scale", 0)
251         # cutoff " 100"
252         ttval = ttval[:-4]
253         ttoption = ""
254         if ttval != "100":
255             ttoption = "scaled=" + format(float(ttval) / 100, '.2f')
256         if i1 != -1 and i2 != -1 and i3!= -1:
257             add_to_preamble(document, ["\\usepackage{paratype}"])
258         else:
259             if i1!= -1:
260                 add_to_preamble(document, ["\\usepackage{PTSerif}"])
261                 document.header[i1] = document.header[i1].replace("PTSerif-TLF", "default")
262             if j!= -1:
263                 if sfoption != "":
264                     add_to_preamble(document, ["\\usepackage[" + sfoption + "]{PTSans}"])
265                 else:
266                     add_to_preamble(document, ["\\usepackage{PTSans}"])
267                 document.header[j] = document.header[j].replace("PTSans-TLF", "default")
268             if k!= -1:
269                 if ttoption != "":
270                     add_to_preamble(document, ["\\usepackage[" + ttoption + "]{PTMono}"])
271                 else:
272                     add_to_preamble(document, ["\\usepackage{PTMono}"])
273                 document.header[k] = document.header[k].replace("PTMono-TLF", "default")
274
275
276 def revert_xcharter(document):
277     " Revert XCharter font definitions to LaTeX "
278
279     i = find_token(document.header, "\\font_roman \"xcharter\"", 0)
280     if i == -1:
281         return
282
283     # replace unsupported font setting
284     document.header[i] = document.header[i].replace("xcharter", "default")
285     # no need for preamble code with system fonts
286     if get_bool_value(document.header, "\\use_non_tex_fonts"):
287         return
288
289     # transfer old style figures setting to package options
290     j = find_token(document.header, "\\font_osf true")
291     if j != -1:
292         options = "[osf]"
293         document.header[j] = "\\font_osf false"
294     else:
295         options = ""
296     if i != -1:
297         add_to_preamble(document, ["\\usepackage%s{XCharter}"%options])
298
299
300 def revert_lscape(document):
301     " Reverts the landscape environment (Landscape module) to TeX-code "
302
303     if not "landscape" in document.get_module_list():
304         return
305
306     i = 0
307     while True:
308         i = find_token(document.body, "\\begin_inset Flex Landscape", i)
309         if i == -1:
310             return
311         j = find_end_of_inset(document.body, i)
312         if j == -1:
313             document.warning("Malformed LyX document: Can't find end of Landscape inset")
314             i += 1
315             continue
316
317         if document.body[i] == "\\begin_inset Flex Landscape (Floating)":
318             document.body[j - 2 : j + 1] = put_cmd_in_ert("\\end{landscape}}")
319             document.body[i : i + 4] = put_cmd_in_ert("\\afterpage{\\begin{landscape}")
320             add_to_preamble(document, ["\\usepackage{afterpage}"])
321         else:
322             document.body[j - 2 : j + 1] = put_cmd_in_ert("\\end{landscape}")
323             document.body[i : i + 4] = put_cmd_in_ert("\\begin{landscape}")
324
325         add_to_preamble(document, ["\\usepackage{pdflscape}"])
326         # no need to reset i
327
328
329 def convert_fontenc(document):
330     " Convert default fontenc setting "
331
332     i = find_token(document.header, "\\fontencoding global", 0)
333     if i == -1:
334         return
335
336     document.header[i] = document.header[i].replace("global", "auto")
337
338
339 def revert_fontenc(document):
340     " Revert default fontenc setting "
341
342     i = find_token(document.header, "\\fontencoding auto", 0)
343     if i == -1:
344         return
345
346     document.header[i] = document.header[i].replace("auto", "global")
347
348
349 def revert_nospellcheck(document):
350     " Remove nospellcheck font info param "
351
352     i = 0
353     while True:
354         i = find_token(document.body, '\\nospellcheck', i)
355         if i == -1:
356             return
357         del document.body[i]
358
359
360 def revert_floatpclass(document):
361     " Remove float placement params 'document' and 'class' "
362
363     i = 0
364     i = find_token(document.header, "\\float_placement class", 0)
365     if i != -1:
366         del document.header[i]
367
368     i = 0
369     while True:
370         i = find_token(document.body, '\\begin_inset Float', i)
371         if i == -1:
372             break
373         j = find_end_of_inset(document.body, i)
374         k = find_token(document.body, 'placement class', i, i + 2)
375         if k == -1:
376             k = find_token(document.body, 'placement document', i, i + 2)
377             if k != -1:
378                 del document.body[k]
379             i = j
380             continue
381         del document.body[k]
382
383
384 def revert_floatalignment(document):
385     " Remove float alignment params "
386
387     i = 0
388     i = find_token(document.header, "\\float_alignment", 0)
389     galignment = ""
390     if i != -1:
391         galignment = get_value(document.header, "\\float_alignment", i)
392         del document.header[i]
393
394     i = 0
395     while True:
396         i = find_token(document.body, '\\begin_inset Float', i)
397         if i == -1:
398             break
399         j = find_end_of_inset(document.body, i)
400         if j == -1:
401             document.warning("Malformed LyX document: Can't find end of inset at line " + str(i))
402             i += 1
403         k = find_token(document.body, 'alignment', i, i + 4)
404         if k == -1:
405             i = j
406             continue
407         alignment = get_value(document.body, "alignment", k)
408         if alignment == "document":
409             alignment = galignment
410         del document.body[k]
411         l = find_token(document.body, "\\begin_layout Plain Layout", i, j)
412         if l == -1:
413             document.warning("Can't find float layout!")
414             i = j
415             continue
416         alcmd = []
417         if alignment == "left":
418             alcmd = put_cmd_in_ert("\\raggedright{}")
419         elif alignment == "center":
420             alcmd = put_cmd_in_ert("\\centering{}")
421         elif alignment == "right":
422             alcmd = put_cmd_in_ert("\\raggedleft{}")
423         if len(alcmd) > 0:
424             document.body[l+1:l+1] = alcmd
425         i = j 
426
427
428 def revert_tuftecite(document):
429     " Revert \cite commands in tufte classes "
430
431     tufte = ["tufte-book", "tufte-handout"]
432     if document.textclass not in tufte:
433         return
434
435     i = 0
436     while (True):
437         i = find_token(document.body, "\\begin_inset CommandInset citation", i)
438         if i == -1:
439             break
440         j = find_end_of_inset(document.body, i)
441         if j == -1:
442             document.warning("Can't find end of citation inset at line %d!!" %(i))
443             i += 1
444             continue
445         k = find_token(document.body, "LatexCommand", i, j)
446         if k == -1:
447             document.warning("Can't find LatexCommand for citation inset at line %d!" %(i))
448             i = j + 1
449             continue
450         cmd = get_value(document.body, "LatexCommand", k)
451         if cmd != "cite":
452             i = j + 1
453             continue
454         pre = get_quoted_value(document.body, "before", i, j)
455         post = get_quoted_value(document.body, "after", i, j)
456         key = get_quoted_value(document.body, "key", i, j)
457         if not key:
458             document.warning("Citation inset at line %d does not have a key!" %(i))
459             key = "???"
460         # Replace command with ERT
461         res = "\\cite"
462         if pre:
463             res += "[" + pre + "]"
464         if post:
465             res += "[" + post + "]"
466         elif pre:
467             res += "[]"
468         res += "{" + key + "}"
469         document.body[i:j+1] = put_cmd_in_ert([res])
470         i = j + 1
471
472
473 def revert_stretchcolumn(document):
474     " We remove the column varwidth flags or everything else will become a mess. "
475     i = 0
476     while True:
477         i = find_token(document.body, "\\begin_inset Tabular", i)
478         if i == -1:
479             return
480         j = find_end_of_inset(document.body, i + 1)
481         if j == -1:
482             document.warning("Malformed LyX document: Could not find end of tabular.")
483             continue
484         for k in range(i, j):
485             if re.search('^<column.*varwidth="[^"]+".*>$', document.body[k]):
486                 document.warning("Converting 'tabularx'/'xltabular' table to normal table.")
487                 document.body[k] = document.body[k].replace(' varwidth="true"', '')
488         i = i + 1
489
490
491 def revert_vcolumns(document):
492     " Revert standard columns with line breaks etc. "
493     i = 0
494     needvarwidth = False
495     needarray = False
496     try:
497         while True:
498             i = find_token(document.body, "\\begin_inset Tabular", i)
499             if i == -1:
500                 return
501             j = find_end_of_inset(document.body, i)
502             if j == -1:
503                 document.warning("Malformed LyX document: Could not find end of tabular.")
504                 i += 1
505                 continue
506
507             # Collect necessary column information
508             m = i + 1
509             nrows = int(document.body[i+1].split('"')[3])
510             ncols = int(document.body[i+1].split('"')[5])
511             col_info = []
512             for k in range(ncols):
513                 m = find_token(document.body, "<column", m)
514                 width = get_option_value(document.body[m], 'width')
515                 varwidth = get_option_value(document.body[m], 'varwidth')
516                 alignment = get_option_value(document.body[m], 'alignment')
517                 special = get_option_value(document.body[m], 'special')
518                 col_info.append([width, varwidth, alignment, special, m])
519
520             # Now parse cells
521             m = i + 1
522             lines = []
523             for row in range(nrows):
524                 for col in range(ncols):
525                     m = find_token(document.body, "<cell", m)
526                     multicolumn = get_option_value(document.body[m], 'multicolumn')
527                     multirow = get_option_value(document.body[m], 'multirow')
528                     width = get_option_value(document.body[m], 'width')
529                     rotate = get_option_value(document.body[m], 'rotate')
530                     # Check for: linebreaks, multipars, non-standard environments
531                     begcell = m
532                     endcell = find_token(document.body, "</cell>", begcell)
533                     vcand = False
534                     if find_token(document.body, "\\begin_inset Newline", begcell, endcell) != -1:
535                         vcand = True
536                     elif count_pars_in_inset(document.body, begcell + 2) > 1:
537                         vcand = True
538                     elif get_value(document.body, "\\begin_layout", begcell) != "Plain Layout":
539                         vcand = True
540                     if vcand and rotate == "" and ((multicolumn == "" and multirow == "") or width == ""):
541                         if col_info[col][0] == "" and col_info[col][1] == "" and col_info[col][3] == "":
542                             needvarwidth = True
543                             alignment = col_info[col][2]
544                             col_line = col_info[col][4]
545                             vval = ""
546                             if alignment == "center":
547                                 vval = ">{\\centering}"
548                             elif  alignment == "left":
549                                 vval = ">{\\raggedright}"
550                             elif alignment == "right":
551                                 vval = ">{\\raggedleft}"
552                             if vval != "":
553                                 needarray = True
554                             vval += "V{\\linewidth}"
555                 
556                             document.body[col_line] = document.body[col_line][:-1] + " special=\"" + vval + "\">"
557                             # ERT newlines and linebreaks (since LyX < 2.4 automatically inserts parboxes
558                             # with newlines, and we do not want that)
559                             while True:
560                                 endcell = find_token(document.body, "</cell>", begcell)
561                                 linebreak = False
562                                 nl = find_token(document.body, "\\begin_inset Newline newline", begcell, endcell)
563                                 if nl == -1:
564                                     nl = find_token(document.body, "\\begin_inset Newline linebreak", begcell, endcell)
565                                     if nl == -1:
566                                          break
567                                     linebreak = True
568                                 nle = find_end_of_inset(document.body, nl)
569                                 del(document.body[nle:nle+1])
570                                 if linebreak:
571                                     document.body[nl:nl+1] = put_cmd_in_ert("\\linebreak{}")
572                                 else:
573                                     document.body[nl:nl+1] = put_cmd_in_ert("\\\\")
574                     m += 1
575
576             i = j + 1
577
578     finally:
579         if needarray == True:
580             add_to_preamble(document, ["\\usepackage{array}"])
581         if needvarwidth == True:
582             add_to_preamble(document, ["\\usepackage{varwidth}"])
583
584
585 def revert_bibencoding(document):
586     " Revert bibliography encoding "
587
588     # Get cite engine
589     engine = "basic"
590     i = find_token(document.header, "\\cite_engine", 0)
591     if i == -1:
592         document.warning("Malformed document! Missing \\cite_engine")
593     else:
594         engine = get_value(document.header, "\\cite_engine", i)
595
596     # Check if biblatex
597     biblatex = False
598     if engine in ["biblatex", "biblatex-natbib"]:
599         biblatex = True
600
601     # Map lyx to latex encoding names 
602     encodings = {
603         "utf8" : "utf8",
604         "utf8x" : "utf8x",
605         "armscii8" : "armscii8",
606         "iso8859-1" : "latin1",
607         "iso8859-2" : "latin2",
608         "iso8859-3" : "latin3",
609         "iso8859-4" : "latin4",
610         "iso8859-5" : "iso88595",
611         "iso8859-6" : "8859-6",
612         "iso8859-7" : "iso-8859-7",
613         "iso8859-8" : "8859-8",
614         "iso8859-9" : "latin5",
615         "iso8859-13" : "latin7",
616         "iso8859-15" : "latin9",
617         "iso8859-16" : "latin10",
618         "applemac" : "applemac",
619         "cp437" : "cp437",
620         "cp437de" : "cp437de",
621         "cp850" : "cp850",
622         "cp852" : "cp852",
623         "cp855" : "cp855",
624         "cp858" : "cp858",
625         "cp862" : "cp862",
626         "cp865" : "cp865",
627         "cp866" : "cp866",
628         "cp1250" : "cp1250",
629         "cp1251" : "cp1251",
630         "cp1252" : "cp1252",
631         "cp1255" : "cp1255",
632         "cp1256" : "cp1256",
633         "cp1257" : "cp1257",
634         "koi8-r" : "koi8-r",
635         "koi8-u" : "koi8-u",
636         "pt154" : "pt154",
637         "utf8-platex" : "utf8",
638         "ascii" : "ascii"
639     }
640
641     i = 0
642     bibresources = []
643     while (True):
644         i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
645         if i == -1:
646             break
647         j = find_end_of_inset(document.body, i)
648         if j == -1:
649             document.warning("Can't find end of bibtex inset at line %d!!" %(i))
650             i += 1
651             continue
652         encoding = get_quoted_value(document.body, "encoding", i, j)
653         if not encoding:
654             i += 1
655             continue
656         # remove encoding line
657         k = find_token(document.body, "encoding", i, j)
658         if k != -1:
659             del document.body[k]
660         # Re-find inset end line
661         j = find_end_of_inset(document.body, i)
662         if biblatex:
663             biblio_options = ""
664             h = find_token(document.header, "\\biblio_options", 0)
665             if h != -1:
666                 biblio_options = get_value(document.header, "\\biblio_options", h)
667                 if not "bibencoding" in biblio_options:
668                      document.header[h] += ",bibencoding=%s" % encodings[encoding]
669             else:
670                 bs = find_token(document.header, "\\biblatex_bibstyle", 0)
671                 if bs == -1:
672                     # this should not happen
673                     document.warning("Malformed LyX document! No \\biblatex_bibstyle header found!")
674                 else:
675                     document.header[bs-1 : bs-1] = ["\\biblio_options bibencoding=" + encodings[encoding]]
676         else:
677             document.body[j+1:j+1] = put_cmd_in_ert("\\egroup")
678             document.body[i:i] = put_cmd_in_ert("\\bgroup\\inputencoding{" + encodings[encoding] + "}")
679
680         i = j + 1
681
682
683
684 def convert_vcsinfo(document):
685     " Separate vcs Info inset from buffer Info inset. "
686
687     types = {
688         "vcs-revision" : "revision",
689         "vcs-tree-revision" : "tree-revision",
690         "vcs-author" : "author",
691         "vcs-time" : "time",
692         "vcs-date" : "date"
693     }
694     i = 0
695     while True:
696         i = find_token(document.body, "\\begin_inset Info", i)
697         if i == -1:
698             return
699         j = find_end_of_inset(document.body, i + 1)
700         if j == -1:
701             document.warning("Malformed LyX document: Could not find end of Info inset.")
702             i = i + 1
703             continue
704         tp = find_token(document.body, 'type', i, j)
705         tpv = get_quoted_value(document.body, "type", tp)
706         if tpv != "buffer":
707             i = i + 1
708             continue
709         arg = find_token(document.body, 'arg', i, j)
710         argv = get_quoted_value(document.body, "arg", arg)
711         if argv not in list(types.keys()):
712             i = i + 1
713             continue
714         document.body[tp] = "type \"vcs\""
715         document.body[arg] = "arg \"" + types[argv] + "\""
716         i = i + 1
717
718
719 def revert_vcsinfo(document):
720     " Merge vcs Info inset to buffer Info inset. "
721
722     args = ["revision", "tree-revision", "author", "time", "date" ]
723     i = 0
724     while True:
725         i = find_token(document.body, "\\begin_inset Info", i)
726         if i == -1:
727             return
728         j = find_end_of_inset(document.body, i + 1)
729         if j == -1:
730             document.warning("Malformed LyX document: Could not find end of Info inset.")
731             i = i + 1
732             continue
733         tp = find_token(document.body, 'type', i, j)
734         tpv = get_quoted_value(document.body, "type", tp)
735         if tpv != "vcs":
736             i = i + 1
737             continue
738         arg = find_token(document.body, 'arg', i, j)
739         argv = get_quoted_value(document.body, "arg", arg)
740         if argv not in args:
741             document.warning("Malformed Info inset. Invalid vcs arg.")
742             i = i + 1
743             continue
744         document.body[tp] = "type \"buffer\""
745         document.body[arg] = "arg \"vcs-" + argv + "\""
746         i = i + 1
747
748
749 def revert_dateinfo(document):
750     " Revert date info insets to static text. "
751
752 # FIXME This currently only considers the main language and uses the system locale
753 # Ideally, it should honor context languages and switch the locale accordingly.
754
755     # The date formats for each language using strftime syntax:
756     # long, short, loclong, locmedium, locshort
757     dateformats = {
758         "afrikaans" : ["%A, %d %B %Y", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y/%m/%d"],
759         "albanian" : ["%A, %d %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
760         "american" : ["%A, %B %d, %Y", "%m/%d/%y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
761         "amharic" : ["%A ፣%d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
762         "ancientgreek" : ["%A, %d %B %Y", "%d %b %Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
763         "arabic_arabi" : ["%A، %d %B، %Y", "%d‏/%m‏/%Y", "%d %B، %Y", "%d/%m/%Y", "%d/%m/%Y"],
764         "arabic_arabtex" : ["%A، %d %B، %Y", "%d‏/%m‏/%Y", "%d %B، %Y", "%d/%m/%Y", "%d/%m/%Y"],
765         "armenian" : ["%Y թ. %B %d, %A", "%d.%m.%y", "%d %B، %Y", "%d %b، %Y", "%d/%m/%Y"],
766         "asturian" : ["%A, %d %B de %Y", "%d/%m/%y", "%d de %B de %Y", "%d %b %Y", "%d/%m/%Y"],
767         "australian" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
768         "austrian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
769         "bahasa" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
770         "bahasam" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
771         "basque" : ["%Y(e)ko %B %d, %A", "%y/%m/%d", "%Y %B %d", "%Y %b %d", "%Y/%m/%d"],
772         "belarusian" : ["%A, %d %B %Y г.", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
773         "bosnian" : ["%A, %d. %B %Y.", "%d.%m.%y.", "%d. %B %Y", "%d. %b %Y", "%Y-%m-%d"],
774         "brazilian" : ["%A, %d de %B de %Y", "%d/%m/%Y", "%d de %B de %Y", "%d de %b de %Y", "%d/%m/%Y"],
775         "breton" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
776         "british" : ["%A, %d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
777         "bulgarian" : ["%A, %d %B %Y г.", "%d.%m.%y г.", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
778         "canadian" : ["%A, %B %d, %Y", "%Y-%m-%d", "%B %d, %Y", "%d %b %Y", "%Y-%m-%d"],
779         "canadien" : ["%A %d %B %Y", "%y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
780         "catalan" : ["%A, %d %B de %Y", "%d/%m/%y", "%d / %B / %Y", "%d / %b / %Y", "%d/%m/%Y"],
781         "chinese-simplified" : ["%Y年%m月%d日%A", "%Y/%m/%d", "%Y年%m月%d日", "%Y-%m-%d", "%y-%m-%d"],
782         "chinese-traditional" : ["%Y年%m月%d日 %A", "%Y/%m/%d", "%Y年%m月%d日", "%Y年%m月%d日", "%y年%m月%d日"],
783         "coptic" : ["%A, %d %B %Y", "%d %b %Y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
784         "croatian" : ["%A, %d. %B %Y.", "%d. %m. %Y.", "%d. %B %Y.", "%d. %b. %Y.", "%d.%m.%Y."],
785         "czech" : ["%A %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b. %Y", "%d.%m.%Y"],
786         "danish" : ["%A den %d. %B %Y", "%d/%m/%Y", "%d. %B %Y", "%d. %b %Y", "%d/%m/%Y"],
787         "divehi" : ["%Y %B %d, %A", "%Y-%m-%d", "%Y %B %d", "%Y %b %d", "%d/%m/%Y"],
788         "dutch" : ["%A %d %B %Y", "%d-%m-%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
789         "english" : ["%A, %B %d, %Y", "%m/%d/%y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
790         "esperanto" : ["%A, %d %B %Y", "%d %b %Y", "la %d de %B %Y", "la %d de %b %Y", "%m/%d/%Y"],
791         "estonian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
792         "farsi" : ["%A %d %B %Y", "%Y/%m/%d", "%d %B %Y", "%d %b %Y", "%Y/%m/%d"],
793         "finnish" : ["%A %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
794         "french" : ["%A %d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
795         "friulan" : ["%A %d di %B dal %Y", "%d/%m/%y", "%d di %B dal %Y", "%d di %b dal %Y", "%d/%m/%Y"],
796         "galician" : ["%A, %d de %B de %Y", "%d/%m/%y", "%d de %B de %Y", "%d de %b de %Y", "%d/%m/%Y"],
797         "georgian" : ["%A, %d %B, %Y", "%d.%m.%y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
798         "german" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
799         "german-ch" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
800         "german-ch-old" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
801         "greek" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
802         "hebrew" : ["%A, %d ב%B %Y", "%d.%m.%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
803         "hindi" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
804         "icelandic" : ["%A, %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
805         "interlingua" : ["%Y %B %d, %A", "%Y-%m-%d", "le %d de %B %Y", "le %d de %b %Y", "%Y-%m-%d"],
806         "irish" : ["%A %d %B %Y", "%d/%m/%Y", "%d. %B %Y", "%d. %b %Y", "%d/%m/%Y"],
807         "italian" : ["%A %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d/%b/%Y", "%d/%m/%Y"],
808         "japanese" : ["%Y年%m月%d日%A", "%Y/%m/%d", "%Y年%m月%d日", "%Y/%m/%d", "%y/%m/%d"],
809         "japanese-cjk" : ["%Y年%m月%d日%A", "%Y/%m/%d", "%Y年%m月%d日", "%Y/%m/%d", "%y/%m/%d"],
810         "kannada" : ["%A, %B %d, %Y", "%d/%m/%y", "%d %B %Y", "%d %B %Y", "%d-%m-%Y"],
811         "kazakh" : ["%Y ж. %d %B, %A", "%d.%m.%y", "%d %B %Y", "%d %B %Y", "%Y-%d-%m"],
812         "khmer" : ["%A %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %B %Y", "%d/%m/%Y"],
813         "korean" : ["%Y년 %m월 %d일 %A", "%y. %m. %d.", "%Y년 %m월 %d일", "%Y. %m. %d.", "%y. %m. %d."],
814         "kurmanji" : ["%A, %d %B %Y", "%d %b %Y", "%d. %B %Y", "%d. %m. %Y", "%Y-%m-%d"],
815         "lao" : ["%A ທີ %d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %B %Y", "%d/%m/%Y"],
816         "latin" : ["%A, %d %B %Y", "%d %b %Y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
817         "latvian" : ["%A, %Y. gada %d. %B", "%d.%m.%y", "%Y. gada %d. %B", "%Y. gada %d. %b", "%d.%m.%Y"],
818         "lithuanian" : ["%Y m. %B %d d., %A", "%Y-%m-%d", "%Y m. %B %d d.", "%Y m. %B %d d.", "%Y-%m-%d"],
819         "lowersorbian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
820         "macedonian" : ["%A, %d %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
821         "magyar" : ["%Y. %B %d., %A", "%Y. %m. %d.", "%Y. %B %d.", "%Y. %b %d.", "%Y.%m.%d."],
822         "marathi" : ["%A, %d %B, %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
823         "mongolian" : ["%A, %Y оны %m сарын %d", "%Y-%m-%d", "%Y оны %m сарын %d", "%d-%m-%Y", "%d-%m-%Y"],
824         "naustrian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
825         "newzealand" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
826         "ngerman" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
827         "norsk" : ["%A %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
828         "nynorsk" : ["%A %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
829         "occitan" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
830         "piedmontese" : ["%A, %d %B %Y", "%d %b %Y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
831         "polish" : ["%A, %d %B %Y", "%d.%m.%Y", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
832         "polutonikogreek" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
833         "portuguese" : ["%A, %d de %B de %Y", "%d/%m/%y", "%d de %B de %Y", "%d de %b de %Y", "%Y/%m/%d"],
834         "romanian" : ["%A, %d %B %Y", "%d.%m.%Y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
835         "romansh" : ["%A, ils %d da %B %Y", "%d-%m-%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
836         "russian" : ["%A, %d %B %Y г.", "%d.%m.%Y", "%d %B %Y г.", "%d %b %Y г.", "%d.%m.%Y"],
837         "samin" : ["%Y %B %d, %A", "%Y-%m-%d", "%B %d. b. %Y", "%b %d. b. %Y", "%d.%m.%Y"],
838         "sanskrit" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
839         "scottish" : ["%A, %dmh %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
840         "serbian" : ["%A, %d. %B %Y.", "%d.%m.%y.", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
841         "serbian-latin" : ["%A, %d. %B %Y.", "%d.%m.%y.", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
842         "slovak" : ["%A, %d. %B %Y", "%d. %m. %Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
843         "slovene" : ["%A, %d. %B %Y", "%d. %m. %y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
844         "spanish" : ["%A, %d de %B de %Y", "%d/%m/%y", "%d de %B %de %Y", "%d %b %Y", "%d/%m/%Y"],
845         "spanish-mexico" : ["%A, %d de %B %de %Y", "%d/%m/%y", "%d de %B de %Y", "%d %b %Y", "%d/%m/%Y"],
846         "swedish" : ["%A %d %B %Y", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
847         "syriac" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
848         "tamil" : ["%A, %d %B, %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
849         "telugu" : ["%d, %B %Y, %A", "%d-%m-%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
850         "thai" : ["%Aที่ %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
851         "tibetan" : ["%Y %Bའི་ཚེས་%d, %A", "%Y-%m-%d", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
852         "turkish" : ["%d %B %Y %A", "%d.%m.%Y", "%d %B %Y", "%d.%b.%Y", "%d.%m.%Y"],
853         "turkmen" : ["%d %B %Y %A", "%d.%m.%Y", "%Y ý. %B %d", "%d.%m.%Y ý.", "%d.%m.%y ý."],
854         "ukrainian" : ["%A, %d %B %Y р.", "%d.%m.%y", "%d %B %Y", "%d %m %Y", "%d.%m.%Y"],
855         "uppersorbian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
856         "urdu" : ["%A، %d %B، %Y", "%d/%m/%y", "%d %B, %Y", "%d %b %Y", "%d/%m/%Y"],
857         "vietnamese" : ["%A, %d %B, %Y", "%d/%m/%Y", "%d tháng %B %Y", "%d-%m-%Y", "%d/%m/%Y"],
858         "welsh" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
859     }
860
861     types = ["date", "fixdate", "moddate" ]
862     i = 0
863     i = find_token(document.header, "\\language", 0)
864     if i == -1:
865         # this should not happen
866         document.warning("Malformed LyX document! No \\language header found!")
867         return
868     lang = get_value(document.header, "\\language", i)
869
870     i = 0
871     while True:
872         i = find_token(document.body, "\\begin_inset Info", i)
873         if i == -1:
874             return
875         j = find_end_of_inset(document.body, i + 1)
876         if j == -1:
877             document.warning("Malformed LyX document: Could not find end of Info inset.")
878             i = i + 1
879             continue
880         tp = find_token(document.body, 'type', i, j)
881         tpv = get_quoted_value(document.body, "type", tp)
882         if tpv not in types:
883             i = i + 1
884             continue
885         arg = find_token(document.body, 'arg', i, j)
886         argv = get_quoted_value(document.body, "arg", arg)
887         isodate = ""
888         dte = date.today()
889         if tpv == "fixdate":
890             datecomps = argv.split('@')
891             if len(datecomps) > 1:
892                 argv = datecomps[0]
893                 isodate = datecomps[1]
894                 m = re.search('(\d\d\d\d)-(\d\d)-(\d\d)', isodate)
895                 if m:
896                     dte = date(int(m.group(1)), int(m.group(2)), int(m.group(3)))
897 # FIXME if we had the path to the original document (not the one in the tmp dir),
898 #        we could use the mtime.
899 #        elif tpv == "moddate":
900 #            dte = date.fromtimestamp(os.path.getmtime(document.dir))
901         result = ""
902         if argv == "ISO":
903             result = dte.isodate()
904         elif argv == "long":
905             result = dte.strftime(dateformats[lang][0])
906         elif argv == "short":
907             result = dte.strftime(dateformats[lang][1])
908         elif argv == "loclong":
909             result = dte.strftime(dateformats[lang][2])
910         elif argv == "locmedium":
911             result = dte.strftime(dateformats[lang][3])
912         elif argv == "locshort":
913             result = dte.strftime(dateformats[lang][4])
914         else:
915             fmt = argv.replace("MMMM", "%b").replace("MMM", "%b").replace("MM", "%m").replace("M", "%m")
916             fmt = fmt.replace("yyyy", "%Y").replace("yy", "%y")
917             fmt = fmt.replace("dddd", "%A").replace("ddd", "%a").replace("dd", "%d")
918             fmt = re.sub('[^\'%]d', '%d', fmt)
919             fmt = fmt.replace("'", "")
920             result = dte.strftime(fmt)
921         document.body[i : j+1] = result
922         i = i + 1
923
924
925 def revert_timeinfo(document):
926     " Revert time info insets to static text. "
927
928 # FIXME This currently only considers the main language and uses the system locale
929 # Ideally, it should honor context languages and switch the locale accordingly.
930 # Also, the time object is "naive", i.e., it does not know of timezones (%Z will
931 # be empty).
932
933     # The time formats for each language using strftime syntax:
934     # long, short
935     timeformats = {
936         "afrikaans" : ["%H:%M:%S %Z", "%H:%M"],
937         "albanian" : ["%I:%M:%S %p, %Z", "%I:%M %p"],
938         "american" : ["%I:%M:%S %p %Z", "%I:%M %p"],
939         "amharic" : ["%I:%M:%S %p %Z", "%I:%M %p"],
940         "ancientgreek" : ["%H:%M:%S %Z", "%H:%M:%S"],
941         "arabic_arabi" : ["%I:%M:%S %p %Z", "%I:%M %p"],
942         "arabic_arabtex" : ["%I:%M:%S %p %Z", "%I:%M %p"],
943         "armenian" : ["%H:%M:%S %Z", "%H:%M"],
944         "asturian" : ["%H:%M:%S %Z", "%H:%M"],
945         "australian" : ["%I:%M:%S %p %Z", "%I:%M %p"],
946         "austrian" : ["%H:%M:%S %Z", "%H:%M"],
947         "bahasa" : ["%H.%M.%S %Z", "%H.%M"],
948         "bahasam" : ["%I:%M:%S %p %Z", "%I:%M %p"],
949         "basque" : ["%H:%M:%S (%Z)", "%H:%M"],
950         "belarusian" : ["%H:%M:%S, %Z", "%H:%M"],
951         "bosnian" : ["%H:%M:%S %Z", "%H:%M"],
952         "brazilian" : ["%H:%M:%S %Z", "%H:%M"],
953         "breton" : ["%H:%M:%S %Z", "%H:%M"],
954         "british" : ["%H:%M:%S %Z", "%H:%M"],
955         "bulgarian" : ["%H:%M:%S %Z", "%H:%M"],
956         "canadian" : ["%I:%M:%S %p %Z", "%I:%M %p"],
957         "canadien" : ["%H:%M:%S %Z", "%H h %M"],
958         "catalan" : ["%H:%M:%S %Z", "%H:%M"],
959         "chinese-simplified" : ["%Z %p%I:%M:%S", "%p%I:%M"],
960         "chinese-traditional" : ["%p%I:%M:%S [%Z]", "%p%I:%M"],
961         "coptic" : ["%H:%M:%S %Z", "%H:%M:%S"],
962         "croatian" : ["%H:%M:%S (%Z)", "%H:%M"],
963         "czech" : ["%H:%M:%S %Z", "%H:%M"],
964         "danish" : ["%H.%M.%S %Z", "%H.%M"],
965         "divehi" : ["%H:%M:%S %Z", "%H:%M"],
966         "dutch" : ["%H:%M:%S %Z", "%H:%M"],
967         "english" : ["%I:%M:%S %p %Z", "%I:%M %p"],
968         "esperanto" : ["%H:%M:%S %Z", "%H:%M:%S"],
969         "estonian" : ["%H:%M:%S %Z", "%H:%M"],
970         "farsi" : ["%H:%M:%S (%Z)", "%H:%M"],
971         "finnish" : ["%H.%M.%S %Z", "%H.%M"],
972         "french" : ["%H:%M:%S %Z", "%H:%M"],
973         "friulan" : ["%H:%M:%S %Z", "%H:%M"],
974         "galician" : ["%H:%M:%S %Z", "%H:%M"],
975         "georgian" : ["%H:%M:%S %Z", "%H:%M"],
976         "german" : ["%H:%M:%S %Z", "%H:%M"],
977         "german-ch" : ["%H:%M:%S %Z", "%H:%M"],
978         "german-ch-old" : ["%H:%M:%S %Z", "%H:%M"],
979         "greek" : ["%I:%M:%S %p %Z", "%I:%M %p"],
980         "hebrew" : ["%H:%M:%S %Z", "%H:%M"],
981         "hindi" : ["%I:%M:%S %p %Z", "%I:%M %p"],
982         "icelandic" : ["%H:%M:%S %Z", "%H:%M"],
983         "interlingua" : ["%H:%M:%S %Z", "%H:%M"],
984         "irish" : ["%H:%M:%S %Z", "%H:%M"],
985         "italian" : ["%H:%M:%S %Z", "%H:%M"],
986         "japanese" : ["%H時%M分%S秒 %Z", "%H:%M"],
987         "japanese-cjk" : ["%H時%M分%S秒 %Z", "%H:%M"],
988         "kannada" : ["%I:%M:%S %p %Z", "%I:%M %p"],
989         "kazakh" : ["%H:%M:%S %Z", "%H:%M"],
990         "khmer" : ["%I:%M:%S %p %Z", "%I:%M %p"],
991         "korean" : ["%p %I시%M분 %S초 %Z", "%p %I:%M"],
992         "kurmanji" : ["%H:%M:%S %Z", "%H:%M:%S"],
993         "lao" : ["%H ໂມງ%M ນາທີ  %S ວິນາທີ %Z", "%H:%M"],
994         "latin" : ["%H:%M:%S %Z", "%H:%M:%S"],
995         "latvian" : ["%H:%M:%S %Z", "%H:%M"],
996         "lithuanian" : ["%H:%M:%S %Z", "%H:%M"],
997         "lowersorbian" : ["%H:%M:%S %Z", "%H:%M"],
998         "macedonian" : ["%H:%M:%S %Z", "%H:%M"],
999         "magyar" : ["%H:%M:%S %Z", "%H:%M"],
1000         "marathi" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1001         "mongolian" : ["%H:%M:%S %Z", "%H:%M"],
1002         "naustrian" : ["%H:%M:%S %Z", "%H:%M"],
1003         "newzealand" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1004         "ngerman" : ["%H:%M:%S %Z", "%H:%M"],
1005         "norsk" : ["%H:%M:%S %Z", "%H:%M"],
1006         "nynorsk" : ["kl. %H:%M:%S %Z", "%H:%M"],
1007         "occitan" : ["%H:%M:%S %Z", "%H:%M"],
1008         "piedmontese" : ["%H:%M:%S %Z", "%H:%M:%S"],
1009         "polish" : ["%H:%M:%S %Z", "%H:%M"],
1010         "polutonikogreek" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1011         "portuguese" : ["%H:%M:%S %Z", "%H:%M"],
1012         "romanian" : ["%H:%M:%S %Z", "%H:%M"],
1013         "romansh" : ["%H:%M:%S %Z", "%H:%M"],
1014         "russian" : ["%H:%M:%S %Z", "%H:%M"],
1015         "samin" : ["%H:%M:%S %Z", "%H:%M"],
1016         "sanskrit" : ["%H:%M:%S %Z", "%H:%M"],
1017         "scottish" : ["%H:%M:%S %Z", "%H:%M"],
1018         "serbian" : ["%H:%M:%S %Z", "%H:%M"],
1019         "serbian-latin" : ["%H:%M:%S %Z", "%H:%M"],
1020         "slovak" : ["%H:%M:%S %Z", "%H:%M"],
1021         "slovene" : ["%H:%M:%S %Z", "%H:%M"],
1022         "spanish" : ["%H:%M:%S (%Z)", "%H:%M"],
1023         "spanish-mexico" : ["%H:%M:%S %Z", "%H:%M"],
1024         "swedish" : ["kl. %H:%M:%S %Z", "%H:%M"],
1025         "syriac" : ["%H:%M:%S %Z", "%H:%M"],
1026         "tamil" : ["%p %I:%M:%S %Z", "%p %I:%M"],
1027         "telugu" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1028         "thai" : ["%H นาฬิกา %M นาที  %S วินาที %Z", "%H:%M"],
1029         "tibetan" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1030         "turkish" : ["%H:%M:%S %Z", "%H:%M"],
1031         "turkmen" : ["%H:%M:%S %Z", "%H:%M"],
1032         "ukrainian" : ["%H:%M:%S %Z", "%H:%M"],
1033         "uppersorbian" : ["%H:%M:%S %Z", "%H:%M hodź."],
1034         "urdu" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1035         "vietnamese" : ["%H:%M:%S %Z", "%H:%M"],
1036         "welsh" : ["%H:%M:%S %Z", "%H:%M"]
1037     }
1038
1039     types = ["time", "fixtime", "modtime" ]
1040     i = 0
1041     i = find_token(document.header, "\\language", 0)
1042     if i == -1:
1043         # this should not happen
1044         document.warning("Malformed LyX document! No \\language header found!")
1045         return
1046     lang = get_value(document.header, "\\language", i)
1047
1048     i = 0
1049     while True:
1050         i = find_token(document.body, "\\begin_inset Info", i)
1051         if i == -1:
1052             return
1053         j = find_end_of_inset(document.body, i + 1)
1054         if j == -1:
1055             document.warning("Malformed LyX document: Could not find end of Info inset.")
1056             i = i + 1
1057             continue
1058         tp = find_token(document.body, 'type', i, j)
1059         tpv = get_quoted_value(document.body, "type", tp)
1060         if tpv not in types:
1061             i = i + 1
1062             continue
1063         arg = find_token(document.body, 'arg', i, j)
1064         argv = get_quoted_value(document.body, "arg", arg)
1065         isotime = ""
1066         dtme = datetime.now()
1067         tme = dtme.time()
1068         if tpv == "fixtime":
1069             timecomps = argv.split('@')
1070             if len(timecomps) > 1:
1071                 argv = timecomps[0]
1072                 isotime = timecomps[1]
1073                 m = re.search('(\d\d):(\d\d):(\d\d)', isotime)
1074                 if m:
1075                     tme = time(int(m.group(1)), int(m.group(2)), int(m.group(3)))
1076                 else:
1077                     m = re.search('(\d\d):(\d\d)', isotime)
1078                     if m:
1079                         tme = time(int(m.group(1)), int(m.group(2)))
1080 # FIXME if we had the path to the original document (not the one in the tmp dir),
1081 #        we could use the mtime.
1082 #        elif tpv == "moddate":
1083 #            dte = date.fromtimestamp(os.path.getmtime(document.dir))
1084         result = ""
1085         if argv == "ISO":
1086             result = tme.isoformat()
1087         elif argv == "long":
1088             result = tme.strftime(timeformats[lang][0])
1089         elif argv == "short":
1090             result = tme.strftime(timeformats[lang][1])
1091         else:
1092             fmt = argv.replace("HH", "%H").replace("H", "%H").replace("hh", "%I").replace("h", "%I")
1093             fmt = fmt.replace("mm", "%M").replace("m", "%M").replace("ss", "%S").replace("s", "%S")
1094             fmt = fmt.replace("zzz", "%f").replace("z", "%f").replace("t", "%Z")
1095             fmt = fmt.replace("AP", "%p").replace("ap", "%p").replace("A", "%p").replace("a", "%p")
1096             fmt = fmt.replace("'", "")
1097             result = dte.strftime(fmt)
1098         document.body[i : j+1] = result
1099         i = i + 1
1100
1101
1102 def revert_namenoextinfo(document):
1103     " Merge buffer Info inset type name-noext to name. "
1104
1105     i = 0
1106     while True:
1107         i = find_token(document.body, "\\begin_inset Info", i)
1108         if i == -1:
1109             return
1110         j = find_end_of_inset(document.body, i + 1)
1111         if j == -1:
1112             document.warning("Malformed LyX document: Could not find end of Info inset.")
1113             i = i + 1
1114             continue
1115         tp = find_token(document.body, 'type', i, j)
1116         tpv = get_quoted_value(document.body, "type", tp)
1117         if tpv != "buffer":
1118             i = i + 1
1119             continue
1120         arg = find_token(document.body, 'arg', i, j)
1121         argv = get_quoted_value(document.body, "arg", arg)
1122         if argv != "name-noext":
1123             i = i + 1
1124             continue
1125         document.body[arg] = "arg \"name\""
1126         i = i + 1
1127
1128
1129 ##
1130 # Conversion hub
1131 #
1132
1133 supported_versions = ["2.4.0", "2.4"]
1134 convert = [
1135            [545, [convert_lst_literalparam]],
1136            [546, []],
1137            [547, []],
1138            [548, []],
1139            [549, []],
1140            [550, [convert_fontenc]],
1141            [551, []],
1142            [552, []],
1143            [553, []],
1144            [554, []],
1145            [555, []],
1146            [556, []],
1147            [557, [convert_vcsinfo]],
1148            [558, [removeFrontMatterStyles]],
1149            [559, []],
1150            [560, []],
1151            [561, [convert_dejavu]]
1152           ]
1153
1154 revert =  [
1155            [560, [revert_dejavu]],
1156            [559, [revert_timeinfo, revert_namenoextinfo]],
1157            [558, [revert_dateinfo]],
1158            [557, [addFrontMatterStyles]],
1159            [556, [revert_vcsinfo]],
1160            [555, [revert_bibencoding]],
1161            [554, [revert_vcolumns]],
1162            [553, [revert_stretchcolumn]],
1163            [552, [revert_tuftecite]],
1164            [551, [revert_floatpclass, revert_floatalignment]],
1165            [550, [revert_nospellcheck]],
1166            [549, [revert_fontenc]],
1167            [548, []],# dummy format change
1168            [547, [revert_lscape]],
1169            [546, [revert_xcharter]],
1170            [545, [revert_paratype]],
1171            [544, [revert_lst_literalparam]]
1172           ]
1173
1174
1175 if __name__ == "__main__":
1176     pass