]> git.lyx.org Git - lyx.git/blob - lib/lyx2lyx/lyx_2_4.py
ctest update.
[lyx.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_containing_layout,
31                           get_option_value, get_value, get_quoted_value)
32 #    del_token, del_value, del_complete_lines,
33 #    find_complete_lines, find_end_of,
34 #    find_re, find_substring, find_token_backwards,
35 #    get_containing_inset,
36 #    is_in_inset, set_bool_value
37 #    find_tokens, find_token_exact, check_token
38
39 from lyx2lyx_tools import (put_cmd_in_ert, add_to_preamble, revert_language, revert_flex_inset)
40 #  revert_font_attrs, insert_to_preamble, latex_length
41 #  get_ert, lyx2latex, lyx2verbatim, length_in_bp, convert_info_insets
42 #  revert_flex_inset, hex2ratio, str2bool
43
44 ####################################################################
45 # Private helper functions
46
47 def add_preamble_fonts(document, fontmap):
48     " Add collected font-packages with their option to user-preamble"
49
50     for pkg in fontmap:
51         if len(fontmap[pkg]) > 0:
52             xoption = "[" + ",".join(fontmap[pkg]) + "]"
53         else:
54             xoption = ""
55         preamble = "\\usepackage" + xoption + "{%s}" % pkg
56         add_to_preamble(document, [preamble])
57
58
59 def createkey(pkg, options):
60     options.sort()
61     return pkg + ':' + "-".join(options)
62
63 class fontinfo:
64     def __init__(self):
65         self.fontname = None    # key into font2pkgmap
66         self.fonttype = None    # roman,sans,typewriter,math
67         self.scaletype = None   # None,sf,tt
68         self.scaleopt = None    # None, 'scaled', 'scale'
69         self.scaleval = 1
70         self.package = None
71         self.options = []
72         self.pkgkey = None      # key into pkg2fontmap
73
74     def addkey(self):
75         self.pkgkey = createkey(self.package, self.options)
76
77 class fontmapping:
78     def __init__(self):
79         self.font2pkgmap = dict()
80         self.pkg2fontmap = dict()
81         self.pkginmap = dict()  # defines, if a map for package exists
82
83     def expandFontMapping(self, font_list, font_type, scale_type, pkg, scaleopt = None):
84         " Expand fontinfo mapping"
85         #
86         # fontlist:    list of fontnames, each element
87         #              may contain a ','-separated list of needed options
88         #              like e.g. 'IBMPlexSansCondensed,condensed'
89         # font_type:   one of 'roman', 'sans', 'typewriter', 'math'
90         # scale_type:  one of None, 'sf', 'tt'
91         # pkg:         package defining the font. Defaults to fontname if None
92         # scaleopt:    one of None, 'scale', 'scaled', or some other string
93         #              to be used in scale option (e.g. scaled=0.7)
94         for fl in font_list:
95             fe = fontinfo()
96             fe.fonttype = font_type
97             fe.scaletype = scale_type
98             flt = fl.split(",")
99             font_name = flt[0]
100             fe.fontname = font_name
101             fe.options = flt[1:]
102             fe.scaleopt = scaleopt
103             if pkg == None:
104                 fe.package = font_name
105             else:
106                 fe.package = pkg
107             fe.addkey()
108             self.font2pkgmap[font_name] = fe
109             if fe.pkgkey in self.pkg2fontmap:
110                 # Repeated the same entry? Check content
111                 if self.pkg2fontmap[fe.pkgkey] != font_name:
112                     document.error("Something is wrong in pkgname+options <-> fontname mapping")
113             self.pkg2fontmap[fe.pkgkey] = font_name
114             self.pkginmap[fe.package] = 1
115
116     def getfontname(self, pkg, options):
117         options.sort()
118         pkgkey = createkey(pkg, options)
119         if not pkgkey in self.pkg2fontmap:
120             return None
121         fontname = self.pkg2fontmap[pkgkey]
122         if not fontname in self.font2pkgmap:
123             document.error("Something is wrong in pkgname+options <-> fontname mapping")
124             return None
125         if pkgkey == self.font2pkgmap[fontname].pkgkey:
126             return fontname
127         return None
128
129 def createFontMapping(fontlist):
130     # Create info for known fonts for the use in
131     #   convert_latexFonts() and
132     #   revert_latexFonts()
133     #
134     # * Would be more handy to parse latexFonts file,
135     #   but the path to this file is unknown
136     # * For now, add DejaVu and IBMPlex only.
137     # * Expand, if desired
138     fm = fontmapping()
139     for font in fontlist:
140         if font == 'DejaVu':
141             fm.expandFontMapping(['DejaVuSerif', 'DejaVuSerifCondensed'], "roman", None, None)
142             fm.expandFontMapping(['DejaVuSans','DejaVuSansCondensed'], "sans", "sf", None, "scaled")
143             fm.expandFontMapping(['DejaVuSansMono'], "typewriter", "tt", None, "scaled")
144         elif font == 'IBM':
145             fm.expandFontMapping(['IBMPlexSerif', 'IBMPlexSerifThin,thin',
146                                   'IBMPlexSerifExtraLight,extralight', 'IBMPlexSerifLight,light',
147                                   'IBMPlexSerifSemibold,semibold'],
148                                  "roman", None, "plex-serif")
149             fm.expandFontMapping(['IBMPlexSans','IBMPlexSansCondensed,condensed',
150                                   'IBMPlexSansThin,thin', 'IBMPlexSansExtraLight,extralight',
151                                   'IBMPlexSansLight,light', 'IBMPlexSansSemibold,semibold'],
152                                  "sans", "sf", "plex-sans", "scale")
153             fm.expandFontMapping(['IBMPlexMono', 'IBMPlexMonoThin,thin',
154                                   'IBMPlexMonoExtraLight,extralight', 'IBMPlexMonoLight,light',
155                                   'IBMPlexMonoSemibold,semibold'],
156                                  "typewriter", "tt", "plex-mono", "scale")
157         elif font == 'Adobe':
158             fm.expandFontMapping(['ADOBESourceSerifPro'], "roman", None, "sourceserifpro")
159             fm.expandFontMapping(['ADOBESourceSansPro'], "sans", "sf", "sourcesanspro", "scaled")
160             fm.expandFontMapping(['ADOBESourceCodePro'], "typewriter", "tt", "sourcecodepro", "scaled")
161     return fm
162
163 def convert_fonts(document, fm):
164     " Handle font definition to LaTeX "
165
166     rpkg = re.compile(r'^\\usepackage(\[([^\]]*)\])?\{([^\}]+)\}')
167     rscaleopt = re.compile(r'^scaled?=(.*)')
168
169     i = 0
170     while i < len(document.preamble):
171         i = find_re(document.preamble, rpkg, i)
172         if i == -1:
173             return
174         mo = rpkg.search(document.preamble[i])
175         if mo == None or mo.group(2) == None:
176             options = []
177         else:
178             options = mo.group(2).replace(' ', '').split(",")
179         pkg = mo.group(3)
180         o = 0
181         oscale = 1
182         while o < len(options):
183             mo = rscaleopt.search(options[o])
184             if mo == None:
185                 o += 1
186                 continue
187             oscale = mo.group(1)
188             del options[o]
189             break
190
191         if not pkg in fm.pkginmap:
192             i += 1
193             continue
194         # determine fontname
195         fn = fm.getfontname(pkg, options)
196         if fn == None:
197             i += 1
198             continue
199         del document.preamble[i]
200         fontinfo = fm.font2pkgmap[fn]
201         if fontinfo.scaletype == None:
202             fontscale = None
203         else:
204             fontscale = "\\font_" + fontinfo.scaletype + "_scale"
205             fontinfo.scaleval = oscale
206
207         if i > 0 and document.preamble[i-1] == "% Added by lyx2lyx":
208             del document.preamble[i-1]
209         if fontscale != None:
210             j = find_token(document.header, fontscale, 0)
211             if j != -1:
212                 val = get_value(document.header, fontscale, j)
213                 vals = val.split()
214                 scale = "100"
215                 if oscale != None:
216                     scale = "%03d" % int(float(oscale) * 100)
217                 document.header[j] = fontscale + " " + scale + " " + vals[1]
218         ft = "\\font_" + fontinfo.fonttype
219         j = find_token(document.header, ft, 0)
220         if j != -1:
221             val = get_value(document.header, ft, j)
222             words = val.split() # ! splits also values like '"DejaVu Sans"'
223             words[0] = '"' + fn + '"'
224             document.header[j] = ft + ' ' + ' '.join(words)
225
226 def revert_fonts(document, fm, fontmap):
227     " Revert native font definition to LaTeX "
228     # fonlist := list of fonts created from the same package
229     # Empty package means that the font-name is the same as the package-name
230     # fontmap (key = package, val += found options) will be filled
231     # and used later in add_preamble_fonts() to be added to user-preamble
232
233     rfontscale = re.compile(r'^\s*(\\font_(roman|sans|typewriter|math))\s+')
234     rscales = re.compile(r'^\s*(\d+)\s+(\d+)')
235     i = 0
236     while i < len(document.header):
237         i = find_re(document.header, rfontscale, i)
238         if (i == -1):
239             break
240         mo = rfontscale.search(document.header[i])
241         if mo == None:
242             i += 1
243             continue
244         ft = mo.group(1)    # 'roman', 'sans', 'typewriter', 'math'
245         val = get_value(document.header, ft, i)
246         words = val.split(' ')     # ! splits also values like '"DejaVu Sans"'
247         font = words[0].strip('"') # TeX font name has no whitespace
248         if not font in fm.font2pkgmap:
249             i += 1
250             continue
251         fontinfo = fm.font2pkgmap[font]
252         val = fontinfo.package
253         if not val in fontmap:
254             fontmap[val] = []
255         words[0] = '"default"'
256         document.header[i] = ft + ' ' + ' '.join(words)
257         if fontinfo.scaleopt != None:
258             xval =  get_value(document.header, "\\font_" + fontinfo.scaletype + "_scale", 0)
259             mo = rscales.search(xval)
260             if mo != None:
261                 xval1 = mo.group(1)
262                 xval2 = mo.group(2)
263                 if xval1 != "100":
264                     # set correct scale option
265                     fontmap[val].extend([fontinfo.scaleopt + "=" + format(float(xval1) / 100, '.2f')])
266         if len(fontinfo.options) > 0:
267             fontmap[val].extend(fontinfo.options)
268         i += 1
269
270 ###############################################################################
271 ###
272 ### Conversion and reversion routines
273 ###
274 ###############################################################################
275
276 def convert_latexFonts(document):
277     " Handle DejaVu and IBMPlex fonts definition to LaTeX "
278
279     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
280         fm = createFontMapping(['DejaVu', 'IBM'])
281         convert_fonts(document, fm)
282
283 def revert_latexFonts(document):
284     " Revert native DejaVu font definition to LaTeX "
285
286     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
287         fontmap = dict()
288         fm = createFontMapping(['DejaVu', 'IBM'])
289         revert_fonts(document, fm, fontmap)
290         add_preamble_fonts(document, fontmap)
291
292 def convert_AdobeFonts(document):
293     " Handle DejaVu and IBMPlex fonts definition to LaTeX "
294
295     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
296         fm = createFontMapping(['Adobe'])
297         convert_fonts(document, fm)
298
299 def revert_AdobeFonts(document):
300     " Revert native DejaVu font definition to LaTeX "
301
302     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
303         fontmap = dict()
304         fm = createFontMapping(['Adobe'])
305         revert_fonts(document, fm, fontmap)
306         add_preamble_fonts(document, fontmap)
307
308 def removeFrontMatterStyles(document):
309     " Remove styles Begin/EndFrontmatter"
310
311     layouts = ['BeginFrontmatter', 'EndFrontmatter']
312     for layout in layouts:
313         i = 0
314         while True:
315             i = find_token(document.body, '\\begin_layout ' + layout, i)
316             if i == -1:
317                 break
318             j = find_end_of_layout(document.body, i)
319             if j == -1:
320                 document.warning("Malformed LyX document: Can't find end of layout at line %d" % i)
321                 i += 1
322                 continue
323             while i > 0 and document.body[i-1].strip() == '':
324                 i -= 1
325             while document.body[j+1].strip() == '':
326                 j = j + 1
327             document.body[i:j+1] = ['']
328
329 def addFrontMatterStyles(document):
330     " Use styles Begin/EndFrontmatter for elsarticle"
331
332     def insertFrontmatter(prefix, line):
333         above = line
334         while above > 0 and document.body[above-1].strip() == '':
335             above -= 1
336         below = line
337         while document.body[below].strip() == '':
338             below += 1
339         document.body[above:below] = ['', '\\begin_layout ' + prefix + 'Frontmatter',
340                                     '\\begin_inset Note Note',
341                                     'status open', '',
342                                     '\\begin_layout Plain Layout',
343                                     'Keep this empty!',
344                                     '\\end_layout', '',
345                                     '\\end_inset', '', '',
346                                     '\\end_layout', '']
347
348     if document.textclass == "elsarticle":
349         layouts = ['Title', 'Title footnote', 'Author', 'Author footnote',
350                    'Corresponding author', 'Address', 'Email', 'Abstract', 'Keywords']
351         first = -1
352         last = -1
353         for layout in layouts:
354             i = 0
355             while True:
356                 i = find_token(document.body, '\\begin_layout ' + layout, i)
357                 if i == -1:
358                     break
359                 k = find_end_of_layout(document.body, i)
360                 if k == -1:
361                     document.warning("Malformed LyX document: Can't find end of layout at line %d" % i)
362                     i += 1;
363                     continue
364                 if first == -1 or i < first:
365                     first = i
366                 if last == -1 or last <= k:
367                     last = k+1
368                 i = k+1
369         if first == -1:
370             return
371         insertFrontmatter('End', last)
372         insertFrontmatter('Begin', first)
373
374 def convert_lst_literalparam(document):
375     " Add param literal to include inset "
376
377     i = 0
378     while True:
379         i = find_token(document.body, '\\begin_inset CommandInset include', i)
380         if i == -1:
381             break
382         j = find_end_of_inset(document.body, i)
383         if j == -1:
384             document.warning("Malformed LyX document: Can't find end of command inset at line %d" % i)
385             i += 1
386             continue
387         while i < j and document.body[i].strip() != '':
388             i += 1
389         document.body.insert(i, "literal \"true\"")
390
391
392 def revert_lst_literalparam(document):
393     " Remove param literal from include inset "
394
395     i = 0
396     while True:
397         i = find_token(document.body, '\\begin_inset CommandInset include', i)
398         if i == -1:
399             break
400         j = find_end_of_inset(document.body, i)
401         if j == -1:
402             document.warning("Malformed LyX document: Can't find end of include inset at line %d" % i)
403             i += 1
404             continue
405         k = find_token(document.body, 'literal', i, j)
406         if k == -1:
407             i += 1
408             continue
409         del document.body[k]
410
411
412 def revert_paratype(document):
413     " Revert ParaType font definitions to LaTeX "
414
415     if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
416         preamble = ""
417         i1 = find_token(document.header, "\\font_roman \"PTSerif-TLF\"", 0)
418         i2 = find_token(document.header, "\\font_sans \"default\"", 0)
419         i3 = find_token(document.header, "\\font_typewriter \"default\"", 0)
420         j = find_token(document.header, "\\font_sans \"PTSans-TLF\"", 0)
421         sfval = get_value(document.header, "\\font_sf_scale", 0)
422         # cutoff " 100"
423         sfval = sfval[:-4]
424         sfoption = ""
425         if sfval != "100":
426             sfoption = "scaled=" + format(float(sfval) / 100, '.2f')
427         k = find_token(document.header, "\\font_typewriter \"PTMono-TLF\"", 0)
428         ttval = get_value(document.header, "\\font_tt_scale", 0)
429         # cutoff " 100"
430         ttval = ttval[:-4]
431         ttoption = ""
432         if ttval != "100":
433             ttoption = "scaled=" + format(float(ttval) / 100, '.2f')
434         if i1 != -1 and i2 != -1 and i3!= -1:
435             add_to_preamble(document, ["\\usepackage{paratype}"])
436         else:
437             if i1!= -1:
438                 add_to_preamble(document, ["\\usepackage{PTSerif}"])
439                 document.header[i1] = document.header[i1].replace("PTSerif-TLF", "default")
440             if j!= -1:
441                 if sfoption != "":
442                     add_to_preamble(document, ["\\usepackage[" + sfoption + "]{PTSans}"])
443                 else:
444                     add_to_preamble(document, ["\\usepackage{PTSans}"])
445                 document.header[j] = document.header[j].replace("PTSans-TLF", "default")
446             if k!= -1:
447                 if ttoption != "":
448                     add_to_preamble(document, ["\\usepackage[" + ttoption + "]{PTMono}"])
449                 else:
450                     add_to_preamble(document, ["\\usepackage{PTMono}"])
451                 document.header[k] = document.header[k].replace("PTMono-TLF", "default")
452
453
454 def revert_xcharter(document):
455     " Revert XCharter font definitions to LaTeX "
456
457     i = find_token(document.header, "\\font_roman \"xcharter\"", 0)
458     if i == -1:
459         return
460
461     # replace unsupported font setting
462     document.header[i] = document.header[i].replace("xcharter", "default")
463     # no need for preamble code with system fonts
464     if get_bool_value(document.header, "\\use_non_tex_fonts"):
465         return
466
467     # transfer old style figures setting to package options
468     j = find_token(document.header, "\\font_osf true")
469     if j != -1:
470         options = "[osf]"
471         document.header[j] = "\\font_osf false"
472     else:
473         options = ""
474     if i != -1:
475         add_to_preamble(document, ["\\usepackage%s{XCharter}"%options])
476
477
478 def revert_lscape(document):
479     " Reverts the landscape environment (Landscape module) to TeX-code "
480
481     if not "landscape" in document.get_module_list():
482         return
483
484     i = 0
485     while True:
486         i = find_token(document.body, "\\begin_inset Flex Landscape", i)
487         if i == -1:
488             return
489         j = find_end_of_inset(document.body, i)
490         if j == -1:
491             document.warning("Malformed LyX document: Can't find end of Landscape inset")
492             i += 1
493             continue
494
495         if document.body[i] == "\\begin_inset Flex Landscape (Floating)":
496             document.body[j - 2 : j + 1] = put_cmd_in_ert("\\end{landscape}}")
497             document.body[i : i + 4] = put_cmd_in_ert("\\afterpage{\\begin{landscape}")
498             add_to_preamble(document, ["\\usepackage{afterpage}"])
499         else:
500             document.body[j - 2 : j + 1] = put_cmd_in_ert("\\end{landscape}")
501             document.body[i : i + 4] = put_cmd_in_ert("\\begin{landscape}")
502
503         add_to_preamble(document, ["\\usepackage{pdflscape}"])
504         # no need to reset i
505
506
507 def convert_fontenc(document):
508     " Convert default fontenc setting "
509
510     i = find_token(document.header, "\\fontencoding global", 0)
511     if i == -1:
512         return
513
514     document.header[i] = document.header[i].replace("global", "auto")
515
516
517 def revert_fontenc(document):
518     " Revert default fontenc setting "
519
520     i = find_token(document.header, "\\fontencoding auto", 0)
521     if i == -1:
522         return
523
524     document.header[i] = document.header[i].replace("auto", "global")
525
526
527 def revert_nospellcheck(document):
528     " Remove nospellcheck font info param "
529
530     i = 0
531     while True:
532         i = find_token(document.body, '\\nospellcheck', i)
533         if i == -1:
534             return
535         del document.body[i]
536
537
538 def revert_floatpclass(document):
539     " Remove float placement params 'document' and 'class' "
540
541     i = 0
542     i = find_token(document.header, "\\float_placement class", 0)
543     if i != -1:
544         del document.header[i]
545
546     i = 0
547     while True:
548         i = find_token(document.body, '\\begin_inset Float', i)
549         if i == -1:
550             break
551         j = find_end_of_inset(document.body, i)
552         k = find_token(document.body, 'placement class', i, i + 2)
553         if k == -1:
554             k = find_token(document.body, 'placement document', i, i + 2)
555             if k != -1:
556                 del document.body[k]
557             i += 1
558             continue
559         del document.body[k]
560
561
562 def revert_floatalignment(document):
563     " Remove float alignment params "
564
565     i = 0
566     i = find_token(document.header, "\\float_alignment", 0)
567     galignment = ""
568     if i != -1:
569         galignment = get_value(document.header, "\\float_alignment", i)
570         del document.header[i]
571
572     i = 0
573     while True:
574         i = find_token(document.body, '\\begin_inset Float', i)
575         if i == -1:
576             break
577         j = find_end_of_inset(document.body, i)
578         if j == -1:
579             document.warning("Malformed LyX document: Can't find end of inset at line " + str(i))
580             i += 1
581         k = find_token(document.body, 'alignment', i, i + 4)
582         if k == -1:
583             i = j
584             continue
585         alignment = get_value(document.body, "alignment", k)
586         if alignment == "document":
587             alignment = galignment
588         del document.body[k]
589         l = find_token(document.body, "\\begin_layout Plain Layout", i, j)
590         if l == -1:
591             document.warning("Can't find float layout!")
592             i += 1
593             continue
594         alcmd = []
595         if alignment == "left":
596             alcmd = put_cmd_in_ert("\\raggedright{}")
597         elif alignment == "center":
598             alcmd = put_cmd_in_ert("\\centering{}")
599         elif alignment == "right":
600             alcmd = put_cmd_in_ert("\\raggedleft{}")
601         if len(alcmd) > 0:
602             document.body[l+1:l+1] = alcmd
603         i += 1
604
605
606 def revert_tuftecite(document):
607     " Revert \cite commands in tufte classes "
608
609     tufte = ["tufte-book", "tufte-handout"]
610     if document.textclass not in tufte:
611         return
612
613     i = 0
614     while (True):
615         i = find_token(document.body, "\\begin_inset CommandInset citation", i)
616         if i == -1:
617             break
618         j = find_end_of_inset(document.body, i)
619         if j == -1:
620             document.warning("Can't find end of citation inset at line %d!!" %(i))
621             i += 1
622             continue
623         k = find_token(document.body, "LatexCommand", i, j)
624         if k == -1:
625             document.warning("Can't find LatexCommand for citation inset at line %d!" %(i))
626             i = j + 1
627             continue
628         cmd = get_value(document.body, "LatexCommand", k)
629         if cmd != "cite":
630             i = j + 1
631             continue
632         pre = get_quoted_value(document.body, "before", i, j)
633         post = get_quoted_value(document.body, "after", i, j)
634         key = get_quoted_value(document.body, "key", i, j)
635         if not key:
636             document.warning("Citation inset at line %d does not have a key!" %(i))
637             key = "???"
638         # Replace command with ERT
639         res = "\\cite"
640         if pre:
641             res += "[" + pre + "]"
642         if post:
643             res += "[" + post + "]"
644         elif pre:
645             res += "[]"
646         res += "{" + key + "}"
647         document.body[i:j+1] = put_cmd_in_ert([res])
648         i = j + 1
649
650
651 def revert_stretchcolumn(document):
652     " We remove the column varwidth flags or everything else will become a mess. "
653     i = 0
654     while True:
655         i = find_token(document.body, "\\begin_inset Tabular", i)
656         if i == -1:
657             return
658         j = find_end_of_inset(document.body, i + 1)
659         if j == -1:
660             document.warning("Malformed LyX document: Could not find end of tabular.")
661             continue
662         for k in range(i, j):
663             if re.search('^<column.*varwidth="[^"]+".*>$', document.body[k]):
664                 document.warning("Converting 'tabularx'/'xltabular' table to normal table.")
665                 document.body[k] = document.body[k].replace(' varwidth="true"', '')
666         i = i + 1
667
668
669 def revert_vcolumns(document):
670     " Revert standard columns with line breaks etc. "
671     i = 0
672     needvarwidth = False
673     needarray = False
674     try:
675         while True:
676             i = find_token(document.body, "\\begin_inset Tabular", i)
677             if i == -1:
678                 return
679             j = find_end_of_inset(document.body, i)
680             if j == -1:
681                 document.warning("Malformed LyX document: Could not find end of tabular.")
682                 i += 1
683                 continue
684
685             # Collect necessary column information
686             m = i + 1
687             nrows = int(document.body[i+1].split('"')[3])
688             ncols = int(document.body[i+1].split('"')[5])
689             col_info = []
690             for k in range(ncols):
691                 m = find_token(document.body, "<column", m)
692                 width = get_option_value(document.body[m], 'width')
693                 varwidth = get_option_value(document.body[m], 'varwidth')
694                 alignment = get_option_value(document.body[m], 'alignment')
695                 special = get_option_value(document.body[m], 'special')
696                 col_info.append([width, varwidth, alignment, special, m])
697
698             # Now parse cells
699             m = i + 1
700             lines = []
701             for row in range(nrows):
702                 for col in range(ncols):
703                     m = find_token(document.body, "<cell", m)
704                     multicolumn = get_option_value(document.body[m], 'multicolumn')
705                     multirow = get_option_value(document.body[m], 'multirow')
706                     width = get_option_value(document.body[m], 'width')
707                     rotate = get_option_value(document.body[m], 'rotate')
708                     # Check for: linebreaks, multipars, non-standard environments
709                     begcell = m
710                     endcell = find_token(document.body, "</cell>", begcell)
711                     vcand = False
712                     if find_token(document.body, "\\begin_inset Newline", begcell, endcell) != -1:
713                         vcand = True
714                     elif count_pars_in_inset(document.body, begcell + 2) > 1:
715                         vcand = True
716                     elif get_value(document.body, "\\begin_layout", begcell) != "Plain Layout":
717                         vcand = True
718                     if vcand and rotate == "" and ((multicolumn == "" and multirow == "") or width == ""):
719                         if col_info[col][0] == "" and col_info[col][1] == "" and col_info[col][3] == "":
720                             needvarwidth = True
721                             alignment = col_info[col][2]
722                             col_line = col_info[col][4]
723                             vval = ""
724                             if alignment == "center":
725                                 vval = ">{\\centering}"
726                             elif  alignment == "left":
727                                 vval = ">{\\raggedright}"
728                             elif alignment == "right":
729                                 vval = ">{\\raggedleft}"
730                             if vval != "":
731                                 needarray = True
732                             vval += "V{\\linewidth}"
733
734                             document.body[col_line] = document.body[col_line][:-1] + " special=\"" + vval + "\">"
735                             # ERT newlines and linebreaks (since LyX < 2.4 automatically inserts parboxes
736                             # with newlines, and we do not want that)
737                             while True:
738                                 endcell = find_token(document.body, "</cell>", begcell)
739                                 linebreak = False
740                                 nl = find_token(document.body, "\\begin_inset Newline newline", begcell, endcell)
741                                 if nl == -1:
742                                     nl = find_token(document.body, "\\begin_inset Newline linebreak", begcell, endcell)
743                                     if nl == -1:
744                                          break
745                                     linebreak = True
746                                 nle = find_end_of_inset(document.body, nl)
747                                 del(document.body[nle:nle+1])
748                                 if linebreak:
749                                     document.body[nl:nl+1] = put_cmd_in_ert("\\linebreak{}")
750                                 else:
751                                     document.body[nl:nl+1] = put_cmd_in_ert("\\\\")
752                     m += 1
753
754             i = j + 1
755
756     finally:
757         if needarray == True:
758             add_to_preamble(document, ["\\usepackage{array}"])
759         if needvarwidth == True:
760             add_to_preamble(document, ["\\usepackage{varwidth}"])
761
762
763 def revert_bibencoding(document):
764     " Revert bibliography encoding "
765
766     # Get cite engine
767     engine = "basic"
768     i = find_token(document.header, "\\cite_engine", 0)
769     if i == -1:
770         document.warning("Malformed document! Missing \\cite_engine")
771     else:
772         engine = get_value(document.header, "\\cite_engine", i)
773
774     # Check if biblatex
775     biblatex = False
776     if engine in ["biblatex", "biblatex-natbib"]:
777         biblatex = True
778
779     # Map lyx to latex encoding names
780     encodings = {
781         "utf8" : "utf8",
782         "utf8x" : "utf8x",
783         "armscii8" : "armscii8",
784         "iso8859-1" : "latin1",
785         "iso8859-2" : "latin2",
786         "iso8859-3" : "latin3",
787         "iso8859-4" : "latin4",
788         "iso8859-5" : "iso88595",
789         "iso8859-6" : "8859-6",
790         "iso8859-7" : "iso-8859-7",
791         "iso8859-8" : "8859-8",
792         "iso8859-9" : "latin5",
793         "iso8859-13" : "latin7",
794         "iso8859-15" : "latin9",
795         "iso8859-16" : "latin10",
796         "applemac" : "applemac",
797         "cp437" : "cp437",
798         "cp437de" : "cp437de",
799         "cp850" : "cp850",
800         "cp852" : "cp852",
801         "cp855" : "cp855",
802         "cp858" : "cp858",
803         "cp862" : "cp862",
804         "cp865" : "cp865",
805         "cp866" : "cp866",
806         "cp1250" : "cp1250",
807         "cp1251" : "cp1251",
808         "cp1252" : "cp1252",
809         "cp1255" : "cp1255",
810         "cp1256" : "cp1256",
811         "cp1257" : "cp1257",
812         "koi8-r" : "koi8-r",
813         "koi8-u" : "koi8-u",
814         "pt154" : "pt154",
815         "utf8-platex" : "utf8",
816         "ascii" : "ascii"
817     }
818
819     i = 0
820     bibresources = []
821     while (True):
822         i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
823         if i == -1:
824             break
825         j = find_end_of_inset(document.body, i)
826         if j == -1:
827             document.warning("Can't find end of bibtex inset at line %d!!" %(i))
828             i += 1
829             continue
830         encoding = get_quoted_value(document.body, "encoding", i, j)
831         if not encoding:
832             i += 1
833             continue
834         # remove encoding line
835         k = find_token(document.body, "encoding", i, j)
836         if k != -1:
837             del document.body[k]
838         if encoding == "default":
839             i += 1
840             continue
841         # Re-find inset end line
842         j = find_end_of_inset(document.body, i)
843         if biblatex:
844             biblio_options = ""
845             h = find_token(document.header, "\\biblio_options", 0)
846             if h != -1:
847                 biblio_options = get_value(document.header, "\\biblio_options", h)
848                 if not "bibencoding" in biblio_options:
849                      document.header[h] += ",bibencoding=%s" % encodings[encoding]
850             else:
851                 bs = find_token(document.header, "\\biblatex_bibstyle", 0)
852                 if bs == -1:
853                     # this should not happen
854                     document.warning("Malformed LyX document! No \\biblatex_bibstyle header found!")
855                 else:
856                     document.header[bs-1 : bs-1] = ["\\biblio_options bibencoding=" + encodings[encoding]]
857         else:
858             document.body[j+1:j+1] = put_cmd_in_ert("\\egroup")
859             document.body[i:i] = put_cmd_in_ert("\\bgroup\\inputencoding{" + encodings[encoding] + "}")
860
861         i = j + 1
862
863
864
865 def convert_vcsinfo(document):
866     " Separate vcs Info inset from buffer Info inset. "
867
868     types = {
869         "vcs-revision" : "revision",
870         "vcs-tree-revision" : "tree-revision",
871         "vcs-author" : "author",
872         "vcs-time" : "time",
873         "vcs-date" : "date"
874     }
875     i = 0
876     while True:
877         i = find_token(document.body, "\\begin_inset Info", i)
878         if i == -1:
879             return
880         j = find_end_of_inset(document.body, i + 1)
881         if j == -1:
882             document.warning("Malformed LyX document: Could not find end of Info inset.")
883             i = i + 1
884             continue
885         tp = find_token(document.body, 'type', i, j)
886         tpv = get_quoted_value(document.body, "type", tp)
887         if tpv != "buffer":
888             i = i + 1
889             continue
890         arg = find_token(document.body, 'arg', i, j)
891         argv = get_quoted_value(document.body, "arg", arg)
892         if argv not in list(types.keys()):
893             i = i + 1
894             continue
895         document.body[tp] = "type \"vcs\""
896         document.body[arg] = "arg \"" + types[argv] + "\""
897         i = i + 1
898
899
900 def revert_vcsinfo(document):
901     " Merge vcs Info inset to buffer Info inset. "
902
903     args = ["revision", "tree-revision", "author", "time", "date" ]
904     i = 0
905     while True:
906         i = find_token(document.body, "\\begin_inset Info", i)
907         if i == -1:
908             return
909         j = find_end_of_inset(document.body, i + 1)
910         if j == -1:
911             document.warning("Malformed LyX document: Could not find end of Info inset.")
912             i = i + 1
913             continue
914         tp = find_token(document.body, 'type', i, j)
915         tpv = get_quoted_value(document.body, "type", tp)
916         if tpv != "vcs":
917             i = i + 1
918             continue
919         arg = find_token(document.body, 'arg', i, j)
920         argv = get_quoted_value(document.body, "arg", arg)
921         if argv not in args:
922             document.warning("Malformed Info inset. Invalid vcs arg.")
923             i = i + 1
924             continue
925         document.body[tp] = "type \"buffer\""
926         document.body[arg] = "arg \"vcs-" + argv + "\""
927         i = i + 1
928
929
930 def revert_dateinfo(document):
931     " Revert date info insets to static text. "
932
933 # FIXME This currently only considers the main language and uses the system locale
934 # Ideally, it should honor context languages and switch the locale accordingly.
935
936     # The date formats for each language using strftime syntax:
937     # long, short, loclong, locmedium, locshort
938     dateformats = {
939         "afrikaans" : ["%A, %d %B %Y", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y/%m/%d"],
940         "albanian" : ["%A, %d %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
941         "american" : ["%A, %B %d, %Y", "%m/%d/%y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
942         "amharic" : ["%A ፣%d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
943         "ancientgreek" : ["%A, %d %B %Y", "%d %b %Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
944         "arabic_arabi" : ["%A، %d %B، %Y", "%d‏/%m‏/%Y", "%d %B، %Y", "%d/%m/%Y", "%d/%m/%Y"],
945         "arabic_arabtex" : ["%A، %d %B، %Y", "%d‏/%m‏/%Y", "%d %B، %Y", "%d/%m/%Y", "%d/%m/%Y"],
946         "armenian" : ["%Y թ. %B %d, %A", "%d.%m.%y", "%d %B، %Y", "%d %b، %Y", "%d/%m/%Y"],
947         "asturian" : ["%A, %d %B de %Y", "%d/%m/%y", "%d de %B de %Y", "%d %b %Y", "%d/%m/%Y"],
948         "australian" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
949         "austrian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
950         "bahasa" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
951         "bahasam" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
952         "basque" : ["%Y(e)ko %B %d, %A", "%y/%m/%d", "%Y %B %d", "%Y %b %d", "%Y/%m/%d"],
953         "belarusian" : ["%A, %d %B %Y г.", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
954         "bosnian" : ["%A, %d. %B %Y.", "%d.%m.%y.", "%d. %B %Y", "%d. %b %Y", "%Y-%m-%d"],
955         "brazilian" : ["%A, %d de %B de %Y", "%d/%m/%Y", "%d de %B de %Y", "%d de %b de %Y", "%d/%m/%Y"],
956         "breton" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
957         "british" : ["%A, %d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
958         "bulgarian" : ["%A, %d %B %Y г.", "%d.%m.%y г.", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
959         "canadian" : ["%A, %B %d, %Y", "%Y-%m-%d", "%B %d, %Y", "%d %b %Y", "%Y-%m-%d"],
960         "canadien" : ["%A %d %B %Y", "%y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
961         "catalan" : ["%A, %d %B de %Y", "%d/%m/%y", "%d / %B / %Y", "%d / %b / %Y", "%d/%m/%Y"],
962         "chinese-simplified" : ["%Y年%m月%d日%A", "%Y/%m/%d", "%Y年%m月%d日", "%Y-%m-%d", "%y-%m-%d"],
963         "chinese-traditional" : ["%Y年%m月%d日 %A", "%Y/%m/%d", "%Y年%m月%d日", "%Y年%m月%d日", "%y年%m月%d日"],
964         "coptic" : ["%A, %d %B %Y", "%d %b %Y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
965         "croatian" : ["%A, %d. %B %Y.", "%d. %m. %Y.", "%d. %B %Y.", "%d. %b. %Y.", "%d.%m.%Y."],
966         "czech" : ["%A %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b. %Y", "%d.%m.%Y"],
967         "danish" : ["%A den %d. %B %Y", "%d/%m/%Y", "%d. %B %Y", "%d. %b %Y", "%d/%m/%Y"],
968         "divehi" : ["%Y %B %d, %A", "%Y-%m-%d", "%Y %B %d", "%Y %b %d", "%d/%m/%Y"],
969         "dutch" : ["%A %d %B %Y", "%d-%m-%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
970         "english" : ["%A, %B %d, %Y", "%m/%d/%y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
971         "esperanto" : ["%A, %d %B %Y", "%d %b %Y", "la %d de %B %Y", "la %d de %b %Y", "%m/%d/%Y"],
972         "estonian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
973         "farsi" : ["%A %d %B %Y", "%Y/%m/%d", "%d %B %Y", "%d %b %Y", "%Y/%m/%d"],
974         "finnish" : ["%A %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
975         "french" : ["%A %d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
976         "friulan" : ["%A %d di %B dal %Y", "%d/%m/%y", "%d di %B dal %Y", "%d di %b dal %Y", "%d/%m/%Y"],
977         "galician" : ["%A, %d de %B de %Y", "%d/%m/%y", "%d de %B de %Y", "%d de %b de %Y", "%d/%m/%Y"],
978         "georgian" : ["%A, %d %B, %Y", "%d.%m.%y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
979         "german" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
980         "german-ch" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
981         "german-ch-old" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
982         "greek" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
983         "hebrew" : ["%A, %d ב%B %Y", "%d.%m.%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
984         "hindi" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
985         "icelandic" : ["%A, %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
986         "interlingua" : ["%Y %B %d, %A", "%Y-%m-%d", "le %d de %B %Y", "le %d de %b %Y", "%Y-%m-%d"],
987         "irish" : ["%A %d %B %Y", "%d/%m/%Y", "%d. %B %Y", "%d. %b %Y", "%d/%m/%Y"],
988         "italian" : ["%A %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d/%b/%Y", "%d/%m/%Y"],
989         "japanese" : ["%Y年%m月%d日%A", "%Y/%m/%d", "%Y年%m月%d日", "%Y/%m/%d", "%y/%m/%d"],
990         "japanese-cjk" : ["%Y年%m月%d日%A", "%Y/%m/%d", "%Y年%m月%d日", "%Y/%m/%d", "%y/%m/%d"],
991         "kannada" : ["%A, %B %d, %Y", "%d/%m/%y", "%d %B %Y", "%d %B %Y", "%d-%m-%Y"],
992         "kazakh" : ["%Y ж. %d %B, %A", "%d.%m.%y", "%d %B %Y", "%d %B %Y", "%Y-%d-%m"],
993         "khmer" : ["%A %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %B %Y", "%d/%m/%Y"],
994         "korean" : ["%Y년 %m월 %d일 %A", "%y. %m. %d.", "%Y년 %m월 %d일", "%Y. %m. %d.", "%y. %m. %d."],
995         "kurmanji" : ["%A, %d %B %Y", "%d %b %Y", "%d. %B %Y", "%d. %m. %Y", "%Y-%m-%d"],
996         "lao" : ["%A ທີ %d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %B %Y", "%d/%m/%Y"],
997         "latin" : ["%A, %d %B %Y", "%d %b %Y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
998         "latvian" : ["%A, %Y. gada %d. %B", "%d.%m.%y", "%Y. gada %d. %B", "%Y. gada %d. %b", "%d.%m.%Y"],
999         "lithuanian" : ["%Y m. %B %d d., %A", "%Y-%m-%d", "%Y m. %B %d d.", "%Y m. %B %d d.", "%Y-%m-%d"],
1000         "lowersorbian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1001         "macedonian" : ["%A, %d %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1002         "magyar" : ["%Y. %B %d., %A", "%Y. %m. %d.", "%Y. %B %d.", "%Y. %b %d.", "%Y.%m.%d."],
1003         "malayalam" : ["%A, %d %B, %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1004         "marathi" : ["%A, %d %B, %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1005         "mongolian" : ["%A, %Y оны %m сарын %d", "%Y-%m-%d", "%Y оны %m сарын %d", "%d-%m-%Y", "%d-%m-%Y"],
1006         "naustrian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1007         "newzealand" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1008         "ngerman" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1009         "norsk" : ["%A %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1010         "nynorsk" : ["%A %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1011         "occitan" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1012         "piedmontese" : ["%A, %d %B %Y", "%d %b %Y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1013         "polish" : ["%A, %d %B %Y", "%d.%m.%Y", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
1014         "polutonikogreek" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1015         "portuguese" : ["%A, %d de %B de %Y", "%d/%m/%y", "%d de %B de %Y", "%d de %b de %Y", "%Y/%m/%d"],
1016         "romanian" : ["%A, %d %B %Y", "%d.%m.%Y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1017         "romansh" : ["%A, ils %d da %B %Y", "%d-%m-%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1018         "russian" : ["%A, %d %B %Y г.", "%d.%m.%Y", "%d %B %Y г.", "%d %b %Y г.", "%d.%m.%Y"],
1019         "samin" : ["%Y %B %d, %A", "%Y-%m-%d", "%B %d. b. %Y", "%b %d. b. %Y", "%d.%m.%Y"],
1020         "sanskrit" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1021         "scottish" : ["%A, %dmh %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1022         "serbian" : ["%A, %d. %B %Y.", "%d.%m.%y.", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1023         "serbian-latin" : ["%A, %d. %B %Y.", "%d.%m.%y.", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1024         "slovak" : ["%A, %d. %B %Y", "%d. %m. %Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1025         "slovene" : ["%A, %d. %B %Y", "%d. %m. %y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1026         "spanish" : ["%A, %d de %B de %Y", "%d/%m/%y", "%d de %B %de %Y", "%d %b %Y", "%d/%m/%Y"],
1027         "spanish-mexico" : ["%A, %d de %B %de %Y", "%d/%m/%y", "%d de %B de %Y", "%d %b %Y", "%d/%m/%Y"],
1028         "swedish" : ["%A %d %B %Y", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
1029         "syriac" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1030         "tamil" : ["%A, %d %B, %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1031         "telugu" : ["%d, %B %Y, %A", "%d-%m-%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1032         "thai" : ["%Aที่ %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1033         "tibetan" : ["%Y %Bའི་ཚེས་%d, %A", "%Y-%m-%d", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1034         "turkish" : ["%d %B %Y %A", "%d.%m.%Y", "%d %B %Y", "%d.%b.%Y", "%d.%m.%Y"],
1035         "turkmen" : ["%d %B %Y %A", "%d.%m.%Y", "%Y ý. %B %d", "%d.%m.%Y ý.", "%d.%m.%y ý."],
1036         "ukrainian" : ["%A, %d %B %Y р.", "%d.%m.%y", "%d %B %Y", "%d %m %Y", "%d.%m.%Y"],
1037         "uppersorbian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1038         "urdu" : ["%A، %d %B، %Y", "%d/%m/%y", "%d %B, %Y", "%d %b %Y", "%d/%m/%Y"],
1039         "vietnamese" : ["%A, %d %B, %Y", "%d/%m/%Y", "%d tháng %B %Y", "%d-%m-%Y", "%d/%m/%Y"],
1040         "welsh" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1041     }
1042
1043     types = ["date", "fixdate", "moddate" ]
1044     i = 0
1045     i = find_token(document.header, "\\language", 0)
1046     if i == -1:
1047         # this should not happen
1048         document.warning("Malformed LyX document! No \\language header found!")
1049         return
1050     lang = get_value(document.header, "\\language", i)
1051
1052     i = 0
1053     while True:
1054         i = find_token(document.body, "\\begin_inset Info", i)
1055         if i == -1:
1056             return
1057         j = find_end_of_inset(document.body, i + 1)
1058         if j == -1:
1059             document.warning("Malformed LyX document: Could not find end of Info inset.")
1060             i = i + 1
1061             continue
1062         tp = find_token(document.body, 'type', i, j)
1063         tpv = get_quoted_value(document.body, "type", tp)
1064         if tpv not in types:
1065             i = i + 1
1066             continue
1067         arg = find_token(document.body, 'arg', i, j)
1068         argv = get_quoted_value(document.body, "arg", arg)
1069         isodate = ""
1070         dte = date.today()
1071         if tpv == "fixdate":
1072             datecomps = argv.split('@')
1073             if len(datecomps) > 1:
1074                 argv = datecomps[0]
1075                 isodate = datecomps[1]
1076                 m = re.search('(\d\d\d\d)-(\d\d)-(\d\d)', isodate)
1077                 if m:
1078                     dte = date(int(m.group(1)), int(m.group(2)), int(m.group(3)))
1079 # FIXME if we had the path to the original document (not the one in the tmp dir),
1080 #        we could use the mtime.
1081 #        elif tpv == "moddate":
1082 #            dte = date.fromtimestamp(os.path.getmtime(document.dir))
1083         result = ""
1084         if argv == "ISO":
1085             result = dte.isodate()
1086         elif argv == "long":
1087             result = dte.strftime(dateformats[lang][0])
1088         elif argv == "short":
1089             result = dte.strftime(dateformats[lang][1])
1090         elif argv == "loclong":
1091             result = dte.strftime(dateformats[lang][2])
1092         elif argv == "locmedium":
1093             result = dte.strftime(dateformats[lang][3])
1094         elif argv == "locshort":
1095             result = dte.strftime(dateformats[lang][4])
1096         else:
1097             fmt = argv.replace("MMMM", "%b").replace("MMM", "%b").replace("MM", "%m").replace("M", "%m")
1098             fmt = fmt.replace("yyyy", "%Y").replace("yy", "%y")
1099             fmt = fmt.replace("dddd", "%A").replace("ddd", "%a").replace("dd", "%d")
1100             fmt = re.sub('[^\'%]d', '%d', fmt)
1101             fmt = fmt.replace("'", "")
1102             result = dte.strftime(fmt)
1103         if sys.version_info < (3,0):
1104             # In Python 2, datetime module works with binary strings,
1105             # our dateformat strings are utf8-encoded:
1106             result = result.decode('utf-8')
1107         document.body[i : j+1] = result
1108         i = i + 1
1109
1110
1111 def revert_timeinfo(document):
1112     " Revert time info insets to static text. "
1113
1114 # FIXME This currently only considers the main language and uses the system locale
1115 # Ideally, it should honor context languages and switch the locale accordingly.
1116 # Also, the time object is "naive", i.e., it does not know of timezones (%Z will
1117 # be empty).
1118
1119     # The time formats for each language using strftime syntax:
1120     # long, short
1121     timeformats = {
1122         "afrikaans" : ["%H:%M:%S %Z", "%H:%M"],
1123         "albanian" : ["%I:%M:%S %p, %Z", "%I:%M %p"],
1124         "american" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1125         "amharic" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1126         "ancientgreek" : ["%H:%M:%S %Z", "%H:%M:%S"],
1127         "arabic_arabi" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1128         "arabic_arabtex" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1129         "armenian" : ["%H:%M:%S %Z", "%H:%M"],
1130         "asturian" : ["%H:%M:%S %Z", "%H:%M"],
1131         "australian" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1132         "austrian" : ["%H:%M:%S %Z", "%H:%M"],
1133         "bahasa" : ["%H.%M.%S %Z", "%H.%M"],
1134         "bahasam" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1135         "basque" : ["%H:%M:%S (%Z)", "%H:%M"],
1136         "belarusian" : ["%H:%M:%S, %Z", "%H:%M"],
1137         "bosnian" : ["%H:%M:%S %Z", "%H:%M"],
1138         "brazilian" : ["%H:%M:%S %Z", "%H:%M"],
1139         "breton" : ["%H:%M:%S %Z", "%H:%M"],
1140         "british" : ["%H:%M:%S %Z", "%H:%M"],
1141         "bulgarian" : ["%H:%M:%S %Z", "%H:%M"],
1142         "canadian" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1143         "canadien" : ["%H:%M:%S %Z", "%H h %M"],
1144         "catalan" : ["%H:%M:%S %Z", "%H:%M"],
1145         "chinese-simplified" : ["%Z %p%I:%M:%S", "%p%I:%M"],
1146         "chinese-traditional" : ["%p%I:%M:%S [%Z]", "%p%I:%M"],
1147         "coptic" : ["%H:%M:%S %Z", "%H:%M:%S"],
1148         "croatian" : ["%H:%M:%S (%Z)", "%H:%M"],
1149         "czech" : ["%H:%M:%S %Z", "%H:%M"],
1150         "danish" : ["%H.%M.%S %Z", "%H.%M"],
1151         "divehi" : ["%H:%M:%S %Z", "%H:%M"],
1152         "dutch" : ["%H:%M:%S %Z", "%H:%M"],
1153         "english" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1154         "esperanto" : ["%H:%M:%S %Z", "%H:%M:%S"],
1155         "estonian" : ["%H:%M:%S %Z", "%H:%M"],
1156         "farsi" : ["%H:%M:%S (%Z)", "%H:%M"],
1157         "finnish" : ["%H.%M.%S %Z", "%H.%M"],
1158         "french" : ["%H:%M:%S %Z", "%H:%M"],
1159         "friulan" : ["%H:%M:%S %Z", "%H:%M"],
1160         "galician" : ["%H:%M:%S %Z", "%H:%M"],
1161         "georgian" : ["%H:%M:%S %Z", "%H:%M"],
1162         "german" : ["%H:%M:%S %Z", "%H:%M"],
1163         "german-ch" : ["%H:%M:%S %Z", "%H:%M"],
1164         "german-ch-old" : ["%H:%M:%S %Z", "%H:%M"],
1165         "greek" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1166         "hebrew" : ["%H:%M:%S %Z", "%H:%M"],
1167         "hindi" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1168         "icelandic" : ["%H:%M:%S %Z", "%H:%M"],
1169         "interlingua" : ["%H:%M:%S %Z", "%H:%M"],
1170         "irish" : ["%H:%M:%S %Z", "%H:%M"],
1171         "italian" : ["%H:%M:%S %Z", "%H:%M"],
1172         "japanese" : ["%H時%M分%S秒 %Z", "%H:%M"],
1173         "japanese-cjk" : ["%H時%M分%S秒 %Z", "%H:%M"],
1174         "kannada" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1175         "kazakh" : ["%H:%M:%S %Z", "%H:%M"],
1176         "khmer" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1177         "korean" : ["%p %I시%M분 %S초 %Z", "%p %I:%M"],
1178         "kurmanji" : ["%H:%M:%S %Z", "%H:%M:%S"],
1179         "lao" : ["%H ໂມງ%M ນາທີ  %S ວິນາທີ %Z", "%H:%M"],
1180         "latin" : ["%H:%M:%S %Z", "%H:%M:%S"],
1181         "latvian" : ["%H:%M:%S %Z", "%H:%M"],
1182         "lithuanian" : ["%H:%M:%S %Z", "%H:%M"],
1183         "lowersorbian" : ["%H:%M:%S %Z", "%H:%M"],
1184         "macedonian" : ["%H:%M:%S %Z", "%H:%M"],
1185         "magyar" : ["%H:%M:%S %Z", "%H:%M"],
1186         "malayalam" : ["%p %I:%M:%S %Z", "%p %I:%M"],
1187         "marathi" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1188         "mongolian" : ["%H:%M:%S %Z", "%H:%M"],
1189         "naustrian" : ["%H:%M:%S %Z", "%H:%M"],
1190         "newzealand" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1191         "ngerman" : ["%H:%M:%S %Z", "%H:%M"],
1192         "norsk" : ["%H:%M:%S %Z", "%H:%M"],
1193         "nynorsk" : ["kl. %H:%M:%S %Z", "%H:%M"],
1194         "occitan" : ["%H:%M:%S %Z", "%H:%M"],
1195         "piedmontese" : ["%H:%M:%S %Z", "%H:%M:%S"],
1196         "polish" : ["%H:%M:%S %Z", "%H:%M"],
1197         "polutonikogreek" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1198         "portuguese" : ["%H:%M:%S %Z", "%H:%M"],
1199         "romanian" : ["%H:%M:%S %Z", "%H:%M"],
1200         "romansh" : ["%H:%M:%S %Z", "%H:%M"],
1201         "russian" : ["%H:%M:%S %Z", "%H:%M"],
1202         "samin" : ["%H:%M:%S %Z", "%H:%M"],
1203         "sanskrit" : ["%H:%M:%S %Z", "%H:%M"],
1204         "scottish" : ["%H:%M:%S %Z", "%H:%M"],
1205         "serbian" : ["%H:%M:%S %Z", "%H:%M"],
1206         "serbian-latin" : ["%H:%M:%S %Z", "%H:%M"],
1207         "slovak" : ["%H:%M:%S %Z", "%H:%M"],
1208         "slovene" : ["%H:%M:%S %Z", "%H:%M"],
1209         "spanish" : ["%H:%M:%S (%Z)", "%H:%M"],
1210         "spanish-mexico" : ["%H:%M:%S %Z", "%H:%M"],
1211         "swedish" : ["kl. %H:%M:%S %Z", "%H:%M"],
1212         "syriac" : ["%H:%M:%S %Z", "%H:%M"],
1213         "tamil" : ["%p %I:%M:%S %Z", "%p %I:%M"],
1214         "telugu" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1215         "thai" : ["%H นาฬิกา %M นาที  %S วินาที %Z", "%H:%M"],
1216         "tibetan" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1217         "turkish" : ["%H:%M:%S %Z", "%H:%M"],
1218         "turkmen" : ["%H:%M:%S %Z", "%H:%M"],
1219         "ukrainian" : ["%H:%M:%S %Z", "%H:%M"],
1220         "uppersorbian" : ["%H:%M:%S %Z", "%H:%M hodź."],
1221         "urdu" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1222         "vietnamese" : ["%H:%M:%S %Z", "%H:%M"],
1223         "welsh" : ["%H:%M:%S %Z", "%H:%M"]
1224     }
1225
1226     types = ["time", "fixtime", "modtime" ]
1227     i = 0
1228     i = find_token(document.header, "\\language", 0)
1229     if i == -1:
1230         # this should not happen
1231         document.warning("Malformed LyX document! No \\language header found!")
1232         return
1233     lang = get_value(document.header, "\\language", i)
1234
1235     i = 0
1236     while True:
1237         i = find_token(document.body, "\\begin_inset Info", i)
1238         if i == -1:
1239             return
1240         j = find_end_of_inset(document.body, i + 1)
1241         if j == -1:
1242             document.warning("Malformed LyX document: Could not find end of Info inset.")
1243             i = i + 1
1244             continue
1245         tp = find_token(document.body, 'type', i, j)
1246         tpv = get_quoted_value(document.body, "type", tp)
1247         if tpv not in types:
1248             i = i + 1
1249             continue
1250         arg = find_token(document.body, 'arg', i, j)
1251         argv = get_quoted_value(document.body, "arg", arg)
1252         isotime = ""
1253         dtme = datetime.now()
1254         tme = dtme.time()
1255         if tpv == "fixtime":
1256             timecomps = argv.split('@')
1257             if len(timecomps) > 1:
1258                 argv = timecomps[0]
1259                 isotime = timecomps[1]
1260                 m = re.search('(\d\d):(\d\d):(\d\d)', isotime)
1261                 if m:
1262                     tme = time(int(m.group(1)), int(m.group(2)), int(m.group(3)))
1263                 else:
1264                     m = re.search('(\d\d):(\d\d)', isotime)
1265                     if m:
1266                         tme = time(int(m.group(1)), int(m.group(2)))
1267 # FIXME if we had the path to the original document (not the one in the tmp dir),
1268 #        we could use the mtime.
1269 #        elif tpv == "moddate":
1270 #            dte = date.fromtimestamp(os.path.getmtime(document.dir))
1271         result = ""
1272         if argv == "ISO":
1273             result = tme.isoformat()
1274         elif argv == "long":
1275             result = tme.strftime(timeformats[lang][0])
1276         elif argv == "short":
1277             result = tme.strftime(timeformats[lang][1])
1278         else:
1279             fmt = argv.replace("HH", "%H").replace("H", "%H").replace("hh", "%I").replace("h", "%I")
1280             fmt = fmt.replace("mm", "%M").replace("m", "%M").replace("ss", "%S").replace("s", "%S")
1281             fmt = fmt.replace("zzz", "%f").replace("z", "%f").replace("t", "%Z")
1282             fmt = fmt.replace("AP", "%p").replace("ap", "%p").replace("A", "%p").replace("a", "%p")
1283             fmt = fmt.replace("'", "")
1284             result = dte.strftime(fmt)
1285         document.body[i : j+1] = result
1286         i = i + 1
1287
1288
1289 def revert_namenoextinfo(document):
1290     " Merge buffer Info inset type name-noext to name. "
1291
1292     i = 0
1293     while True:
1294         i = find_token(document.body, "\\begin_inset Info", i)
1295         if i == -1:
1296             return
1297         j = find_end_of_inset(document.body, i + 1)
1298         if j == -1:
1299             document.warning("Malformed LyX document: Could not find end of Info inset.")
1300             i = i + 1
1301             continue
1302         tp = find_token(document.body, 'type', i, j)
1303         tpv = get_quoted_value(document.body, "type", tp)
1304         if tpv != "buffer":
1305             i = i + 1
1306             continue
1307         arg = find_token(document.body, 'arg', i, j)
1308         argv = get_quoted_value(document.body, "arg", arg)
1309         if argv != "name-noext":
1310             i = i + 1
1311             continue
1312         document.body[arg] = "arg \"name\""
1313         i = i + 1
1314
1315
1316 def revert_l7ninfo(document):
1317     " Revert l7n Info inset to text. "
1318
1319     i = 0
1320     while True:
1321         i = find_token(document.body, "\\begin_inset Info", i)
1322         if i == -1:
1323             return
1324         j = find_end_of_inset(document.body, i + 1)
1325         if j == -1:
1326             document.warning("Malformed LyX document: Could not find end of Info inset.")
1327             i = i + 1
1328             continue
1329         tp = find_token(document.body, 'type', i, j)
1330         tpv = get_quoted_value(document.body, "type", tp)
1331         if tpv != "l7n":
1332             i = i + 1
1333             continue
1334         arg = find_token(document.body, 'arg', i, j)
1335         argv = get_quoted_value(document.body, "arg", arg)
1336         # remove trailing colons, menu accelerator (|...) and qt accelerator (&), while keeping literal " & "
1337         argv = argv.rstrip(':').split('|')[0].replace(" & ", "</amp;>").replace("&", "").replace("</amp;>", " & ")
1338         document.body[i : j+1] = argv
1339         i = i + 1
1340
1341
1342 def revert_listpargs(document):
1343     " Reverts listpreamble arguments to TeX-code "
1344     i = 0
1345     while True:
1346         i = find_token(document.body, "\\begin_inset Argument listpreamble:", i)
1347         if i == -1:
1348             return
1349         j = find_end_of_inset(document.body, i)
1350         # Find containing paragraph layout
1351         parent = get_containing_layout(document.body, i)
1352         if parent == False:
1353             document.warning("Malformed LyX document: Can't find parent paragraph layout")
1354             i += 1
1355             continue
1356         parbeg = parent[3]
1357         beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1358         endPlain = find_end_of_layout(document.body, beginPlain)
1359         content = document.body[beginPlain + 1 : endPlain]
1360         del document.body[i:j+1]
1361         subst = ["\\begin_inset ERT", "status collapsed", "", "\\begin_layout Plain Layout",
1362                  "{"] + content + ["}", "\\end_layout", "", "\\end_inset", ""]
1363         document.body[parbeg : parbeg] = subst
1364         i += 1
1365
1366
1367 def revert_lformatinfo(document):
1368     " Revert layout format Info inset to text. "
1369
1370     i = 0
1371     while True:
1372         i = find_token(document.body, "\\begin_inset Info", i)
1373         if i == -1:
1374             return
1375         j = find_end_of_inset(document.body, i + 1)
1376         if j == -1:
1377             document.warning("Malformed LyX document: Could not find end of Info inset.")
1378             i = i + 1
1379             continue
1380         tp = find_token(document.body, 'type', i, j)
1381         tpv = get_quoted_value(document.body, "type", tp)
1382         if tpv != "lyxinfo":
1383             i = i + 1
1384             continue
1385         arg = find_token(document.body, 'arg', i, j)
1386         argv = get_quoted_value(document.body, "arg", arg)
1387         if argv != "layoutformat":
1388             i = i + 1
1389             continue
1390         # hardcoded for now
1391         document.body[i : j+1] = "69"
1392         i = i + 1
1393
1394
1395 def convert_hebrew_parentheses(document):
1396     " Don't reverse parentheses in Hebrew text"
1397     current_language = document.language
1398     for i, line in enumerate(document.body):
1399         if line.startswith('\\lang '):
1400             current_language = line[len('\\lang '):]
1401         elif line.startswith('\\end_layout'):
1402             current_language = document.language
1403         elif current_language == 'hebrew' and not line.startswith('\\'):
1404             document.body[i] = line.replace('(','\x00').replace(')','(').replace('\x00',')')
1405
1406
1407 def revert_hebrew_parentheses(document):
1408     " Store parentheses in Hebrew text reversed"
1409     # This only exists to keep the convert/revert naming convention
1410     convert_hebrew_parentheses(document)
1411
1412
1413 def revert_malayalam(document):
1414     " Set the document language to English but assure Malayalam output "
1415
1416     revert_language(document, "malayalam", "", "malayalam")
1417
1418
1419 def revert_soul(document):
1420     " Revert soul module flex insets to ERT "
1421
1422     flexes = ["Spaceletters", "Strikethrough", "Underline", "Highlight", "Capitalize"]
1423
1424     for flex in flexes:
1425         i = find_token(document.body, "\\begin_inset Flex %s" % flex, 0)
1426         if i != -1:
1427             add_to_preamble(document, ["\\usepackage{soul}"])
1428             break
1429     i = find_token(document.body, "\\begin_inset Flex Highlight", 0)
1430     if i != -1:
1431         add_to_preamble(document, ["\\usepackage{color}"])
1432     
1433     revert_flex_inset(document.body, "Spaceletters", "\\so")
1434     revert_flex_inset(document.body, "Strikethrough", "\\st")
1435     revert_flex_inset(document.body, "Underline", "\\ul")
1436     revert_flex_inset(document.body, "Highlight", "\\hl")
1437     revert_flex_inset(document.body, "Capitalize", "\\caps")
1438
1439
1440 def revert_tablestyle(document):
1441     " Remove tablestyle params "
1442
1443     i = 0
1444     i = find_token(document.header, "\\tablestyle", 0)
1445     if i != -1:
1446         del document.header[i]
1447
1448
1449 def revert_bibfileencodings(document):
1450     " Revert individual Biblatex bibliography encodings "
1451
1452     # Get cite engine
1453     engine = "basic"
1454     i = find_token(document.header, "\\cite_engine", 0)
1455     if i == -1:
1456         document.warning("Malformed document! Missing \\cite_engine")
1457     else:
1458         engine = get_value(document.header, "\\cite_engine", i)
1459
1460     # Check if biblatex
1461     biblatex = False
1462     if engine in ["biblatex", "biblatex-natbib"]:
1463         biblatex = True
1464
1465     # Map lyx to latex encoding names
1466     encodings = {
1467         "utf8" : "utf8",
1468         "utf8x" : "utf8x",
1469         "armscii8" : "armscii8",
1470         "iso8859-1" : "latin1",
1471         "iso8859-2" : "latin2",
1472         "iso8859-3" : "latin3",
1473         "iso8859-4" : "latin4",
1474         "iso8859-5" : "iso88595",
1475         "iso8859-6" : "8859-6",
1476         "iso8859-7" : "iso-8859-7",
1477         "iso8859-8" : "8859-8",
1478         "iso8859-9" : "latin5",
1479         "iso8859-13" : "latin7",
1480         "iso8859-15" : "latin9",
1481         "iso8859-16" : "latin10",
1482         "applemac" : "applemac",
1483         "cp437" : "cp437",
1484         "cp437de" : "cp437de",
1485         "cp850" : "cp850",
1486         "cp852" : "cp852",
1487         "cp855" : "cp855",
1488         "cp858" : "cp858",
1489         "cp862" : "cp862",
1490         "cp865" : "cp865",
1491         "cp866" : "cp866",
1492         "cp1250" : "cp1250",
1493         "cp1251" : "cp1251",
1494         "cp1252" : "cp1252",
1495         "cp1255" : "cp1255",
1496         "cp1256" : "cp1256",
1497         "cp1257" : "cp1257",
1498         "koi8-r" : "koi8-r",
1499         "koi8-u" : "koi8-u",
1500         "pt154" : "pt154",
1501         "utf8-platex" : "utf8",
1502         "ascii" : "ascii"
1503     }
1504
1505     i = 0
1506     bibresources = []
1507     while (True):
1508         i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
1509         if i == -1:
1510             break
1511         j = find_end_of_inset(document.body, i)
1512         if j == -1:
1513             document.warning("Can't find end of bibtex inset at line %d!!" %(i))
1514             i += 1
1515             continue
1516         encodings = get_quoted_value(document.body, "file_encodings", i, j)
1517         if not encodings:
1518             i += 1
1519             continue
1520         bibfiles = get_quoted_value(document.body, "bibfiles", i, j).split(",")
1521         opts = get_quoted_value(document.body, "biblatexopts", i, j)
1522         if len(bibfiles) == 0:
1523             document.warning("Bibtex inset at line %d does not have a bibfile!" %(i))
1524         # remove encoding line
1525         k = find_token(document.body, "file_encodings", i, j)
1526         if k != -1:
1527             del document.body[k]
1528         # Re-find inset end line
1529         j = find_end_of_inset(document.body, i)
1530         if biblatex:
1531             enclist = encodings.split("\t")
1532             encmap = dict()
1533             for pp in enclist:
1534                 ppp = pp.split(" ", 1)
1535                 encmap[ppp[0]] = ppp[1]
1536             for bib in bibfiles:
1537                 pr = "\\addbibresource"
1538                 if bib in encmap.keys():
1539                     pr += "[bibencoding=" + encmap[bib] + "]"
1540                 pr += "{" + bib + "}"
1541                 add_to_preamble(document, [pr])
1542             # Insert ERT \\printbibliography and wrap bibtex inset to a Note
1543             pcmd = "printbibliography"
1544             if opts:
1545                 pcmd += "[" + opts + "]"
1546             repl = ["\\begin_inset ERT", "status open", "", "\\begin_layout Plain Layout",\
1547                     "", "", "\\backslash", pcmd, "\\end_layout", "", "\\end_inset", "", "",\
1548                     "\\end_layout", "", "\\begin_layout Standard", "\\begin_inset Note Note",\
1549                     "status open", "", "\\begin_layout Plain Layout" ]
1550             repl += document.body[i:j+1]
1551             repl += ["", "\\end_layout", "", "\\end_inset", "", ""]
1552             document.body[i:j+1] = repl
1553             j += 27
1554
1555         i = j + 1
1556
1557
1558
1559 ##
1560 # Conversion hub
1561 #
1562
1563 supported_versions = ["2.4.0", "2.4"]
1564 convert = [
1565            [545, [convert_lst_literalparam]],
1566            [546, []],
1567            [547, []],
1568            [548, []],
1569            [549, []],
1570            [550, [convert_fontenc]],
1571            [551, []],
1572            [552, []],
1573            [553, []],
1574            [554, []],
1575            [555, []],
1576            [556, []],
1577            [557, [convert_vcsinfo]],
1578            [558, [removeFrontMatterStyles]],
1579            [559, []],
1580            [560, []],
1581            [561, [convert_latexFonts]], # Handle dejavu, ibmplex fonts in GUI
1582            [562, []],
1583            [563, []],
1584            [564, []],
1585            [565, [convert_AdobeFonts]], # Handle adobe fonts in GUI
1586            [566, [convert_hebrew_parentheses]],
1587            [567, []],
1588            [568, []],
1589            [569, []],
1590            [570, []]
1591           ]
1592
1593 revert =  [
1594            [569, [revert_bibfileencodings]],
1595            [568, [revert_tablestyle]],
1596            [567, [revert_soul]],
1597            [566, [revert_malayalam]],
1598            [565, [revert_hebrew_parentheses]],
1599            [564, [revert_AdobeFonts]],
1600            [563, [revert_lformatinfo]],
1601            [562, [revert_listpargs]],
1602            [561, [revert_l7ninfo]],
1603            [560, [revert_latexFonts]], # Handle dejavu, ibmplex fonts in user preamble
1604            [559, [revert_timeinfo, revert_namenoextinfo]],
1605            [558, [revert_dateinfo]],
1606            [557, [addFrontMatterStyles]],
1607            [556, [revert_vcsinfo]],
1608            [555, [revert_bibencoding]],
1609            [554, [revert_vcolumns]],
1610            [553, [revert_stretchcolumn]],
1611            [552, [revert_tuftecite]],
1612            [551, [revert_floatpclass, revert_floatalignment]],
1613            [550, [revert_nospellcheck]],
1614            [549, [revert_fontenc]],
1615            [548, []],# dummy format change
1616            [547, [revert_lscape]],
1617            [546, [revert_xcharter]],
1618            [545, [revert_paratype]],
1619            [544, [revert_lst_literalparam]]
1620           ]
1621
1622
1623 if __name__ == "__main__":
1624     pass