1 # -*- coding: utf-8 -*-
2 # This file is part of lyx2lyx
3 # Copyright (C) 2018 The LyX team
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.
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.
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.
19 """ Convert files to the file format generated by lyx 2.4"""
25 from datetime import (datetime, date, time)
27 # Uncomment only what you need to import, please.
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
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
44 ####################################################################
45 # Private helper functions
47 def add_preamble_fonts(document, fontmap):
48 " Add collected font-packages with their option to user-preamble"
51 if len(fontmap[pkg]) > 0:
52 xoption = "[" + ",".join(fontmap[pkg]) + "]"
55 preamble = "\\usepackage" + xoption + "{%s}" % pkg
56 add_to_preamble(document, [preamble])
59 def createkey(pkg, options):
61 return pkg + ':' + "-".join(options)
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'
72 self.pkgkey = None # key into pkg2fontmap
75 self.pkgkey = createkey(self.package, self.options)
79 self.font2pkgmap = dict()
80 self.pkg2fontmap = dict()
81 self.pkginmap = dict() # defines, if a map for package exists
83 def expandFontMapping(self, font_list, font_type, scale_type, pkg, scaleopt = None):
84 " Expand fontinfo mapping"
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)
96 fe.fonttype = font_type
97 fe.scaletype = scale_type
100 fe.fontname = font_name
102 fe.scaleopt = scaleopt
104 fe.package = font_name
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
116 def getfontname(self, pkg, options):
118 pkgkey = createkey(pkg, options)
119 if not pkgkey in self.pkg2fontmap:
121 fontname = self.pkg2fontmap[pkgkey]
122 if not fontname in self.font2pkgmap:
123 document.error("Something is wrong in pkgname+options <-> fontname mapping")
125 if pkgkey == self.font2pkgmap[fontname].pkgkey:
129 def createFontMapping(fontlist):
130 # Create info for known fonts for the use in
131 # convert_latexFonts() and
132 # revert_latexFonts()
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
139 for font in fontlist:
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")
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")
162 fm.expandFontMapping(['NotoSerifRegular,regular', 'NotoSerifMedium,medium',
163 'NotoSerifThin,thin', 'NotoSerifLight,light',
164 'NotoSerifExtralight,extralight'],
165 "roman", None, "noto-serif")
166 fm.expandFontMapping(['NotoSansRegular,regular', 'NotoSansMedium,medium',
167 'NotoSansThin,thin', 'NotoSansLight,light',
168 'NotoSansExtralight,extralight'],
169 "sans", "sf", "noto-sans", "scaled")
170 fm.expandFontMapping(['NotoMonoRegular'], "typewriter", "tt", "noto-mono", "scaled")
173 def convert_fonts(document, fm):
174 " Handle font definition to LaTeX "
176 rpkg = re.compile(r'^\\usepackage(\[([^\]]*)\])?\{([^\}]+)\}')
177 rscaleopt = re.compile(r'^scaled?=(.*)')
180 while i < len(document.preamble):
181 i = find_re(document.preamble, rpkg, i)
184 mo = rpkg.search(document.preamble[i])
185 if mo == None or mo.group(2) == None:
188 options = mo.group(2).replace(' ', '').split(",")
192 while o < len(options):
193 mo = rscaleopt.search(options[o])
201 if not pkg in fm.pkginmap:
205 fn = fm.getfontname(pkg, options)
209 del document.preamble[i]
210 fontinfo = fm.font2pkgmap[fn]
211 if fontinfo.scaletype == None:
214 fontscale = "\\font_" + fontinfo.scaletype + "_scale"
215 fontinfo.scaleval = oscale
217 if i > 0 and document.preamble[i-1] == "% Added by lyx2lyx":
218 del document.preamble[i-1]
219 if fontscale != None:
220 j = find_token(document.header, fontscale, 0)
222 val = get_value(document.header, fontscale, j)
226 scale = "%03d" % int(float(oscale) * 100)
227 document.header[j] = fontscale + " " + scale + " " + vals[1]
228 ft = "\\font_" + fontinfo.fonttype
229 j = find_token(document.header, ft, 0)
231 val = get_value(document.header, ft, j)
232 words = val.split() # ! splits also values like '"DejaVu Sans"'
233 words[0] = '"' + fn + '"'
234 document.header[j] = ft + ' ' + ' '.join(words)
236 def revert_fonts(document, fm, fontmap):
237 " Revert native font definition to LaTeX "
238 # fonlist := list of fonts created from the same package
239 # Empty package means that the font-name is the same as the package-name
240 # fontmap (key = package, val += found options) will be filled
241 # and used later in add_preamble_fonts() to be added to user-preamble
243 rfontscale = re.compile(r'^\s*(\\font_(roman|sans|typewriter|math))\s+')
244 rscales = re.compile(r'^\s*(\d+)\s+(\d+)')
246 while i < len(document.header):
247 i = find_re(document.header, rfontscale, i)
250 mo = rfontscale.search(document.header[i])
254 ft = mo.group(1) # 'roman', 'sans', 'typewriter', 'math'
255 val = get_value(document.header, ft, i)
256 words = val.split(' ') # ! splits also values like '"DejaVu Sans"'
257 font = words[0].strip('"') # TeX font name has no whitespace
258 if not font in fm.font2pkgmap:
261 fontinfo = fm.font2pkgmap[font]
262 val = fontinfo.package
263 if not val in fontmap:
265 words[0] = '"default"'
266 document.header[i] = ft + ' ' + ' '.join(words)
267 if fontinfo.scaleopt != None:
268 xval = get_value(document.header, "\\font_" + fontinfo.scaletype + "_scale", 0)
269 mo = rscales.search(xval)
274 # set correct scale option
275 fontmap[val].extend([fontinfo.scaleopt + "=" + format(float(xval1) / 100, '.2f')])
276 if len(fontinfo.options) > 0:
277 fontmap[val].extend(fontinfo.options)
280 ###############################################################################
282 ### Conversion and reversion routines
284 ###############################################################################
286 def convert_inputencoding_namechange(document):
287 " Rename inputencoding settings. "
288 i = find_token(document.header, "\\inputencoding", 0)
291 s = document.header[i].replace("auto", "auto-legacy")
292 document.header[i] = s.replace("default", "auto-legacy-plain")
294 def revert_inputencoding_namechange(document):
295 " Rename inputencoding settings. "
296 i = find_token(document.header, "\\inputencoding", 0)
299 s = document.header[i].replace("auto-legacy-plain", "default")
300 document.header[i] = s.replace("auto-legacy", "auto")
302 def convert_notoFonts(document):
303 " Handle Noto fonts definition to LaTeX "
305 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
306 fm = createFontMapping(['Noto'])
307 convert_fonts(document, fm)
309 def revert_notoFonts(document):
310 " Revert native Noto font definition to LaTeX "
312 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
314 fm = createFontMapping(['Noto'])
315 revert_fonts(document, fm, fontmap)
316 add_preamble_fonts(document, fontmap)
318 def convert_latexFonts(document):
319 " Handle DejaVu and IBMPlex fonts definition to LaTeX "
321 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
322 fm = createFontMapping(['DejaVu', 'IBM'])
323 convert_fonts(document, fm)
325 def revert_latexFonts(document):
326 " Revert native DejaVu font definition to LaTeX "
328 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
330 fm = createFontMapping(['DejaVu', 'IBM'])
331 revert_fonts(document, fm, fontmap)
332 add_preamble_fonts(document, fontmap)
334 def convert_AdobeFonts(document):
335 " Handle DejaVu and IBMPlex fonts definition to LaTeX "
337 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
338 fm = createFontMapping(['Adobe'])
339 convert_fonts(document, fm)
341 def revert_AdobeFonts(document):
342 " Revert native DejaVu font definition to LaTeX "
344 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
346 fm = createFontMapping(['Adobe'])
347 revert_fonts(document, fm, fontmap)
348 add_preamble_fonts(document, fontmap)
350 def removeFrontMatterStyles(document):
351 " Remove styles Begin/EndFrontmatter"
353 layouts = ['BeginFrontmatter', 'EndFrontmatter']
354 for layout in layouts:
357 i = find_token(document.body, '\\begin_layout ' + layout, i)
360 j = find_end_of_layout(document.body, i)
362 document.warning("Malformed LyX document: Can't find end of layout at line %d" % i)
365 while i > 0 and document.body[i-1].strip() == '':
367 while document.body[j+1].strip() == '':
369 document.body[i:j+1] = ['']
371 def addFrontMatterStyles(document):
372 " Use styles Begin/EndFrontmatter for elsarticle"
374 def insertFrontmatter(prefix, line):
376 while above > 0 and document.body[above-1].strip() == '':
379 while document.body[below].strip() == '':
381 document.body[above:below] = ['', '\\begin_layout ' + prefix + 'Frontmatter',
382 '\\begin_inset Note Note',
384 '\\begin_layout Plain Layout',
387 '\\end_inset', '', '',
390 if document.textclass == "elsarticle":
391 layouts = ['Title', 'Title footnote', 'Author', 'Author footnote',
392 'Corresponding author', 'Address', 'Email', 'Abstract', 'Keywords']
395 for layout in layouts:
398 i = find_token(document.body, '\\begin_layout ' + layout, i)
401 k = find_end_of_layout(document.body, i)
403 document.warning("Malformed LyX document: Can't find end of layout at line %d" % i)
406 if first == -1 or i < first:
408 if last == -1 or last <= k:
413 insertFrontmatter('End', last)
414 insertFrontmatter('Begin', first)
416 def convert_lst_literalparam(document):
417 " Add param literal to include inset "
421 i = find_token(document.body, '\\begin_inset CommandInset include', i)
424 j = find_end_of_inset(document.body, i)
426 document.warning("Malformed LyX document: Can't find end of command inset at line %d" % i)
429 while i < j and document.body[i].strip() != '':
431 document.body.insert(i, "literal \"true\"")
434 def revert_lst_literalparam(document):
435 " Remove param literal from include inset "
439 i = find_token(document.body, '\\begin_inset CommandInset include', i)
442 j = find_end_of_inset(document.body, i)
444 document.warning("Malformed LyX document: Can't find end of include inset at line %d" % i)
447 k = find_token(document.body, 'literal', i, j)
454 def revert_paratype(document):
455 " Revert ParaType font definitions to LaTeX "
457 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
459 i1 = find_token(document.header, "\\font_roman \"PTSerif-TLF\"", 0)
460 i2 = find_token(document.header, "\\font_sans \"default\"", 0)
461 i3 = find_token(document.header, "\\font_typewriter \"default\"", 0)
462 j = find_token(document.header, "\\font_sans \"PTSans-TLF\"", 0)
463 sfval = get_value(document.header, "\\font_sf_scale", 0)
468 sfoption = "scaled=" + format(float(sfval) / 100, '.2f')
469 k = find_token(document.header, "\\font_typewriter \"PTMono-TLF\"", 0)
470 ttval = get_value(document.header, "\\font_tt_scale", 0)
475 ttoption = "scaled=" + format(float(ttval) / 100, '.2f')
476 if i1 != -1 and i2 != -1 and i3!= -1:
477 add_to_preamble(document, ["\\usepackage{paratype}"])
480 add_to_preamble(document, ["\\usepackage{PTSerif}"])
481 document.header[i1] = document.header[i1].replace("PTSerif-TLF", "default")
484 add_to_preamble(document, ["\\usepackage[" + sfoption + "]{PTSans}"])
486 add_to_preamble(document, ["\\usepackage{PTSans}"])
487 document.header[j] = document.header[j].replace("PTSans-TLF", "default")
490 add_to_preamble(document, ["\\usepackage[" + ttoption + "]{PTMono}"])
492 add_to_preamble(document, ["\\usepackage{PTMono}"])
493 document.header[k] = document.header[k].replace("PTMono-TLF", "default")
496 def revert_xcharter(document):
497 " Revert XCharter font definitions to LaTeX "
499 i = find_token(document.header, "\\font_roman \"xcharter\"", 0)
503 # replace unsupported font setting
504 document.header[i] = document.header[i].replace("xcharter", "default")
505 # no need for preamble code with system fonts
506 if get_bool_value(document.header, "\\use_non_tex_fonts"):
509 # transfer old style figures setting to package options
510 j = find_token(document.header, "\\font_osf true")
513 document.header[j] = "\\font_osf false"
517 add_to_preamble(document, ["\\usepackage%s{XCharter}"%options])
520 def revert_lscape(document):
521 " Reverts the landscape environment (Landscape module) to TeX-code "
523 if not "landscape" in document.get_module_list():
528 i = find_token(document.body, "\\begin_inset Flex Landscape", i)
531 j = find_end_of_inset(document.body, i)
533 document.warning("Malformed LyX document: Can't find end of Landscape inset")
537 if document.body[i] == "\\begin_inset Flex Landscape (Floating)":
538 document.body[j - 2 : j + 1] = put_cmd_in_ert("\\end{landscape}}")
539 document.body[i : i + 4] = put_cmd_in_ert("\\afterpage{\\begin{landscape}")
540 add_to_preamble(document, ["\\usepackage{afterpage}"])
542 document.body[j - 2 : j + 1] = put_cmd_in_ert("\\end{landscape}")
543 document.body[i : i + 4] = put_cmd_in_ert("\\begin{landscape}")
545 add_to_preamble(document, ["\\usepackage{pdflscape}"])
549 def convert_fontenc(document):
550 " Convert default fontenc setting "
552 i = find_token(document.header, "\\fontencoding global", 0)
556 document.header[i] = document.header[i].replace("global", "auto")
559 def revert_fontenc(document):
560 " Revert default fontenc setting "
562 i = find_token(document.header, "\\fontencoding auto", 0)
566 document.header[i] = document.header[i].replace("auto", "global")
569 def revert_nospellcheck(document):
570 " Remove nospellcheck font info param "
574 i = find_token(document.body, '\\nospellcheck', i)
580 def revert_floatpclass(document):
581 " Remove float placement params 'document' and 'class' "
584 i = find_token(document.header, "\\float_placement class", 0)
586 del document.header[i]
590 i = find_token(document.body, '\\begin_inset Float', i)
593 j = find_end_of_inset(document.body, i)
594 k = find_token(document.body, 'placement class', i, i + 2)
596 k = find_token(document.body, 'placement document', i, i + 2)
604 def revert_floatalignment(document):
605 " Remove float alignment params "
608 i = find_token(document.header, "\\float_alignment", 0)
611 galignment = get_value(document.header, "\\float_alignment", i)
612 del document.header[i]
616 i = find_token(document.body, '\\begin_inset Float', i)
619 j = find_end_of_inset(document.body, i)
621 document.warning("Malformed LyX document: Can't find end of inset at line " + str(i))
623 k = find_token(document.body, 'alignment', i, i + 4)
627 alignment = get_value(document.body, "alignment", k)
628 if alignment == "document":
629 alignment = galignment
631 l = find_token(document.body, "\\begin_layout Plain Layout", i, j)
633 document.warning("Can't find float layout!")
637 if alignment == "left":
638 alcmd = put_cmd_in_ert("\\raggedright{}")
639 elif alignment == "center":
640 alcmd = put_cmd_in_ert("\\centering{}")
641 elif alignment == "right":
642 alcmd = put_cmd_in_ert("\\raggedleft{}")
644 document.body[l+1:l+1] = alcmd
648 def revert_tuftecite(document):
649 " Revert \cite commands in tufte classes "
651 tufte = ["tufte-book", "tufte-handout"]
652 if document.textclass not in tufte:
657 i = find_token(document.body, "\\begin_inset CommandInset citation", i)
660 j = find_end_of_inset(document.body, i)
662 document.warning("Can't find end of citation inset at line %d!!" %(i))
665 k = find_token(document.body, "LatexCommand", i, j)
667 document.warning("Can't find LatexCommand for citation inset at line %d!" %(i))
670 cmd = get_value(document.body, "LatexCommand", k)
674 pre = get_quoted_value(document.body, "before", i, j)
675 post = get_quoted_value(document.body, "after", i, j)
676 key = get_quoted_value(document.body, "key", i, j)
678 document.warning("Citation inset at line %d does not have a key!" %(i))
680 # Replace command with ERT
683 res += "[" + pre + "]"
685 res += "[" + post + "]"
688 res += "{" + key + "}"
689 document.body[i:j+1] = put_cmd_in_ert([res])
693 def revert_stretchcolumn(document):
694 " We remove the column varwidth flags or everything else will become a mess. "
697 i = find_token(document.body, "\\begin_inset Tabular", i)
700 j = find_end_of_inset(document.body, i + 1)
702 document.warning("Malformed LyX document: Could not find end of tabular.")
704 for k in range(i, j):
705 if re.search('^<column.*varwidth="[^"]+".*>$', document.body[k]):
706 document.warning("Converting 'tabularx'/'xltabular' table to normal table.")
707 document.body[k] = document.body[k].replace(' varwidth="true"', '')
711 def revert_vcolumns(document):
712 " Revert standard columns with line breaks etc. "
718 i = find_token(document.body, "\\begin_inset Tabular", i)
721 j = find_end_of_inset(document.body, i)
723 document.warning("Malformed LyX document: Could not find end of tabular.")
727 # Collect necessary column information
729 nrows = int(document.body[i+1].split('"')[3])
730 ncols = int(document.body[i+1].split('"')[5])
732 for k in range(ncols):
733 m = find_token(document.body, "<column", m)
734 width = get_option_value(document.body[m], 'width')
735 varwidth = get_option_value(document.body[m], 'varwidth')
736 alignment = get_option_value(document.body[m], 'alignment')
737 special = get_option_value(document.body[m], 'special')
738 col_info.append([width, varwidth, alignment, special, m])
743 for row in range(nrows):
744 for col in range(ncols):
745 m = find_token(document.body, "<cell", m)
746 multicolumn = get_option_value(document.body[m], 'multicolumn')
747 multirow = get_option_value(document.body[m], 'multirow')
748 width = get_option_value(document.body[m], 'width')
749 rotate = get_option_value(document.body[m], 'rotate')
750 # Check for: linebreaks, multipars, non-standard environments
752 endcell = find_token(document.body, "</cell>", begcell)
754 if find_token(document.body, "\\begin_inset Newline", begcell, endcell) != -1:
756 elif count_pars_in_inset(document.body, begcell + 2) > 1:
758 elif get_value(document.body, "\\begin_layout", begcell) != "Plain Layout":
760 if vcand and rotate == "" and ((multicolumn == "" and multirow == "") or width == ""):
761 if col_info[col][0] == "" and col_info[col][1] == "" and col_info[col][3] == "":
763 alignment = col_info[col][2]
764 col_line = col_info[col][4]
766 if alignment == "center":
767 vval = ">{\\centering}"
768 elif alignment == "left":
769 vval = ">{\\raggedright}"
770 elif alignment == "right":
771 vval = ">{\\raggedleft}"
774 vval += "V{\\linewidth}"
776 document.body[col_line] = document.body[col_line][:-1] + " special=\"" + vval + "\">"
777 # ERT newlines and linebreaks (since LyX < 2.4 automatically inserts parboxes
778 # with newlines, and we do not want that)
780 endcell = find_token(document.body, "</cell>", begcell)
782 nl = find_token(document.body, "\\begin_inset Newline newline", begcell, endcell)
784 nl = find_token(document.body, "\\begin_inset Newline linebreak", begcell, endcell)
788 nle = find_end_of_inset(document.body, nl)
789 del(document.body[nle:nle+1])
791 document.body[nl:nl+1] = put_cmd_in_ert("\\linebreak{}")
793 document.body[nl:nl+1] = put_cmd_in_ert("\\\\")
799 if needarray == True:
800 add_to_preamble(document, ["\\usepackage{array}"])
801 if needvarwidth == True:
802 add_to_preamble(document, ["\\usepackage{varwidth}"])
805 def revert_bibencoding(document):
806 " Revert bibliography encoding "
810 i = find_token(document.header, "\\cite_engine", 0)
812 document.warning("Malformed document! Missing \\cite_engine")
814 engine = get_value(document.header, "\\cite_engine", i)
818 if engine in ["biblatex", "biblatex-natbib"]:
821 # Map lyx to latex encoding names
825 "armscii8" : "armscii8",
826 "iso8859-1" : "latin1",
827 "iso8859-2" : "latin2",
828 "iso8859-3" : "latin3",
829 "iso8859-4" : "latin4",
830 "iso8859-5" : "iso88595",
831 "iso8859-6" : "8859-6",
832 "iso8859-7" : "iso-8859-7",
833 "iso8859-8" : "8859-8",
834 "iso8859-9" : "latin5",
835 "iso8859-13" : "latin7",
836 "iso8859-15" : "latin9",
837 "iso8859-16" : "latin10",
838 "applemac" : "applemac",
840 "cp437de" : "cp437de",
857 "utf8-platex" : "utf8",
864 i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
867 j = find_end_of_inset(document.body, i)
869 document.warning("Can't find end of bibtex inset at line %d!!" %(i))
872 encoding = get_quoted_value(document.body, "encoding", i, j)
876 # remove encoding line
877 k = find_token(document.body, "encoding", i, j)
880 if encoding == "default":
883 # Re-find inset end line
884 j = find_end_of_inset(document.body, i)
887 h = find_token(document.header, "\\biblio_options", 0)
889 biblio_options = get_value(document.header, "\\biblio_options", h)
890 if not "bibencoding" in biblio_options:
891 document.header[h] += ",bibencoding=%s" % encodings[encoding]
893 bs = find_token(document.header, "\\biblatex_bibstyle", 0)
895 # this should not happen
896 document.warning("Malformed LyX document! No \\biblatex_bibstyle header found!")
898 document.header[bs-1 : bs-1] = ["\\biblio_options bibencoding=" + encodings[encoding]]
900 document.body[j+1:j+1] = put_cmd_in_ert("\\egroup")
901 document.body[i:i] = put_cmd_in_ert("\\bgroup\\inputencoding{" + encodings[encoding] + "}")
907 def convert_vcsinfo(document):
908 " Separate vcs Info inset from buffer Info inset. "
911 "vcs-revision" : "revision",
912 "vcs-tree-revision" : "tree-revision",
913 "vcs-author" : "author",
919 i = find_token(document.body, "\\begin_inset Info", i)
922 j = find_end_of_inset(document.body, i + 1)
924 document.warning("Malformed LyX document: Could not find end of Info inset.")
927 tp = find_token(document.body, 'type', i, j)
928 tpv = get_quoted_value(document.body, "type", tp)
932 arg = find_token(document.body, 'arg', i, j)
933 argv = get_quoted_value(document.body, "arg", arg)
934 if argv not in list(types.keys()):
937 document.body[tp] = "type \"vcs\""
938 document.body[arg] = "arg \"" + types[argv] + "\""
942 def revert_vcsinfo(document):
943 " Merge vcs Info inset to buffer Info inset. "
945 args = ["revision", "tree-revision", "author", "time", "date" ]
948 i = find_token(document.body, "\\begin_inset Info", i)
951 j = find_end_of_inset(document.body, i + 1)
953 document.warning("Malformed LyX document: Could not find end of Info inset.")
956 tp = find_token(document.body, 'type', i, j)
957 tpv = get_quoted_value(document.body, "type", tp)
961 arg = find_token(document.body, 'arg', i, j)
962 argv = get_quoted_value(document.body, "arg", arg)
964 document.warning("Malformed Info inset. Invalid vcs arg.")
967 document.body[tp] = "type \"buffer\""
968 document.body[arg] = "arg \"vcs-" + argv + "\""
972 def revert_dateinfo(document):
973 " Revert date info insets to static text. "
975 # FIXME This currently only considers the main language and uses the system locale
976 # Ideally, it should honor context languages and switch the locale accordingly.
978 # The date formats for each language using strftime syntax:
979 # long, short, loclong, locmedium, locshort
981 "afrikaans" : ["%A, %d %B %Y", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y/%m/%d"],
982 "albanian" : ["%A, %d %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
983 "american" : ["%A, %B %d, %Y", "%m/%d/%y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
984 "amharic" : ["%A ፣%d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
985 "ancientgreek" : ["%A, %d %B %Y", "%d %b %Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
986 "arabic_arabi" : ["%A، %d %B، %Y", "%d/%m/%Y", "%d %B، %Y", "%d/%m/%Y", "%d/%m/%Y"],
987 "arabic_arabtex" : ["%A، %d %B، %Y", "%d/%m/%Y", "%d %B، %Y", "%d/%m/%Y", "%d/%m/%Y"],
988 "armenian" : ["%Y թ. %B %d, %A", "%d.%m.%y", "%d %B، %Y", "%d %b، %Y", "%d/%m/%Y"],
989 "asturian" : ["%A, %d %B de %Y", "%d/%m/%y", "%d de %B de %Y", "%d %b %Y", "%d/%m/%Y"],
990 "australian" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
991 "austrian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
992 "bahasa" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
993 "bahasam" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
994 "basque" : ["%Y(e)ko %B %d, %A", "%y/%m/%d", "%Y %B %d", "%Y %b %d", "%Y/%m/%d"],
995 "belarusian" : ["%A, %d %B %Y г.", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
996 "bosnian" : ["%A, %d. %B %Y.", "%d.%m.%y.", "%d. %B %Y", "%d. %b %Y", "%Y-%m-%d"],
997 "brazilian" : ["%A, %d de %B de %Y", "%d/%m/%Y", "%d de %B de %Y", "%d de %b de %Y", "%d/%m/%Y"],
998 "breton" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
999 "british" : ["%A, %d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1000 "bulgarian" : ["%A, %d %B %Y г.", "%d.%m.%y г.", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
1001 "canadian" : ["%A, %B %d, %Y", "%Y-%m-%d", "%B %d, %Y", "%d %b %Y", "%Y-%m-%d"],
1002 "canadien" : ["%A %d %B %Y", "%y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
1003 "catalan" : ["%A, %d %B de %Y", "%d/%m/%y", "%d / %B / %Y", "%d / %b / %Y", "%d/%m/%Y"],
1004 "chinese-simplified" : ["%Y年%m月%d日%A", "%Y/%m/%d", "%Y年%m月%d日", "%Y-%m-%d", "%y-%m-%d"],
1005 "chinese-traditional" : ["%Y年%m月%d日 %A", "%Y/%m/%d", "%Y年%m月%d日", "%Y年%m月%d日", "%y年%m月%d日"],
1006 "coptic" : ["%A, %d %B %Y", "%d %b %Y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1007 "croatian" : ["%A, %d. %B %Y.", "%d. %m. %Y.", "%d. %B %Y.", "%d. %b. %Y.", "%d.%m.%Y."],
1008 "czech" : ["%A %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b. %Y", "%d.%m.%Y"],
1009 "danish" : ["%A den %d. %B %Y", "%d/%m/%Y", "%d. %B %Y", "%d. %b %Y", "%d/%m/%Y"],
1010 "divehi" : ["%Y %B %d, %A", "%Y-%m-%d", "%Y %B %d", "%Y %b %d", "%d/%m/%Y"],
1011 "dutch" : ["%A %d %B %Y", "%d-%m-%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1012 "english" : ["%A, %B %d, %Y", "%m/%d/%y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1013 "esperanto" : ["%A, %d %B %Y", "%d %b %Y", "la %d de %B %Y", "la %d de %b %Y", "%m/%d/%Y"],
1014 "estonian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1015 "farsi" : ["%A %d %B %Y", "%Y/%m/%d", "%d %B %Y", "%d %b %Y", "%Y/%m/%d"],
1016 "finnish" : ["%A %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1017 "french" : ["%A %d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1018 "friulan" : ["%A %d di %B dal %Y", "%d/%m/%y", "%d di %B dal %Y", "%d di %b dal %Y", "%d/%m/%Y"],
1019 "galician" : ["%A, %d de %B de %Y", "%d/%m/%y", "%d de %B de %Y", "%d de %b de %Y", "%d/%m/%Y"],
1020 "georgian" : ["%A, %d %B, %Y", "%d.%m.%y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1021 "german" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1022 "german-ch" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1023 "german-ch-old" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1024 "greek" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1025 "hebrew" : ["%A, %d ב%B %Y", "%d.%m.%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1026 "hindi" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1027 "icelandic" : ["%A, %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1028 "interlingua" : ["%Y %B %d, %A", "%Y-%m-%d", "le %d de %B %Y", "le %d de %b %Y", "%Y-%m-%d"],
1029 "irish" : ["%A %d %B %Y", "%d/%m/%Y", "%d. %B %Y", "%d. %b %Y", "%d/%m/%Y"],
1030 "italian" : ["%A %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d/%b/%Y", "%d/%m/%Y"],
1031 "japanese" : ["%Y年%m月%d日%A", "%Y/%m/%d", "%Y年%m月%d日", "%Y/%m/%d", "%y/%m/%d"],
1032 "japanese-cjk" : ["%Y年%m月%d日%A", "%Y/%m/%d", "%Y年%m月%d日", "%Y/%m/%d", "%y/%m/%d"],
1033 "kannada" : ["%A, %B %d, %Y", "%d/%m/%y", "%d %B %Y", "%d %B %Y", "%d-%m-%Y"],
1034 "kazakh" : ["%Y ж. %d %B, %A", "%d.%m.%y", "%d %B %Y", "%d %B %Y", "%Y-%d-%m"],
1035 "khmer" : ["%A %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %B %Y", "%d/%m/%Y"],
1036 "korean" : ["%Y년 %m월 %d일 %A", "%y. %m. %d.", "%Y년 %m월 %d일", "%Y. %m. %d.", "%y. %m. %d."],
1037 "kurmanji" : ["%A, %d %B %Y", "%d %b %Y", "%d. %B %Y", "%d. %m. %Y", "%Y-%m-%d"],
1038 "lao" : ["%A ທີ %d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %B %Y", "%d/%m/%Y"],
1039 "latin" : ["%A, %d %B %Y", "%d %b %Y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1040 "latvian" : ["%A, %Y. gada %d. %B", "%d.%m.%y", "%Y. gada %d. %B", "%Y. gada %d. %b", "%d.%m.%Y"],
1041 "lithuanian" : ["%Y m. %B %d d., %A", "%Y-%m-%d", "%Y m. %B %d d.", "%Y m. %B %d d.", "%Y-%m-%d"],
1042 "lowersorbian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1043 "macedonian" : ["%A, %d %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1044 "magyar" : ["%Y. %B %d., %A", "%Y. %m. %d.", "%Y. %B %d.", "%Y. %b %d.", "%Y.%m.%d."],
1045 "malayalam" : ["%A, %d %B, %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1046 "marathi" : ["%A, %d %B, %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1047 "mongolian" : ["%A, %Y оны %m сарын %d", "%Y-%m-%d", "%Y оны %m сарын %d", "%d-%m-%Y", "%d-%m-%Y"],
1048 "naustrian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1049 "newzealand" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1050 "ngerman" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1051 "norsk" : ["%A %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1052 "nynorsk" : ["%A %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1053 "occitan" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1054 "piedmontese" : ["%A, %d %B %Y", "%d %b %Y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1055 "polish" : ["%A, %d %B %Y", "%d.%m.%Y", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
1056 "polutonikogreek" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1057 "portuguese" : ["%A, %d de %B de %Y", "%d/%m/%y", "%d de %B de %Y", "%d de %b de %Y", "%Y/%m/%d"],
1058 "romanian" : ["%A, %d %B %Y", "%d.%m.%Y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1059 "romansh" : ["%A, ils %d da %B %Y", "%d-%m-%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1060 "russian" : ["%A, %d %B %Y г.", "%d.%m.%Y", "%d %B %Y г.", "%d %b %Y г.", "%d.%m.%Y"],
1061 "samin" : ["%Y %B %d, %A", "%Y-%m-%d", "%B %d. b. %Y", "%b %d. b. %Y", "%d.%m.%Y"],
1062 "sanskrit" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1063 "scottish" : ["%A, %dmh %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1064 "serbian" : ["%A, %d. %B %Y.", "%d.%m.%y.", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1065 "serbian-latin" : ["%A, %d. %B %Y.", "%d.%m.%y.", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1066 "slovak" : ["%A, %d. %B %Y", "%d. %m. %Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1067 "slovene" : ["%A, %d. %B %Y", "%d. %m. %y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1068 "spanish" : ["%A, %d de %B de %Y", "%d/%m/%y", "%d de %B %de %Y", "%d %b %Y", "%d/%m/%Y"],
1069 "spanish-mexico" : ["%A, %d de %B %de %Y", "%d/%m/%y", "%d de %B de %Y", "%d %b %Y", "%d/%m/%Y"],
1070 "swedish" : ["%A %d %B %Y", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
1071 "syriac" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1072 "tamil" : ["%A, %d %B, %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1073 "telugu" : ["%d, %B %Y, %A", "%d-%m-%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1074 "thai" : ["%Aที่ %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1075 "tibetan" : ["%Y %Bའི་ཚེས་%d, %A", "%Y-%m-%d", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1076 "turkish" : ["%d %B %Y %A", "%d.%m.%Y", "%d %B %Y", "%d.%b.%Y", "%d.%m.%Y"],
1077 "turkmen" : ["%d %B %Y %A", "%d.%m.%Y", "%Y ý. %B %d", "%d.%m.%Y ý.", "%d.%m.%y ý."],
1078 "ukrainian" : ["%A, %d %B %Y р.", "%d.%m.%y", "%d %B %Y", "%d %m %Y", "%d.%m.%Y"],
1079 "uppersorbian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1080 "urdu" : ["%A، %d %B، %Y", "%d/%m/%y", "%d %B, %Y", "%d %b %Y", "%d/%m/%Y"],
1081 "vietnamese" : ["%A, %d %B, %Y", "%d/%m/%Y", "%d tháng %B %Y", "%d-%m-%Y", "%d/%m/%Y"],
1082 "welsh" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1085 types = ["date", "fixdate", "moddate" ]
1087 i = find_token(document.header, "\\language", 0)
1089 # this should not happen
1090 document.warning("Malformed LyX document! No \\language header found!")
1092 lang = get_value(document.header, "\\language", i)
1096 i = find_token(document.body, "\\begin_inset Info", i)
1099 j = find_end_of_inset(document.body, i + 1)
1101 document.warning("Malformed LyX document: Could not find end of Info inset.")
1104 tp = find_token(document.body, 'type', i, j)
1105 tpv = get_quoted_value(document.body, "type", tp)
1106 if tpv not in types:
1109 arg = find_token(document.body, 'arg', i, j)
1110 argv = get_quoted_value(document.body, "arg", arg)
1113 if tpv == "fixdate":
1114 datecomps = argv.split('@')
1115 if len(datecomps) > 1:
1117 isodate = datecomps[1]
1118 m = re.search('(\d\d\d\d)-(\d\d)-(\d\d)', isodate)
1120 dte = date(int(m.group(1)), int(m.group(2)), int(m.group(3)))
1121 # FIXME if we had the path to the original document (not the one in the tmp dir),
1122 # we could use the mtime.
1123 # elif tpv == "moddate":
1124 # dte = date.fromtimestamp(os.path.getmtime(document.dir))
1127 result = dte.isodate()
1128 elif argv == "long":
1129 result = dte.strftime(dateformats[lang][0])
1130 elif argv == "short":
1131 result = dte.strftime(dateformats[lang][1])
1132 elif argv == "loclong":
1133 result = dte.strftime(dateformats[lang][2])
1134 elif argv == "locmedium":
1135 result = dte.strftime(dateformats[lang][3])
1136 elif argv == "locshort":
1137 result = dte.strftime(dateformats[lang][4])
1139 fmt = argv.replace("MMMM", "%b").replace("MMM", "%b").replace("MM", "%m").replace("M", "%m")
1140 fmt = fmt.replace("yyyy", "%Y").replace("yy", "%y")
1141 fmt = fmt.replace("dddd", "%A").replace("ddd", "%a").replace("dd", "%d")
1142 fmt = re.sub('[^\'%]d', '%d', fmt)
1143 fmt = fmt.replace("'", "")
1144 result = dte.strftime(fmt)
1145 if sys.version_info < (3,0):
1146 # In Python 2, datetime module works with binary strings,
1147 # our dateformat strings are utf8-encoded:
1148 result = result.decode('utf-8')
1149 document.body[i : j+1] = result
1153 def revert_timeinfo(document):
1154 " Revert time info insets to static text. "
1156 # FIXME This currently only considers the main language and uses the system locale
1157 # Ideally, it should honor context languages and switch the locale accordingly.
1158 # Also, the time object is "naive", i.e., it does not know of timezones (%Z will
1161 # The time formats for each language using strftime syntax:
1164 "afrikaans" : ["%H:%M:%S %Z", "%H:%M"],
1165 "albanian" : ["%I:%M:%S %p, %Z", "%I:%M %p"],
1166 "american" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1167 "amharic" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1168 "ancientgreek" : ["%H:%M:%S %Z", "%H:%M:%S"],
1169 "arabic_arabi" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1170 "arabic_arabtex" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1171 "armenian" : ["%H:%M:%S %Z", "%H:%M"],
1172 "asturian" : ["%H:%M:%S %Z", "%H:%M"],
1173 "australian" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1174 "austrian" : ["%H:%M:%S %Z", "%H:%M"],
1175 "bahasa" : ["%H.%M.%S %Z", "%H.%M"],
1176 "bahasam" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1177 "basque" : ["%H:%M:%S (%Z)", "%H:%M"],
1178 "belarusian" : ["%H:%M:%S, %Z", "%H:%M"],
1179 "bosnian" : ["%H:%M:%S %Z", "%H:%M"],
1180 "brazilian" : ["%H:%M:%S %Z", "%H:%M"],
1181 "breton" : ["%H:%M:%S %Z", "%H:%M"],
1182 "british" : ["%H:%M:%S %Z", "%H:%M"],
1183 "bulgarian" : ["%H:%M:%S %Z", "%H:%M"],
1184 "canadian" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1185 "canadien" : ["%H:%M:%S %Z", "%H h %M"],
1186 "catalan" : ["%H:%M:%S %Z", "%H:%M"],
1187 "chinese-simplified" : ["%Z %p%I:%M:%S", "%p%I:%M"],
1188 "chinese-traditional" : ["%p%I:%M:%S [%Z]", "%p%I:%M"],
1189 "coptic" : ["%H:%M:%S %Z", "%H:%M:%S"],
1190 "croatian" : ["%H:%M:%S (%Z)", "%H:%M"],
1191 "czech" : ["%H:%M:%S %Z", "%H:%M"],
1192 "danish" : ["%H.%M.%S %Z", "%H.%M"],
1193 "divehi" : ["%H:%M:%S %Z", "%H:%M"],
1194 "dutch" : ["%H:%M:%S %Z", "%H:%M"],
1195 "english" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1196 "esperanto" : ["%H:%M:%S %Z", "%H:%M:%S"],
1197 "estonian" : ["%H:%M:%S %Z", "%H:%M"],
1198 "farsi" : ["%H:%M:%S (%Z)", "%H:%M"],
1199 "finnish" : ["%H.%M.%S %Z", "%H.%M"],
1200 "french" : ["%H:%M:%S %Z", "%H:%M"],
1201 "friulan" : ["%H:%M:%S %Z", "%H:%M"],
1202 "galician" : ["%H:%M:%S %Z", "%H:%M"],
1203 "georgian" : ["%H:%M:%S %Z", "%H:%M"],
1204 "german" : ["%H:%M:%S %Z", "%H:%M"],
1205 "german-ch" : ["%H:%M:%S %Z", "%H:%M"],
1206 "german-ch-old" : ["%H:%M:%S %Z", "%H:%M"],
1207 "greek" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1208 "hebrew" : ["%H:%M:%S %Z", "%H:%M"],
1209 "hindi" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1210 "icelandic" : ["%H:%M:%S %Z", "%H:%M"],
1211 "interlingua" : ["%H:%M:%S %Z", "%H:%M"],
1212 "irish" : ["%H:%M:%S %Z", "%H:%M"],
1213 "italian" : ["%H:%M:%S %Z", "%H:%M"],
1214 "japanese" : ["%H時%M分%S秒 %Z", "%H:%M"],
1215 "japanese-cjk" : ["%H時%M分%S秒 %Z", "%H:%M"],
1216 "kannada" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1217 "kazakh" : ["%H:%M:%S %Z", "%H:%M"],
1218 "khmer" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1219 "korean" : ["%p %I시%M분 %S초 %Z", "%p %I:%M"],
1220 "kurmanji" : ["%H:%M:%S %Z", "%H:%M:%S"],
1221 "lao" : ["%H ໂມງ%M ນາທີ %S ວິນາທີ %Z", "%H:%M"],
1222 "latin" : ["%H:%M:%S %Z", "%H:%M:%S"],
1223 "latvian" : ["%H:%M:%S %Z", "%H:%M"],
1224 "lithuanian" : ["%H:%M:%S %Z", "%H:%M"],
1225 "lowersorbian" : ["%H:%M:%S %Z", "%H:%M"],
1226 "macedonian" : ["%H:%M:%S %Z", "%H:%M"],
1227 "magyar" : ["%H:%M:%S %Z", "%H:%M"],
1228 "malayalam" : ["%p %I:%M:%S %Z", "%p %I:%M"],
1229 "marathi" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1230 "mongolian" : ["%H:%M:%S %Z", "%H:%M"],
1231 "naustrian" : ["%H:%M:%S %Z", "%H:%M"],
1232 "newzealand" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1233 "ngerman" : ["%H:%M:%S %Z", "%H:%M"],
1234 "norsk" : ["%H:%M:%S %Z", "%H:%M"],
1235 "nynorsk" : ["kl. %H:%M:%S %Z", "%H:%M"],
1236 "occitan" : ["%H:%M:%S %Z", "%H:%M"],
1237 "piedmontese" : ["%H:%M:%S %Z", "%H:%M:%S"],
1238 "polish" : ["%H:%M:%S %Z", "%H:%M"],
1239 "polutonikogreek" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1240 "portuguese" : ["%H:%M:%S %Z", "%H:%M"],
1241 "romanian" : ["%H:%M:%S %Z", "%H:%M"],
1242 "romansh" : ["%H:%M:%S %Z", "%H:%M"],
1243 "russian" : ["%H:%M:%S %Z", "%H:%M"],
1244 "samin" : ["%H:%M:%S %Z", "%H:%M"],
1245 "sanskrit" : ["%H:%M:%S %Z", "%H:%M"],
1246 "scottish" : ["%H:%M:%S %Z", "%H:%M"],
1247 "serbian" : ["%H:%M:%S %Z", "%H:%M"],
1248 "serbian-latin" : ["%H:%M:%S %Z", "%H:%M"],
1249 "slovak" : ["%H:%M:%S %Z", "%H:%M"],
1250 "slovene" : ["%H:%M:%S %Z", "%H:%M"],
1251 "spanish" : ["%H:%M:%S (%Z)", "%H:%M"],
1252 "spanish-mexico" : ["%H:%M:%S %Z", "%H:%M"],
1253 "swedish" : ["kl. %H:%M:%S %Z", "%H:%M"],
1254 "syriac" : ["%H:%M:%S %Z", "%H:%M"],
1255 "tamil" : ["%p %I:%M:%S %Z", "%p %I:%M"],
1256 "telugu" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1257 "thai" : ["%H นาฬิกา %M นาที %S วินาที %Z", "%H:%M"],
1258 "tibetan" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1259 "turkish" : ["%H:%M:%S %Z", "%H:%M"],
1260 "turkmen" : ["%H:%M:%S %Z", "%H:%M"],
1261 "ukrainian" : ["%H:%M:%S %Z", "%H:%M"],
1262 "uppersorbian" : ["%H:%M:%S %Z", "%H:%M hodź."],
1263 "urdu" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1264 "vietnamese" : ["%H:%M:%S %Z", "%H:%M"],
1265 "welsh" : ["%H:%M:%S %Z", "%H:%M"]
1268 types = ["time", "fixtime", "modtime" ]
1270 i = find_token(document.header, "\\language", 0)
1272 # this should not happen
1273 document.warning("Malformed LyX document! No \\language header found!")
1275 lang = get_value(document.header, "\\language", i)
1279 i = find_token(document.body, "\\begin_inset Info", i)
1282 j = find_end_of_inset(document.body, i + 1)
1284 document.warning("Malformed LyX document: Could not find end of Info inset.")
1287 tp = find_token(document.body, 'type', i, j)
1288 tpv = get_quoted_value(document.body, "type", tp)
1289 if tpv not in types:
1292 arg = find_token(document.body, 'arg', i, j)
1293 argv = get_quoted_value(document.body, "arg", arg)
1295 dtme = datetime.now()
1297 if tpv == "fixtime":
1298 timecomps = argv.split('@')
1299 if len(timecomps) > 1:
1301 isotime = timecomps[1]
1302 m = re.search('(\d\d):(\d\d):(\d\d)', isotime)
1304 tme = time(int(m.group(1)), int(m.group(2)), int(m.group(3)))
1306 m = re.search('(\d\d):(\d\d)', isotime)
1308 tme = time(int(m.group(1)), int(m.group(2)))
1309 # FIXME if we had the path to the original document (not the one in the tmp dir),
1310 # we could use the mtime.
1311 # elif tpv == "moddate":
1312 # dte = date.fromtimestamp(os.path.getmtime(document.dir))
1315 result = tme.isoformat()
1316 elif argv == "long":
1317 result = tme.strftime(timeformats[lang][0])
1318 elif argv == "short":
1319 result = tme.strftime(timeformats[lang][1])
1321 fmt = argv.replace("HH", "%H").replace("H", "%H").replace("hh", "%I").replace("h", "%I")
1322 fmt = fmt.replace("mm", "%M").replace("m", "%M").replace("ss", "%S").replace("s", "%S")
1323 fmt = fmt.replace("zzz", "%f").replace("z", "%f").replace("t", "%Z")
1324 fmt = fmt.replace("AP", "%p").replace("ap", "%p").replace("A", "%p").replace("a", "%p")
1325 fmt = fmt.replace("'", "")
1326 result = dte.strftime(fmt)
1327 document.body[i : j+1] = result
1331 def revert_namenoextinfo(document):
1332 " Merge buffer Info inset type name-noext to name. "
1336 i = find_token(document.body, "\\begin_inset Info", i)
1339 j = find_end_of_inset(document.body, i + 1)
1341 document.warning("Malformed LyX document: Could not find end of Info inset.")
1344 tp = find_token(document.body, 'type', i, j)
1345 tpv = get_quoted_value(document.body, "type", tp)
1349 arg = find_token(document.body, 'arg', i, j)
1350 argv = get_quoted_value(document.body, "arg", arg)
1351 if argv != "name-noext":
1354 document.body[arg] = "arg \"name\""
1358 def revert_l7ninfo(document):
1359 " Revert l7n Info inset to text. "
1363 i = find_token(document.body, "\\begin_inset Info", i)
1366 j = find_end_of_inset(document.body, i + 1)
1368 document.warning("Malformed LyX document: Could not find end of Info inset.")
1371 tp = find_token(document.body, 'type', i, j)
1372 tpv = get_quoted_value(document.body, "type", tp)
1376 arg = find_token(document.body, 'arg', i, j)
1377 argv = get_quoted_value(document.body, "arg", arg)
1378 # remove trailing colons, menu accelerator (|...) and qt accelerator (&), while keeping literal " & "
1379 argv = argv.rstrip(':').split('|')[0].replace(" & ", "</amp;>").replace("&", "").replace("</amp;>", " & ")
1380 document.body[i : j+1] = argv
1384 def revert_listpargs(document):
1385 " Reverts listpreamble arguments to TeX-code "
1388 i = find_token(document.body, "\\begin_inset Argument listpreamble:", i)
1391 j = find_end_of_inset(document.body, i)
1392 # Find containing paragraph layout
1393 parent = get_containing_layout(document.body, i)
1395 document.warning("Malformed LyX document: Can't find parent paragraph layout")
1399 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1400 endPlain = find_end_of_layout(document.body, beginPlain)
1401 content = document.body[beginPlain + 1 : endPlain]
1402 del document.body[i:j+1]
1403 subst = ["\\begin_inset ERT", "status collapsed", "", "\\begin_layout Plain Layout",
1404 "{"] + content + ["}", "\\end_layout", "", "\\end_inset", ""]
1405 document.body[parbeg : parbeg] = subst
1409 def revert_lformatinfo(document):
1410 " Revert layout format Info inset to text. "
1414 i = find_token(document.body, "\\begin_inset Info", i)
1417 j = find_end_of_inset(document.body, i + 1)
1419 document.warning("Malformed LyX document: Could not find end of Info inset.")
1422 tp = find_token(document.body, 'type', i, j)
1423 tpv = get_quoted_value(document.body, "type", tp)
1424 if tpv != "lyxinfo":
1427 arg = find_token(document.body, 'arg', i, j)
1428 argv = get_quoted_value(document.body, "arg", arg)
1429 if argv != "layoutformat":
1433 document.body[i : j+1] = "69"
1437 def convert_hebrew_parentheses(document):
1438 """ Swap opening/closing parentheses in Hebrew text.
1440 Up to LyX 2.4, ")" was used as opening parenthesis and
1441 "(" as closing parenthesis for Hebrew in the LyX source.
1444 print("convert hebrew parentheses")
1445 current_languages = [document.language]
1446 for i, line in enumerate(document.body):
1447 if line.startswith('\\lang '):
1448 current_languages[-1] = line.lstrip('\\lang ')
1449 elif line.startswith('\\begin_layout'):
1450 current_languages.append(current_languages[-1])
1451 print (line, current_languages[-1])
1452 elif line.startswith('\\end_layout'):
1453 current_languages.pop()
1454 elif current_languages[-1] == 'hebrew' and not line.startswith('\\'):
1455 document.body[i] = line.replace('(','\x00').replace(')','(').replace('\x00',')')
1458 def revert_hebrew_parentheses(document):
1459 " Store parentheses in Hebrew text reversed"
1460 # This only exists to keep the convert/revert naming convention
1461 convert_hebrew_parentheses(document)
1464 def revert_malayalam(document):
1465 " Set the document language to English but assure Malayalam output "
1467 revert_language(document, "malayalam", "", "malayalam")
1470 def revert_soul(document):
1471 " Revert soul module flex insets to ERT "
1473 flexes = ["Spaceletters", "Strikethrough", "Underline", "Highlight", "Capitalize"]
1476 i = find_token(document.body, "\\begin_inset Flex %s" % flex, 0)
1478 add_to_preamble(document, ["\\usepackage{soul}"])
1480 i = find_token(document.body, "\\begin_inset Flex Highlight", 0)
1482 add_to_preamble(document, ["\\usepackage{color}"])
1484 revert_flex_inset(document.body, "Spaceletters", "\\so")
1485 revert_flex_inset(document.body, "Strikethrough", "\\st")
1486 revert_flex_inset(document.body, "Underline", "\\ul")
1487 revert_flex_inset(document.body, "Highlight", "\\hl")
1488 revert_flex_inset(document.body, "Capitalize", "\\caps")
1491 def revert_tablestyle(document):
1492 " Remove tablestyle params "
1495 i = find_token(document.header, "\\tablestyle", 0)
1497 del document.header[i]
1500 def revert_bibfileencodings(document):
1501 " Revert individual Biblatex bibliography encodings "
1505 i = find_token(document.header, "\\cite_engine", 0)
1507 document.warning("Malformed document! Missing \\cite_engine")
1509 engine = get_value(document.header, "\\cite_engine", i)
1513 if engine in ["biblatex", "biblatex-natbib"]:
1516 # Map lyx to latex encoding names
1520 "armscii8" : "armscii8",
1521 "iso8859-1" : "latin1",
1522 "iso8859-2" : "latin2",
1523 "iso8859-3" : "latin3",
1524 "iso8859-4" : "latin4",
1525 "iso8859-5" : "iso88595",
1526 "iso8859-6" : "8859-6",
1527 "iso8859-7" : "iso-8859-7",
1528 "iso8859-8" : "8859-8",
1529 "iso8859-9" : "latin5",
1530 "iso8859-13" : "latin7",
1531 "iso8859-15" : "latin9",
1532 "iso8859-16" : "latin10",
1533 "applemac" : "applemac",
1535 "cp437de" : "cp437de",
1543 "cp1250" : "cp1250",
1544 "cp1251" : "cp1251",
1545 "cp1252" : "cp1252",
1546 "cp1255" : "cp1255",
1547 "cp1256" : "cp1256",
1548 "cp1257" : "cp1257",
1549 "koi8-r" : "koi8-r",
1550 "koi8-u" : "koi8-u",
1552 "utf8-platex" : "utf8",
1559 i = find_token(document.body, "\\begin_inset CommandInset bibtex", i)
1562 j = find_end_of_inset(document.body, i)
1564 document.warning("Can't find end of bibtex inset at line %d!!" %(i))
1567 encodings = get_quoted_value(document.body, "file_encodings", i, j)
1571 bibfiles = get_quoted_value(document.body, "bibfiles", i, j).split(",")
1572 opts = get_quoted_value(document.body, "biblatexopts", i, j)
1573 if len(bibfiles) == 0:
1574 document.warning("Bibtex inset at line %d does not have a bibfile!" %(i))
1575 # remove encoding line
1576 k = find_token(document.body, "file_encodings", i, j)
1578 del document.body[k]
1579 # Re-find inset end line
1580 j = find_end_of_inset(document.body, i)
1582 enclist = encodings.split("\t")
1585 ppp = pp.split(" ", 1)
1586 encmap[ppp[0]] = ppp[1]
1587 for bib in bibfiles:
1588 pr = "\\addbibresource"
1589 if bib in encmap.keys():
1590 pr += "[bibencoding=" + encmap[bib] + "]"
1591 pr += "{" + bib + "}"
1592 add_to_preamble(document, [pr])
1593 # Insert ERT \\printbibliography and wrap bibtex inset to a Note
1594 pcmd = "printbibliography"
1596 pcmd += "[" + opts + "]"
1597 repl = ["\\begin_inset ERT", "status open", "", "\\begin_layout Plain Layout",\
1598 "", "", "\\backslash", pcmd, "\\end_layout", "", "\\end_inset", "", "",\
1599 "\\end_layout", "", "\\begin_layout Standard", "\\begin_inset Note Note",\
1600 "status open", "", "\\begin_layout Plain Layout" ]
1601 repl += document.body[i:j+1]
1602 repl += ["", "\\end_layout", "", "\\end_inset", "", ""]
1603 document.body[i:j+1] = repl
1609 def revert_cmidruletrimming(document):
1610 " Remove \\cmidrule trimming "
1612 # FIXME: Revert to TeX code?
1615 # first, let's find out if we need to do anything
1616 i = find_token(document.body, '<cell ', i)
1619 j = document.body[i].find('trim="')
1623 rgx = re.compile(r' (bottom|top)line[lr]trim="true"')
1624 # remove trim option
1625 document.body[i] = rgx.sub('', document.body[i])
1634 supported_versions = ["2.4.0", "2.4"]
1636 [545, [convert_lst_literalparam]],
1641 [550, [convert_fontenc]],
1648 [557, [convert_vcsinfo]],
1649 [558, [removeFrontMatterStyles]],
1652 [561, [convert_latexFonts]], # Handle dejavu, ibmplex fonts in GUI
1656 [565, [convert_AdobeFonts]], # Handle adobe fonts in GUI
1657 [566, [convert_hebrew_parentheses]],
1663 [572, [convert_notoFonts]], # Added options thin, light, extralight for Noto
1664 [573, [convert_inputencoding_namechange]],
1667 revert = [[572, [revert_inputencoding_namechange]],
1668 [571, [revert_notoFonts]],
1669 [570, [revert_cmidruletrimming]],
1670 [569, [revert_bibfileencodings]],
1671 [568, [revert_tablestyle]],
1672 [567, [revert_soul]],
1673 [566, [revert_malayalam]],
1674 [565, [revert_hebrew_parentheses]],
1675 [564, [revert_AdobeFonts]],
1676 [563, [revert_lformatinfo]],
1677 [562, [revert_listpargs]],
1678 [561, [revert_l7ninfo]],
1679 [560, [revert_latexFonts]], # Handle dejavu, ibmplex fonts in user preamble
1680 [559, [revert_timeinfo, revert_namenoextinfo]],
1681 [558, [revert_dateinfo]],
1682 [557, [addFrontMatterStyles]],
1683 [556, [revert_vcsinfo]],
1684 [555, [revert_bibencoding]],
1685 [554, [revert_vcolumns]],
1686 [553, [revert_stretchcolumn]],
1687 [552, [revert_tuftecite]],
1688 [551, [revert_floatpclass, revert_floatalignment]],
1689 [550, [revert_nospellcheck]],
1690 [549, [revert_fontenc]],
1691 [548, []],# dummy format change
1692 [547, [revert_lscape]],
1693 [546, [revert_xcharter]],
1694 [545, [revert_paratype]],
1695 [544, [revert_lst_literalparam]]
1699 if __name__ == "__main__":