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, del_token, find_end_of_inset,
30 find_end_of_layout, find_token, find_token_backwards, find_token_exact,
31 find_re, get_bool_value,
32 get_containing_layout, get_option_value, get_value, get_quoted_value)
33 # del_value, del_complete_lines,
34 # find_complete_lines, find_end_of,
35 # find_re, find_substring,
36 # get_containing_inset,
37 # is_in_inset, set_bool_value
38 # find_tokens, check_token
40 from lyx2lyx_tools import (put_cmd_in_ert, add_to_preamble, insert_to_preamble, lyx2latex,
41 revert_language, revert_flex_inset, str2bool)
42 # revert_font_attrs, latex_length
43 # get_ert, lyx2verbatim, length_in_bp, convert_info_insets
44 # revert_flex_inset, hex2ratio
46 ####################################################################
47 # Private helper functions
49 def add_preamble_fonts(document, fontmap):
50 " Add collected font-packages with their option to user-preamble"
53 if len(fontmap[pkg]) > 0:
54 xoption = "[" + ",".join(fontmap[pkg]) + "]"
57 preamble = "\\usepackage%s{%s}" % (xoption, pkg)
58 add_to_preamble(document, [preamble])
61 def createkey(pkg, options):
63 return pkg + ':' + "-".join(options)
67 self.fontname = None # key into font2pkgmap
68 self.fonttype = None # roman,sans,typewriter,math
69 self.scaletype = None # None,sf,tt
70 self.scaleopt = None # None, 'scaled', 'scale'
74 self.pkgkey = None # key into pkg2fontmap
75 self.osfopt = None # None, string
78 self.pkgkey = createkey(self.package, self.options)
82 self.font2pkgmap = dict()
83 self.pkg2fontmap = dict()
84 self.pkginmap = dict() # defines, if a map for package exists
86 def expandFontMapping(self, font_list, font_type, scale_type, pkg, scaleopt = None, osfopt = None):
87 " Expand fontinfo mapping"
89 # fontlist: list of fontnames, each element
90 # may contain a ','-separated list of needed options
91 # like e.g. 'IBMPlexSansCondensed,condensed'
92 # font_type: one of 'roman', 'sans', 'typewriter', 'math'
93 # scale_type: one of None, 'sf', 'tt'
94 # pkg: package defining the font. Defaults to fontname if None
95 # scaleopt: one of None, 'scale', 'scaled', or some other string
96 # to be used in scale option (e.g. scaled=0.7)
97 # osfopt: None or some other string to be used in osf option
100 fe.fonttype = font_type
101 fe.scaletype = scale_type
104 fe.fontname = font_name
106 fe.scaleopt = scaleopt
109 fe.package = font_name
113 self.font2pkgmap[font_name] = fe
114 if fe.pkgkey in self.pkg2fontmap:
115 # Repeated the same entry? Check content
116 if self.pkg2fontmap[fe.pkgkey] != font_name:
117 document.error("Something is wrong in pkgname+options <-> fontname mapping")
118 self.pkg2fontmap[fe.pkgkey] = font_name
119 self.pkginmap[fe.package] = 1
121 def getfontname(self, pkg, options):
123 pkgkey = createkey(pkg, options)
124 if not pkgkey in self.pkg2fontmap:
126 fontname = self.pkg2fontmap[pkgkey]
127 if not fontname in self.font2pkgmap:
128 document.error("Something is wrong in pkgname+options <-> fontname mapping")
130 if pkgkey == self.font2pkgmap[fontname].pkgkey:
134 def createFontMapping(fontlist):
135 # Create info for known fonts for the use in
136 # convert_latexFonts() and
137 # revert_latexFonts()
139 # * Would be more handy to parse latexFonts file,
140 # but the path to this file is unknown
141 # * For now, add DejaVu and IBMPlex only.
142 # * Expand, if desired
144 for font in fontlist:
146 fm.expandFontMapping(['DejaVuSerif', 'DejaVuSerifCondensed'], "roman", None, None)
147 fm.expandFontMapping(['DejaVuSans','DejaVuSansCondensed'], "sans", "sf", None, "scaled")
148 fm.expandFontMapping(['DejaVuSansMono'], "typewriter", "tt", None, "scaled")
150 fm.expandFontMapping(['IBMPlexSerif', 'IBMPlexSerifThin,thin',
151 'IBMPlexSerifExtraLight,extralight', 'IBMPlexSerifLight,light',
152 'IBMPlexSerifSemibold,semibold'],
153 "roman", None, "plex-serif")
154 fm.expandFontMapping(['IBMPlexSans','IBMPlexSansCondensed,condensed',
155 'IBMPlexSansThin,thin', 'IBMPlexSansExtraLight,extralight',
156 'IBMPlexSansLight,light', 'IBMPlexSansSemibold,semibold'],
157 "sans", "sf", "plex-sans", "scale")
158 fm.expandFontMapping(['IBMPlexMono', 'IBMPlexMonoThin,thin',
159 'IBMPlexMonoExtraLight,extralight', 'IBMPlexMonoLight,light',
160 'IBMPlexMonoSemibold,semibold'],
161 "typewriter", "tt", "plex-mono", "scale")
162 elif font == 'Adobe':
163 fm.expandFontMapping(['ADOBESourceSerifPro'], "roman", None, "sourceserifpro", None, "osf")
164 fm.expandFontMapping(['ADOBESourceSansPro'], "sans", "sf", "sourcesanspro", "scaled", "osf")
165 fm.expandFontMapping(['ADOBESourceCodePro'], "typewriter", "tt", "sourcecodepro", "scaled", "osf")
167 fm.expandFontMapping(['NotoSerifRegular,regular', 'NotoSerifMedium,medium',
168 'NotoSerifThin,thin', 'NotoSerifLight,light',
169 'NotoSerifExtralight,extralight'],
170 "roman", None, "noto-serif", None, "osf")
171 fm.expandFontMapping(['NotoSansRegular,regular', 'NotoSansMedium,medium',
172 'NotoSansThin,thin', 'NotoSansLight,light',
173 'NotoSansExtralight,extralight'],
174 "sans", "sf", "noto-sans", "scaled")
175 fm.expandFontMapping(['NotoMonoRegular,regular'], "typewriter", "tt", "noto-mono", "scaled")
176 elif font == 'Cantarell':
177 fm.expandFontMapping(['cantarell,defaultsans'],
178 "sans", "sf", "cantarell", "scaled", "oldstyle")
181 def convert_fonts(document, fm, osfoption = "osf"):
182 " Handle font definition (LaTeX preamble -> native) "
184 rpkg = re.compile(r'^\\usepackage(\[([^\]]*)\])?\{([^\}]+)\}')
185 rscaleopt = re.compile(r'^scaled?=(.*)')
187 # Check whether we go beyond font option feature introduction
188 haveFontOpts = document.end_format > 580
191 while i < len(document.preamble):
192 i = find_re(document.preamble, rpkg, i+1)
195 mo = rpkg.search(document.preamble[i])
196 if mo == None or mo.group(2) == None:
199 options = mo.group(2).replace(' ', '').split(",")
204 while o < len(options):
205 if options[o] == osfoption:
209 mo = rscaleopt.search(options[o])
217 if not pkg in fm.pkginmap:
222 # Try with name-option combination first
223 # (only one default option supported currently)
225 while o < len(options):
227 fn = fm.getfontname(pkg, [opt])
234 fn = fm.getfontname(pkg, [])
236 fn = fm.getfontname(pkg, options)
239 del document.preamble[i]
240 fontinfo = fm.font2pkgmap[fn]
241 if fontinfo.scaletype == None:
244 fontscale = "\\font_" + fontinfo.scaletype + "_scale"
245 fontinfo.scaleval = oscale
247 if fontinfo.osfopt == None:
248 options.extend(osfoption)
250 osf = find_token(document.header, "\\font_osf false")
251 osftag = "\\font_osf"
252 if osf == -1 and fontinfo.fonttype != "math":
253 # Try with newer format
254 osftag = "\\font_" + fontinfo.fonttype + "_osf"
255 osf = find_token(document.header, osftag + " false")
257 document.header[osf] = osftag + " true"
258 if i > 0 and document.preamble[i-1] == "% Added by lyx2lyx":
259 del document.preamble[i-1]
261 if fontscale != None:
262 j = find_token(document.header, fontscale, 0)
264 val = get_value(document.header, fontscale, j)
268 scale = "%03d" % int(float(oscale) * 100)
269 document.header[j] = fontscale + " " + scale + " " + vals[1]
270 ft = "\\font_" + fontinfo.fonttype
271 j = find_token(document.header, ft, 0)
273 val = get_value(document.header, ft, j)
274 words = val.split() # ! splits also values like '"DejaVu Sans"'
275 words[0] = '"' + fn + '"'
276 document.header[j] = ft + ' ' + ' '.join(words)
277 if haveFontOpts and fontinfo.fonttype != "math":
278 fotag = "\\font_" + fontinfo.fonttype + "_opts"
279 fo = find_token(document.header, fotag)
281 document.header[fo] = fotag + " \"" + ",".join(options) + "\""
283 # Sensible place to insert tag
284 fo = find_token(document.header, "\\font_sf_scale")
286 document.warning("Malformed LyX document! Missing \\font_sf_scale")
288 document.header.insert(fo, fotag + " \"" + ",".join(options) + "\"")
292 def revert_fonts(document, fm, fontmap, OnlyWithXOpts = False, WithXOpts = False):
293 " Revert native font definition to LaTeX "
294 # fonlist := list of fonts created from the same package
295 # Empty package means that the font-name is the same as the package-name
296 # fontmap (key = package, val += found options) will be filled
297 # and used later in add_preamble_fonts() to be added to user-preamble
299 rfontscale = re.compile(r'^\s*(\\font_(roman|sans|typewriter|math))\s+')
300 rscales = re.compile(r'^\s*(\d+)\s+(\d+)')
302 while i < len(document.header):
303 i = find_re(document.header, rfontscale, i+1)
306 mo = rfontscale.search(document.header[i])
309 ft = mo.group(1) # 'roman', 'sans', 'typewriter', 'math'
310 val = get_value(document.header, ft, i)
311 words = val.split(' ') # ! splits also values like '"DejaVu Sans"'
312 font = words[0].strip('"') # TeX font name has no whitespace
313 if not font in fm.font2pkgmap:
315 fontinfo = fm.font2pkgmap[font]
316 val = fontinfo.package
317 if not val in fontmap:
320 if OnlyWithXOpts or WithXOpts:
321 if ft == "\\font_math":
323 regexp = re.compile(r'^\s*(\\font_roman_opts)\s+')
324 if ft == "\\font_sans":
325 regexp = re.compile(r'^\s*(\\font_sans_opts)\s+')
326 elif ft == "\\font_typewriter":
327 regexp = re.compile(r'^\s*(\\font_typewriter_opts)\s+')
328 x = find_re(document.header, regexp, 0)
329 if x == -1 and OnlyWithXOpts:
333 # We need to use this regex since split() does not handle quote protection
334 xopts = re.findall(r'[^"\s]\S*|".+?"', document.header[x])
335 opts = xopts[1].strip('"').split(",")
336 fontmap[val].extend(opts)
337 del document.header[x]
338 words[0] = '"default"'
339 document.header[i] = ft + ' ' + ' '.join(words)
340 if fontinfo.scaleopt != None:
341 xval = get_value(document.header, "\\font_" + fontinfo.scaletype + "_scale", 0)
342 mo = rscales.search(xval)
347 # set correct scale option
348 fontmap[val].extend([fontinfo.scaleopt + "=" + format(float(xval1) / 100, '.2f')])
349 if fontinfo.osfopt != None:
350 osf = find_token(document.header, "\\font_osf true")
351 if osf == -1 and ft != "\\font_math":
352 # Try with newer format
353 osftag = "\\font_roman_osf true"
354 if ft == "\\font_sans":
355 osftag = "\\font_sans_osf true"
356 elif ft == "\\font_typewriter":
357 osftag = "\\font_typewriter_osf true"
358 osf = find_token(document.header, osftag)
360 fontmap[val].extend([fontinfo.osfopt])
361 if len(fontinfo.options) > 0:
362 fontmap[val].extend(fontinfo.options)
365 ###############################################################################
367 ### Conversion and reversion routines
369 ###############################################################################
371 def convert_inputencoding_namechange(document):
372 " Rename inputencoding settings. "
373 i = find_token(document.header, "\\inputencoding", 0)
376 s = document.header[i].replace("auto", "auto-legacy")
377 document.header[i] = s.replace("default", "auto-legacy-plain")
379 def revert_inputencoding_namechange(document):
380 " Rename inputencoding settings. "
381 i = find_token(document.header, "\\inputencoding", 0)
384 s = document.header[i].replace("auto-legacy-plain", "default")
385 document.header[i] = s.replace("auto-legacy", "auto")
387 def convert_notoFonts(document):
388 " Handle Noto fonts definition to LaTeX "
390 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
391 fm = createFontMapping(['Noto'])
392 convert_fonts(document, fm)
394 def revert_notoFonts(document):
395 " Revert native Noto font definition to LaTeX "
397 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
399 fm = createFontMapping(['Noto'])
400 if revert_fonts(document, fm, fontmap):
401 add_preamble_fonts(document, fontmap)
403 def convert_latexFonts(document):
404 " Handle DejaVu and IBMPlex fonts definition to LaTeX "
406 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
407 fm = createFontMapping(['DejaVu', 'IBM'])
408 convert_fonts(document, fm)
410 def revert_latexFonts(document):
411 " Revert native DejaVu font definition to LaTeX "
413 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
415 fm = createFontMapping(['DejaVu', 'IBM'])
416 if revert_fonts(document, fm, fontmap):
417 add_preamble_fonts(document, fontmap)
419 def convert_AdobeFonts(document):
420 " Handle Adobe Source fonts definition to LaTeX "
422 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
423 fm = createFontMapping(['Adobe'])
424 convert_fonts(document, fm)
426 def revert_AdobeFonts(document):
427 " Revert Adobe Source font definition to LaTeX "
429 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
431 fm = createFontMapping(['Adobe'])
432 if revert_fonts(document, fm, fontmap):
433 add_preamble_fonts(document, fontmap)
435 def removeFrontMatterStyles(document):
436 " Remove styles Begin/EndFrontmatter"
438 layouts = ['BeginFrontmatter', 'EndFrontmatter']
439 tokenend = len('\\begin_layout ')
442 i = find_token_exact(document.body, '\\begin_layout ', i+1)
445 layout = document.body[i][tokenend:].strip()
446 if layout not in layouts:
448 j = find_end_of_layout(document.body, i)
450 document.warning("Malformed LyX document: Can't find end of layout at line %d" % i)
452 while document.body[j+1].strip() == '':
454 document.body[i:j+1] = []
456 def addFrontMatterStyles(document):
457 " Use styles Begin/EndFrontmatter for elsarticle"
459 if document.textclass != "elsarticle":
462 def insertFrontmatter(prefix, line):
464 while above > 0 and document.body[above-1].strip() == '':
467 while document.body[below].strip() == '':
469 document.body[above:below] = ['', '\\begin_layout ' + prefix + 'Frontmatter',
470 '\\begin_inset Note Note',
472 '\\begin_layout Plain Layout',
475 '\\end_inset', '', '',
478 layouts = ['Title', 'Title footnote', 'Author', 'Author footnote',
479 'Corresponding author', 'Address', 'Email', 'Abstract', 'Keywords']
480 tokenend = len('\\begin_layout ')
484 i = find_token_exact(document.body, '\\begin_layout ', i+1)
487 layout = document.body[i][tokenend:].strip()
488 if layout not in layouts:
490 k = find_end_of_layout(document.body, i)
492 document.warning("Malformed LyX document: Can't find end of layout at line %d" % i)
499 insertFrontmatter('End', k+1)
500 insertFrontmatter('Begin', first)
503 def convert_lst_literalparam(document):
504 " Add param literal to include inset "
508 i = find_token(document.body, '\\begin_inset CommandInset include', i+1)
511 j = find_end_of_inset(document.body, i)
513 document.warning("Malformed LyX document: Can't find end of command inset at line %d" % i)
515 while i < j and document.body[i].strip() != '':
517 document.body.insert(i, 'literal "true"')
520 def revert_lst_literalparam(document):
521 " Remove param literal from include inset "
525 i = find_token(document.body, '\\begin_inset CommandInset include', i+1)
528 j = find_end_of_inset(document.body, i)
530 document.warning("Malformed LyX document: Can't find end of include inset at line %d" % i)
532 del_token(document.body, 'literal', i, j)
535 def revert_paratype(document):
536 " Revert ParaType font definitions to LaTeX "
538 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
540 i1 = find_token(document.header, "\\font_roman \"PTSerif-TLF\"", 0)
541 i2 = find_token(document.header, "\\font_sans \"default\"", 0)
542 i3 = find_token(document.header, "\\font_typewriter \"default\"", 0)
543 j = find_token(document.header, "\\font_sans \"PTSans-TLF\"", 0)
546 sfval = find_token(document.header, "\\font_sf_scale", 0)
548 document.warning("Malformed LyX document: Missing \\font_sf_scale.")
550 sfscale = document.header[sfval].split()
553 document.header[sfval] = " ".join(sfscale)
556 sf_scale = float(val)
558 document.warning("Invalid font_sf_scale value: " + val)
561 if sf_scale != "100.0":
562 sfoption = "scaled=" + str(sf_scale / 100.0)
563 k = find_token(document.header, "\\font_typewriter \"PTMono-TLF\"", 0)
564 ttval = get_value(document.header, "\\font_tt_scale", 0)
569 ttoption = "scaled=" + format(float(ttval) / 100, '.2f')
570 if i1 != -1 and i2 != -1 and i3!= -1:
571 add_to_preamble(document, ["\\usepackage{paratype}"])
574 add_to_preamble(document, ["\\usepackage{PTSerif}"])
575 document.header[i1] = document.header[i1].replace("PTSerif-TLF", "default")
578 add_to_preamble(document, ["\\usepackage[" + sfoption + "]{PTSans}"])
580 add_to_preamble(document, ["\\usepackage{PTSans}"])
581 document.header[j] = document.header[j].replace("PTSans-TLF", "default")
584 add_to_preamble(document, ["\\usepackage[" + ttoption + "]{PTMono}"])
586 add_to_preamble(document, ["\\usepackage{PTMono}"])
587 document.header[k] = document.header[k].replace("PTMono-TLF", "default")
590 def revert_xcharter(document):
591 " Revert XCharter font definitions to LaTeX "
593 i = find_token(document.header, "\\font_roman \"xcharter\"", 0)
597 # replace unsupported font setting
598 document.header[i] = document.header[i].replace("xcharter", "default")
599 # no need for preamble code with system fonts
600 if get_bool_value(document.header, "\\use_non_tex_fonts"):
603 # transfer old style figures setting to package options
604 j = find_token(document.header, "\\font_osf true")
607 document.header[j] = "\\font_osf false"
611 add_to_preamble(document, ["\\usepackage%s{XCharter}"%options])
614 def revert_lscape(document):
615 " Reverts the landscape environment (Landscape module) to TeX-code "
617 if not "landscape" in document.get_module_list():
622 i = find_token(document.body, "\\begin_inset Flex Landscape", i+1)
625 j = find_end_of_inset(document.body, i)
627 document.warning("Malformed LyX document: Can't find end of Landscape inset")
630 if document.body[i] == "\\begin_inset Flex Landscape (Floating)":
631 document.body[j - 2 : j + 1] = put_cmd_in_ert("\\end{landscape}}")
632 document.body[i : i + 4] = put_cmd_in_ert("\\afterpage{\\begin{landscape}")
633 add_to_preamble(document, ["\\usepackage{afterpage}"])
635 document.body[j - 2 : j + 1] = put_cmd_in_ert("\\end{landscape}")
636 document.body[i : i + 4] = put_cmd_in_ert("\\begin{landscape}")
638 add_to_preamble(document, ["\\usepackage{pdflscape}"])
641 def convert_fontenc(document):
642 " Convert default fontenc setting "
644 i = find_token(document.header, "\\fontencoding global", 0)
648 document.header[i] = document.header[i].replace("global", "auto")
651 def revert_fontenc(document):
652 " Revert default fontenc setting "
654 i = find_token(document.header, "\\fontencoding auto", 0)
658 document.header[i] = document.header[i].replace("auto", "global")
661 def revert_nospellcheck(document):
662 " Remove nospellcheck font info param "
666 i = find_token(document.body, '\\nospellcheck', i)
672 def revert_floatpclass(document):
673 " Remove float placement params 'document' and 'class' "
675 del_token(document.header, "\\float_placement class")
679 i = find_token(document.body, '\\begin_inset Float', i+1)
682 j = find_end_of_inset(document.body, i)
683 k = find_token(document.body, 'placement class', i, i + 2)
685 k = find_token(document.body, 'placement document', i, i + 2)
692 def revert_floatalignment(document):
693 " Remove float alignment params "
695 galignment = get_value(document.header, "\\float_alignment", delete=True)
699 i = find_token(document.body, '\\begin_inset Float', i+1)
702 j = find_end_of_inset(document.body, i)
704 document.warning("Malformed LyX document: Can't find end of inset at line " + str(i))
706 k = find_token(document.body, 'alignment', i, i+4)
710 alignment = get_value(document.body, "alignment", k)
711 if alignment == "document":
712 alignment = galignment
714 l = find_token(document.body, "\\begin_layout Plain Layout", i, j)
716 document.warning("Can't find float layout!")
719 if alignment == "left":
720 alcmd = put_cmd_in_ert("\\raggedright{}")
721 elif alignment == "center":
722 alcmd = put_cmd_in_ert("\\centering{}")
723 elif alignment == "right":
724 alcmd = put_cmd_in_ert("\\raggedleft{}")
726 document.body[l+1:l+1] = alcmd
729 def revert_tuftecite(document):
730 " Revert \cite commands in tufte classes "
732 tufte = ["tufte-book", "tufte-handout"]
733 if document.textclass not in tufte:
738 i = find_token(document.body, "\\begin_inset CommandInset citation", i+1)
741 j = find_end_of_inset(document.body, i)
743 document.warning("Can't find end of citation inset at line %d!!" %(i))
745 k = find_token(document.body, "LatexCommand", i, j)
747 document.warning("Can't find LatexCommand for citation inset at line %d!" %(i))
750 cmd = get_value(document.body, "LatexCommand", k)
754 pre = get_quoted_value(document.body, "before", i, j)
755 post = get_quoted_value(document.body, "after", i, j)
756 key = get_quoted_value(document.body, "key", i, j)
758 document.warning("Citation inset at line %d does not have a key!" %(i))
760 # Replace command with ERT
763 res += "[" + pre + "]"
765 res += "[" + post + "]"
768 res += "{" + key + "}"
769 document.body[i:j+1] = put_cmd_in_ert([res])
773 def revert_stretchcolumn(document):
774 " We remove the column varwidth flags or everything else will become a mess. "
777 i = find_token(document.body, "\\begin_inset Tabular", i+1)
780 j = find_end_of_inset(document.body, i+1)
782 document.warning("Malformed LyX document: Could not find end of tabular.")
784 for k in range(i, j):
785 if re.search('^<column.*varwidth="[^"]+".*>$', document.body[k]):
786 document.warning("Converting 'tabularx'/'xltabular' table to normal table.")
787 document.body[k] = document.body[k].replace(' varwidth="true"', '')
790 def revert_vcolumns(document):
791 " Revert standard columns with line breaks etc. "
797 i = find_token(document.body, "\\begin_inset Tabular", i+1)
800 j = find_end_of_inset(document.body, i)
802 document.warning("Malformed LyX document: Could not find end of tabular.")
805 # Collect necessary column information
807 nrows = int(document.body[i+1].split('"')[3])
808 ncols = int(document.body[i+1].split('"')[5])
810 for k in range(ncols):
811 m = find_token(document.body, "<column", m)
812 width = get_option_value(document.body[m], 'width')
813 varwidth = get_option_value(document.body[m], 'varwidth')
814 alignment = get_option_value(document.body[m], 'alignment')
815 special = get_option_value(document.body[m], 'special')
816 col_info.append([width, varwidth, alignment, special, m])
821 for row in range(nrows):
822 for col in range(ncols):
823 m = find_token(document.body, "<cell", m)
824 multicolumn = get_option_value(document.body[m], 'multicolumn')
825 multirow = get_option_value(document.body[m], 'multirow')
826 width = get_option_value(document.body[m], 'width')
827 rotate = get_option_value(document.body[m], 'rotate')
828 # Check for: linebreaks, multipars, non-standard environments
830 endcell = find_token(document.body, "</cell>", begcell)
832 if find_token(document.body, "\\begin_inset Newline", begcell, endcell) != -1:
834 elif count_pars_in_inset(document.body, begcell + 2) > 1:
836 elif get_value(document.body, "\\begin_layout", begcell) != "Plain Layout":
838 if vcand and rotate == "" and ((multicolumn == "" and multirow == "") or width == ""):
839 if col_info[col][0] == "" and col_info[col][1] == "" and col_info[col][3] == "":
841 alignment = col_info[col][2]
842 col_line = col_info[col][4]
844 if alignment == "center":
845 vval = ">{\\centering}"
846 elif alignment == "left":
847 vval = ">{\\raggedright}"
848 elif alignment == "right":
849 vval = ">{\\raggedleft}"
852 vval += "V{\\linewidth}"
854 document.body[col_line] = document.body[col_line][:-1] + " special=\"" + vval + "\">"
855 # ERT newlines and linebreaks (since LyX < 2.4 automatically inserts parboxes
856 # with newlines, and we do not want that)
858 endcell = find_token(document.body, "</cell>", begcell)
860 nl = find_token(document.body, "\\begin_inset Newline newline", begcell, endcell)
862 nl = find_token(document.body, "\\begin_inset Newline linebreak", begcell, endcell)
866 nle = find_end_of_inset(document.body, nl)
867 del(document.body[nle:nle+1])
869 document.body[nl:nl+1] = put_cmd_in_ert("\\linebreak{}")
871 document.body[nl:nl+1] = put_cmd_in_ert("\\\\")
877 if needarray == True:
878 add_to_preamble(document, ["\\usepackage{array}"])
879 if needvarwidth == True:
880 add_to_preamble(document, ["\\usepackage{varwidth}"])
883 def revert_bibencoding(document):
884 " Revert bibliography encoding "
888 i = find_token(document.header, "\\cite_engine", 0)
890 document.warning("Malformed document! Missing \\cite_engine")
892 engine = get_value(document.header, "\\cite_engine", i)
896 if engine in ["biblatex", "biblatex-natbib"]:
899 # Map lyx to latex encoding names
903 "armscii8" : "armscii8",
904 "iso8859-1" : "latin1",
905 "iso8859-2" : "latin2",
906 "iso8859-3" : "latin3",
907 "iso8859-4" : "latin4",
908 "iso8859-5" : "iso88595",
909 "iso8859-6" : "8859-6",
910 "iso8859-7" : "iso-8859-7",
911 "iso8859-8" : "8859-8",
912 "iso8859-9" : "latin5",
913 "iso8859-13" : "latin7",
914 "iso8859-15" : "latin9",
915 "iso8859-16" : "latin10",
916 "applemac" : "applemac",
918 "cp437de" : "cp437de",
935 "utf8-platex" : "utf8",
942 i = find_token(document.body, "\\begin_inset CommandInset bibtex", i+1)
945 j = find_end_of_inset(document.body, i)
947 document.warning("Can't find end of bibtex inset at line %d!!" %(i))
949 encoding = get_quoted_value(document.body, "encoding", i, j)
952 # remove encoding line
953 k = find_token(document.body, "encoding", i, j)
956 if encoding == "default":
958 # Re-find inset end line
959 j = find_end_of_inset(document.body, i)
962 h = find_token(document.header, "\\biblio_options", 0)
964 biblio_options = get_value(document.header, "\\biblio_options", h)
965 if not "bibencoding" in biblio_options:
966 document.header[h] += ",bibencoding=%s" % encodings[encoding]
968 bs = find_token(document.header, "\\biblatex_bibstyle", 0)
970 # this should not happen
971 document.warning("Malformed LyX document! No \\biblatex_bibstyle header found!")
973 document.header[bs-1 : bs-1] = ["\\biblio_options bibencoding=" + encodings[encoding]]
975 document.body[j+1:j+1] = put_cmd_in_ert("\\egroup")
976 document.body[i:i] = put_cmd_in_ert("\\bgroup\\inputencoding{" + encodings[encoding] + "}")
982 def convert_vcsinfo(document):
983 " Separate vcs Info inset from buffer Info inset. "
986 "vcs-revision" : "revision",
987 "vcs-tree-revision" : "tree-revision",
988 "vcs-author" : "author",
994 i = find_token(document.body, "\\begin_inset Info", i+1)
997 j = find_end_of_inset(document.body, i+1)
999 document.warning("Malformed LyX document: Could not find end of Info inset.")
1001 tp = find_token(document.body, 'type', i, j)
1002 tpv = get_quoted_value(document.body, "type", tp)
1005 arg = find_token(document.body, 'arg', i, j)
1006 argv = get_quoted_value(document.body, "arg", arg)
1007 if argv not in list(types.keys()):
1009 document.body[tp] = "type \"vcs\""
1010 document.body[arg] = "arg \"" + types[argv] + "\""
1013 def revert_vcsinfo(document):
1014 " Merge vcs Info inset to buffer Info inset. "
1016 args = ["revision", "tree-revision", "author", "time", "date" ]
1019 i = find_token(document.body, "\\begin_inset Info", i+1)
1022 j = find_end_of_inset(document.body, i+1)
1024 document.warning("Malformed LyX document: Could not find end of Info inset.")
1026 tp = find_token(document.body, 'type', i, j)
1027 tpv = get_quoted_value(document.body, "type", tp)
1030 arg = find_token(document.body, 'arg', i, j)
1031 argv = get_quoted_value(document.body, "arg", arg)
1032 if argv not in args:
1033 document.warning("Malformed Info inset. Invalid vcs arg.")
1035 document.body[tp] = "type \"buffer\""
1036 document.body[arg] = "arg \"vcs-" + argv + "\""
1039 def revert_dateinfo(document):
1040 " Revert date info insets to static text. "
1042 # FIXME This currently only considers the main language and uses the system locale
1043 # Ideally, it should honor context languages and switch the locale accordingly.
1045 # The date formats for each language using strftime syntax:
1046 # long, short, loclong, locmedium, locshort
1048 "afrikaans" : ["%A, %d %B %Y", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y/%m/%d"],
1049 "albanian" : ["%A, %d %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1050 "american" : ["%A, %B %d, %Y", "%m/%d/%y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1051 "amharic" : ["%A ፣%d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1052 "ancientgreek" : ["%A, %d %B %Y", "%d %b %Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1053 "arabic_arabi" : ["%A، %d %B، %Y", "%d/%m/%Y", "%d %B، %Y", "%d/%m/%Y", "%d/%m/%Y"],
1054 "arabic_arabtex" : ["%A، %d %B، %Y", "%d/%m/%Y", "%d %B، %Y", "%d/%m/%Y", "%d/%m/%Y"],
1055 "armenian" : ["%Y թ. %B %d, %A", "%d.%m.%y", "%d %B، %Y", "%d %b، %Y", "%d/%m/%Y"],
1056 "asturian" : ["%A, %d %B de %Y", "%d/%m/%y", "%d de %B de %Y", "%d %b %Y", "%d/%m/%Y"],
1057 "australian" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1058 "austrian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1059 "bahasa" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1060 "bahasam" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1061 "basque" : ["%Y(e)ko %B %d, %A", "%y/%m/%d", "%Y %B %d", "%Y %b %d", "%Y/%m/%d"],
1062 "belarusian" : ["%A, %d %B %Y г.", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1063 "bosnian" : ["%A, %d. %B %Y.", "%d.%m.%y.", "%d. %B %Y", "%d. %b %Y", "%Y-%m-%d"],
1064 "brazilian" : ["%A, %d de %B de %Y", "%d/%m/%Y", "%d de %B de %Y", "%d de %b de %Y", "%d/%m/%Y"],
1065 "breton" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
1066 "british" : ["%A, %d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1067 "bulgarian" : ["%A, %d %B %Y г.", "%d.%m.%y г.", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
1068 "canadian" : ["%A, %B %d, %Y", "%Y-%m-%d", "%B %d, %Y", "%d %b %Y", "%Y-%m-%d"],
1069 "canadien" : ["%A %d %B %Y", "%y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
1070 "catalan" : ["%A, %d %B de %Y", "%d/%m/%y", "%d / %B / %Y", "%d / %b / %Y", "%d/%m/%Y"],
1071 "chinese-simplified" : ["%Y年%m月%d日%A", "%Y/%m/%d", "%Y年%m月%d日", "%Y-%m-%d", "%y-%m-%d"],
1072 "chinese-traditional" : ["%Y年%m月%d日 %A", "%Y/%m/%d", "%Y年%m月%d日", "%Y年%m月%d日", "%y年%m月%d日"],
1073 "coptic" : ["%A, %d %B %Y", "%d %b %Y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1074 "croatian" : ["%A, %d. %B %Y.", "%d. %m. %Y.", "%d. %B %Y.", "%d. %b. %Y.", "%d.%m.%Y."],
1075 "czech" : ["%A %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b. %Y", "%d.%m.%Y"],
1076 "danish" : ["%A den %d. %B %Y", "%d/%m/%Y", "%d. %B %Y", "%d. %b %Y", "%d/%m/%Y"],
1077 "divehi" : ["%Y %B %d, %A", "%Y-%m-%d", "%Y %B %d", "%Y %b %d", "%d/%m/%Y"],
1078 "dutch" : ["%A %d %B %Y", "%d-%m-%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1079 "english" : ["%A, %B %d, %Y", "%m/%d/%y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1080 "esperanto" : ["%A, %d %B %Y", "%d %b %Y", "la %d de %B %Y", "la %d de %b %Y", "%m/%d/%Y"],
1081 "estonian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1082 "farsi" : ["%A %d %B %Y", "%Y/%m/%d", "%d %B %Y", "%d %b %Y", "%Y/%m/%d"],
1083 "finnish" : ["%A %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1084 "french" : ["%A %d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1085 "friulan" : ["%A %d di %B dal %Y", "%d/%m/%y", "%d di %B dal %Y", "%d di %b dal %Y", "%d/%m/%Y"],
1086 "galician" : ["%A, %d de %B de %Y", "%d/%m/%y", "%d de %B de %Y", "%d de %b de %Y", "%d/%m/%Y"],
1087 "georgian" : ["%A, %d %B, %Y", "%d.%m.%y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1088 "german" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1089 "german-ch" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1090 "german-ch-old" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1091 "greek" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1092 "hebrew" : ["%A, %d ב%B %Y", "%d.%m.%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1093 "hindi" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1094 "icelandic" : ["%A, %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1095 "interlingua" : ["%Y %B %d, %A", "%Y-%m-%d", "le %d de %B %Y", "le %d de %b %Y", "%Y-%m-%d"],
1096 "irish" : ["%A %d %B %Y", "%d/%m/%Y", "%d. %B %Y", "%d. %b %Y", "%d/%m/%Y"],
1097 "italian" : ["%A %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d/%b/%Y", "%d/%m/%Y"],
1098 "japanese" : ["%Y年%m月%d日%A", "%Y/%m/%d", "%Y年%m月%d日", "%Y/%m/%d", "%y/%m/%d"],
1099 "japanese-cjk" : ["%Y年%m月%d日%A", "%Y/%m/%d", "%Y年%m月%d日", "%Y/%m/%d", "%y/%m/%d"],
1100 "kannada" : ["%A, %B %d, %Y", "%d/%m/%y", "%d %B %Y", "%d %B %Y", "%d-%m-%Y"],
1101 "kazakh" : ["%Y ж. %d %B, %A", "%d.%m.%y", "%d %B %Y", "%d %B %Y", "%Y-%d-%m"],
1102 "khmer" : ["%A %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %B %Y", "%d/%m/%Y"],
1103 "korean" : ["%Y년 %m월 %d일 %A", "%y. %m. %d.", "%Y년 %m월 %d일", "%Y. %m. %d.", "%y. %m. %d."],
1104 "kurmanji" : ["%A, %d %B %Y", "%d %b %Y", "%d. %B %Y", "%d. %m. %Y", "%Y-%m-%d"],
1105 "lao" : ["%A ທີ %d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %B %Y", "%d/%m/%Y"],
1106 "latin" : ["%A, %d %B %Y", "%d %b %Y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1107 "latvian" : ["%A, %Y. gada %d. %B", "%d.%m.%y", "%Y. gada %d. %B", "%Y. gada %d. %b", "%d.%m.%Y"],
1108 "lithuanian" : ["%Y m. %B %d d., %A", "%Y-%m-%d", "%Y m. %B %d d.", "%Y m. %B %d d.", "%Y-%m-%d"],
1109 "lowersorbian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1110 "macedonian" : ["%A, %d %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1111 "magyar" : ["%Y. %B %d., %A", "%Y. %m. %d.", "%Y. %B %d.", "%Y. %b %d.", "%Y.%m.%d."],
1112 "malayalam" : ["%A, %d %B, %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1113 "marathi" : ["%A, %d %B, %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1114 "mongolian" : ["%A, %Y оны %m сарын %d", "%Y-%m-%d", "%Y оны %m сарын %d", "%d-%m-%Y", "%d-%m-%Y"],
1115 "naustrian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1116 "newzealand" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1117 "ngerman" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1118 "norsk" : ["%A %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1119 "nynorsk" : ["%A %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1120 "occitan" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1121 "piedmontese" : ["%A, %d %B %Y", "%d %b %Y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1122 "polish" : ["%A, %d %B %Y", "%d.%m.%Y", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
1123 "polutonikogreek" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1124 "portuguese" : ["%A, %d de %B de %Y", "%d/%m/%y", "%d de %B de %Y", "%d de %b de %Y", "%Y/%m/%d"],
1125 "romanian" : ["%A, %d %B %Y", "%d.%m.%Y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1126 "romansh" : ["%A, ils %d da %B %Y", "%d-%m-%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1127 "russian" : ["%A, %d %B %Y г.", "%d.%m.%Y", "%d %B %Y г.", "%d %b %Y г.", "%d.%m.%Y"],
1128 "samin" : ["%Y %B %d, %A", "%Y-%m-%d", "%B %d. b. %Y", "%b %d. b. %Y", "%d.%m.%Y"],
1129 "sanskrit" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1130 "scottish" : ["%A, %dmh %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1131 "serbian" : ["%A, %d. %B %Y.", "%d.%m.%y.", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1132 "serbian-latin" : ["%A, %d. %B %Y.", "%d.%m.%y.", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1133 "slovak" : ["%A, %d. %B %Y", "%d. %m. %Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1134 "slovene" : ["%A, %d. %B %Y", "%d. %m. %y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1135 "spanish" : ["%A, %d de %B de %Y", "%d/%m/%y", "%d de %B %de %Y", "%d %b %Y", "%d/%m/%Y"],
1136 "spanish-mexico" : ["%A, %d de %B %de %Y", "%d/%m/%y", "%d de %B de %Y", "%d %b %Y", "%d/%m/%Y"],
1137 "swedish" : ["%A %d %B %Y", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
1138 "syriac" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1139 "tamil" : ["%A, %d %B, %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1140 "telugu" : ["%d, %B %Y, %A", "%d-%m-%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1141 "thai" : ["%Aที่ %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1142 "tibetan" : ["%Y %Bའི་ཚེས་%d, %A", "%Y-%m-%d", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1143 "turkish" : ["%d %B %Y %A", "%d.%m.%Y", "%d %B %Y", "%d.%b.%Y", "%d.%m.%Y"],
1144 "turkmen" : ["%d %B %Y %A", "%d.%m.%Y", "%Y ý. %B %d", "%d.%m.%Y ý.", "%d.%m.%y ý."],
1145 "ukrainian" : ["%A, %d %B %Y р.", "%d.%m.%y", "%d %B %Y", "%d %m %Y", "%d.%m.%Y"],
1146 "uppersorbian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1147 "urdu" : ["%A، %d %B، %Y", "%d/%m/%y", "%d %B, %Y", "%d %b %Y", "%d/%m/%Y"],
1148 "vietnamese" : ["%A, %d %B, %Y", "%d/%m/%Y", "%d tháng %B %Y", "%d-%m-%Y", "%d/%m/%Y"],
1149 "welsh" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1152 types = ["date", "fixdate", "moddate" ]
1153 lang = get_value(document.header, "\\language")
1155 document.warning("Malformed LyX document! No \\language header found!")
1160 i = find_token(document.body, "\\begin_inset Info", i+1)
1163 j = find_end_of_inset(document.body, i+1)
1165 document.warning("Malformed LyX document: Could not find end of Info inset.")
1167 tp = find_token(document.body, 'type', i, j)
1168 tpv = get_quoted_value(document.body, "type", tp)
1169 if tpv not in types:
1171 arg = find_token(document.body, 'arg', i, j)
1172 argv = get_quoted_value(document.body, "arg", arg)
1175 if tpv == "fixdate":
1176 datecomps = argv.split('@')
1177 if len(datecomps) > 1:
1179 isodate = datecomps[1]
1180 m = re.search('(\d\d\d\d)-(\d\d)-(\d\d)', isodate)
1182 dte = date(int(m.group(1)), int(m.group(2)), int(m.group(3)))
1183 # FIXME if we had the path to the original document (not the one in the tmp dir),
1184 # we could use the mtime.
1185 # elif tpv == "moddate":
1186 # dte = date.fromtimestamp(os.path.getmtime(document.dir))
1189 result = dte.isodate()
1190 elif argv == "long":
1191 result = dte.strftime(dateformats[lang][0])
1192 elif argv == "short":
1193 result = dte.strftime(dateformats[lang][1])
1194 elif argv == "loclong":
1195 result = dte.strftime(dateformats[lang][2])
1196 elif argv == "locmedium":
1197 result = dte.strftime(dateformats[lang][3])
1198 elif argv == "locshort":
1199 result = dte.strftime(dateformats[lang][4])
1201 fmt = argv.replace("MMMM", "%b").replace("MMM", "%b").replace("MM", "%m").replace("M", "%m")
1202 fmt = fmt.replace("yyyy", "%Y").replace("yy", "%y")
1203 fmt = fmt.replace("dddd", "%A").replace("ddd", "%a").replace("dd", "%d")
1204 fmt = re.sub('[^\'%]d', '%d', fmt)
1205 fmt = fmt.replace("'", "")
1206 result = dte.strftime(fmt)
1207 if sys.version_info < (3,0):
1208 # In Python 2, datetime module works with binary strings,
1209 # our dateformat strings are utf8-encoded:
1210 result = result.decode('utf-8')
1211 document.body[i : j+1] = [result]
1214 def revert_timeinfo(document):
1215 " Revert time info insets to static text. "
1217 # FIXME This currently only considers the main language and uses the system locale
1218 # Ideally, it should honor context languages and switch the locale accordingly.
1219 # Also, the time object is "naive", i.e., it does not know of timezones (%Z will
1222 # The time formats for each language using strftime syntax:
1225 "afrikaans" : ["%H:%M:%S %Z", "%H:%M"],
1226 "albanian" : ["%I:%M:%S %p, %Z", "%I:%M %p"],
1227 "american" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1228 "amharic" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1229 "ancientgreek" : ["%H:%M:%S %Z", "%H:%M:%S"],
1230 "arabic_arabi" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1231 "arabic_arabtex" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1232 "armenian" : ["%H:%M:%S %Z", "%H:%M"],
1233 "asturian" : ["%H:%M:%S %Z", "%H:%M"],
1234 "australian" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1235 "austrian" : ["%H:%M:%S %Z", "%H:%M"],
1236 "bahasa" : ["%H.%M.%S %Z", "%H.%M"],
1237 "bahasam" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1238 "basque" : ["%H:%M:%S (%Z)", "%H:%M"],
1239 "belarusian" : ["%H:%M:%S, %Z", "%H:%M"],
1240 "bosnian" : ["%H:%M:%S %Z", "%H:%M"],
1241 "brazilian" : ["%H:%M:%S %Z", "%H:%M"],
1242 "breton" : ["%H:%M:%S %Z", "%H:%M"],
1243 "british" : ["%H:%M:%S %Z", "%H:%M"],
1244 "bulgarian" : ["%H:%M:%S %Z", "%H:%M"],
1245 "canadian" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1246 "canadien" : ["%H:%M:%S %Z", "%H h %M"],
1247 "catalan" : ["%H:%M:%S %Z", "%H:%M"],
1248 "chinese-simplified" : ["%Z %p%I:%M:%S", "%p%I:%M"],
1249 "chinese-traditional" : ["%p%I:%M:%S [%Z]", "%p%I:%M"],
1250 "coptic" : ["%H:%M:%S %Z", "%H:%M:%S"],
1251 "croatian" : ["%H:%M:%S (%Z)", "%H:%M"],
1252 "czech" : ["%H:%M:%S %Z", "%H:%M"],
1253 "danish" : ["%H.%M.%S %Z", "%H.%M"],
1254 "divehi" : ["%H:%M:%S %Z", "%H:%M"],
1255 "dutch" : ["%H:%M:%S %Z", "%H:%M"],
1256 "english" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1257 "esperanto" : ["%H:%M:%S %Z", "%H:%M:%S"],
1258 "estonian" : ["%H:%M:%S %Z", "%H:%M"],
1259 "farsi" : ["%H:%M:%S (%Z)", "%H:%M"],
1260 "finnish" : ["%H.%M.%S %Z", "%H.%M"],
1261 "french" : ["%H:%M:%S %Z", "%H:%M"],
1262 "friulan" : ["%H:%M:%S %Z", "%H:%M"],
1263 "galician" : ["%H:%M:%S %Z", "%H:%M"],
1264 "georgian" : ["%H:%M:%S %Z", "%H:%M"],
1265 "german" : ["%H:%M:%S %Z", "%H:%M"],
1266 "german-ch" : ["%H:%M:%S %Z", "%H:%M"],
1267 "german-ch-old" : ["%H:%M:%S %Z", "%H:%M"],
1268 "greek" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1269 "hebrew" : ["%H:%M:%S %Z", "%H:%M"],
1270 "hindi" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1271 "icelandic" : ["%H:%M:%S %Z", "%H:%M"],
1272 "interlingua" : ["%H:%M:%S %Z", "%H:%M"],
1273 "irish" : ["%H:%M:%S %Z", "%H:%M"],
1274 "italian" : ["%H:%M:%S %Z", "%H:%M"],
1275 "japanese" : ["%H時%M分%S秒 %Z", "%H:%M"],
1276 "japanese-cjk" : ["%H時%M分%S秒 %Z", "%H:%M"],
1277 "kannada" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1278 "kazakh" : ["%H:%M:%S %Z", "%H:%M"],
1279 "khmer" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1280 "korean" : ["%p %I시%M분 %S초 %Z", "%p %I:%M"],
1281 "kurmanji" : ["%H:%M:%S %Z", "%H:%M:%S"],
1282 "lao" : ["%H ໂມງ%M ນາທີ %S ວິນາທີ %Z", "%H:%M"],
1283 "latin" : ["%H:%M:%S %Z", "%H:%M:%S"],
1284 "latvian" : ["%H:%M:%S %Z", "%H:%M"],
1285 "lithuanian" : ["%H:%M:%S %Z", "%H:%M"],
1286 "lowersorbian" : ["%H:%M:%S %Z", "%H:%M"],
1287 "macedonian" : ["%H:%M:%S %Z", "%H:%M"],
1288 "magyar" : ["%H:%M:%S %Z", "%H:%M"],
1289 "malayalam" : ["%p %I:%M:%S %Z", "%p %I:%M"],
1290 "marathi" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1291 "mongolian" : ["%H:%M:%S %Z", "%H:%M"],
1292 "naustrian" : ["%H:%M:%S %Z", "%H:%M"],
1293 "newzealand" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1294 "ngerman" : ["%H:%M:%S %Z", "%H:%M"],
1295 "norsk" : ["%H:%M:%S %Z", "%H:%M"],
1296 "nynorsk" : ["kl. %H:%M:%S %Z", "%H:%M"],
1297 "occitan" : ["%H:%M:%S %Z", "%H:%M"],
1298 "piedmontese" : ["%H:%M:%S %Z", "%H:%M:%S"],
1299 "polish" : ["%H:%M:%S %Z", "%H:%M"],
1300 "polutonikogreek" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1301 "portuguese" : ["%H:%M:%S %Z", "%H:%M"],
1302 "romanian" : ["%H:%M:%S %Z", "%H:%M"],
1303 "romansh" : ["%H:%M:%S %Z", "%H:%M"],
1304 "russian" : ["%H:%M:%S %Z", "%H:%M"],
1305 "samin" : ["%H:%M:%S %Z", "%H:%M"],
1306 "sanskrit" : ["%H:%M:%S %Z", "%H:%M"],
1307 "scottish" : ["%H:%M:%S %Z", "%H:%M"],
1308 "serbian" : ["%H:%M:%S %Z", "%H:%M"],
1309 "serbian-latin" : ["%H:%M:%S %Z", "%H:%M"],
1310 "slovak" : ["%H:%M:%S %Z", "%H:%M"],
1311 "slovene" : ["%H:%M:%S %Z", "%H:%M"],
1312 "spanish" : ["%H:%M:%S (%Z)", "%H:%M"],
1313 "spanish-mexico" : ["%H:%M:%S %Z", "%H:%M"],
1314 "swedish" : ["kl. %H:%M:%S %Z", "%H:%M"],
1315 "syriac" : ["%H:%M:%S %Z", "%H:%M"],
1316 "tamil" : ["%p %I:%M:%S %Z", "%p %I:%M"],
1317 "telugu" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1318 "thai" : ["%H นาฬิกา %M นาที %S วินาที %Z", "%H:%M"],
1319 "tibetan" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1320 "turkish" : ["%H:%M:%S %Z", "%H:%M"],
1321 "turkmen" : ["%H:%M:%S %Z", "%H:%M"],
1322 "ukrainian" : ["%H:%M:%S %Z", "%H:%M"],
1323 "uppersorbian" : ["%H:%M:%S %Z", "%H:%M hodź."],
1324 "urdu" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1325 "vietnamese" : ["%H:%M:%S %Z", "%H:%M"],
1326 "welsh" : ["%H:%M:%S %Z", "%H:%M"]
1329 types = ["time", "fixtime", "modtime" ]
1331 i = find_token(document.header, "\\language", 0)
1333 # this should not happen
1334 document.warning("Malformed LyX document! No \\language header found!")
1336 lang = get_value(document.header, "\\language", i)
1340 i = find_token(document.body, "\\begin_inset Info", i+1)
1343 j = find_end_of_inset(document.body, i+1)
1345 document.warning("Malformed LyX document: Could not find end of Info inset.")
1347 tp = find_token(document.body, 'type', i, j)
1348 tpv = get_quoted_value(document.body, "type", tp)
1349 if tpv not in types:
1351 arg = find_token(document.body, 'arg', i, j)
1352 argv = get_quoted_value(document.body, "arg", arg)
1354 dtme = datetime.now()
1356 if tpv == "fixtime":
1357 timecomps = argv.split('@')
1358 if len(timecomps) > 1:
1360 isotime = timecomps[1]
1361 m = re.search('(\d\d):(\d\d):(\d\d)', isotime)
1363 tme = time(int(m.group(1)), int(m.group(2)), int(m.group(3)))
1365 m = re.search('(\d\d):(\d\d)', isotime)
1367 tme = time(int(m.group(1)), int(m.group(2)))
1368 # FIXME if we had the path to the original document (not the one in the tmp dir),
1369 # we could use the mtime.
1370 # elif tpv == "moddate":
1371 # dte = date.fromtimestamp(os.path.getmtime(document.dir))
1374 result = tme.isoformat()
1375 elif argv == "long":
1376 result = tme.strftime(timeformats[lang][0])
1377 elif argv == "short":
1378 result = tme.strftime(timeformats[lang][1])
1380 fmt = argv.replace("HH", "%H").replace("H", "%H").replace("hh", "%I").replace("h", "%I")
1381 fmt = fmt.replace("mm", "%M").replace("m", "%M").replace("ss", "%S").replace("s", "%S")
1382 fmt = fmt.replace("zzz", "%f").replace("z", "%f").replace("t", "%Z")
1383 fmt = fmt.replace("AP", "%p").replace("ap", "%p").replace("A", "%p").replace("a", "%p")
1384 fmt = fmt.replace("'", "")
1385 result = dte.strftime(fmt)
1386 document.body[i : j+1] = result
1389 def revert_namenoextinfo(document):
1390 " Merge buffer Info inset type name-noext to name. "
1394 i = find_token(document.body, "\\begin_inset Info", i+1)
1397 j = find_end_of_inset(document.body, i+1)
1399 document.warning("Malformed LyX document: Could not find end of Info inset.")
1401 tp = find_token(document.body, 'type', i, j)
1402 tpv = get_quoted_value(document.body, "type", tp)
1405 arg = find_token(document.body, 'arg', i, j)
1406 argv = get_quoted_value(document.body, "arg", arg)
1407 if argv != "name-noext":
1409 document.body[arg] = "arg \"name\""
1412 def revert_l7ninfo(document):
1413 " Revert l7n Info inset to text. "
1417 i = find_token(document.body, "\\begin_inset Info", i+1)
1420 j = find_end_of_inset(document.body, i+1)
1422 document.warning("Malformed LyX document: Could not find end of Info inset.")
1424 tp = find_token(document.body, 'type', i, j)
1425 tpv = get_quoted_value(document.body, "type", tp)
1428 arg = find_token(document.body, 'arg', i, j)
1429 argv = get_quoted_value(document.body, "arg", arg)
1430 # remove trailing colons, menu accelerator (|...) and qt accelerator (&), while keeping literal " & "
1431 argv = argv.rstrip(':').split('|')[0].replace(" & ", "</amp;>").replace("&", "").replace("</amp;>", " & ")
1432 document.body[i : j+1] = argv
1435 def revert_listpargs(document):
1436 " Reverts listpreamble arguments to TeX-code "
1439 i = find_token(document.body, "\\begin_inset Argument listpreamble:", i+1)
1442 j = find_end_of_inset(document.body, i)
1443 # Find containing paragraph layout
1444 parent = get_containing_layout(document.body, i)
1446 document.warning("Malformed LyX document: Can't find parent paragraph layout")
1449 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1450 endPlain = find_end_of_layout(document.body, beginPlain)
1451 content = document.body[beginPlain + 1 : endPlain]
1452 del document.body[i:j+1]
1453 subst = ["\\begin_inset ERT", "status collapsed", "", "\\begin_layout Plain Layout",
1454 "{"] + content + ["}", "\\end_layout", "", "\\end_inset", ""]
1455 document.body[parbeg : parbeg] = subst
1458 def revert_lformatinfo(document):
1459 " Revert layout format Info inset to text. "
1463 i = find_token(document.body, "\\begin_inset Info", i+1)
1466 j = find_end_of_inset(document.body, i+1)
1468 document.warning("Malformed LyX document: Could not find end of Info inset.")
1470 tp = find_token(document.body, 'type', i, j)
1471 tpv = get_quoted_value(document.body, "type", tp)
1472 if tpv != "lyxinfo":
1474 arg = find_token(document.body, 'arg', i, j)
1475 argv = get_quoted_value(document.body, "arg", arg)
1476 if argv != "layoutformat":
1479 document.body[i : j+1] = "69"
1482 def convert_hebrew_parentheses(document):
1483 """ Swap opening/closing parentheses in Hebrew text.
1485 Up to LyX 2.4, "(" was used as closing parenthesis and
1486 ")" as opening parenthesis for Hebrew in the LyX source.
1488 # print("convert hebrew parentheses")
1489 current_languages = [document.language]
1490 for i, line in enumerate(document.body):
1491 if line.startswith('\\lang '):
1492 current_languages[-1] = line.lstrip('\\lang ')
1493 elif line.startswith('\\begin_layout'):
1494 current_languages.append(current_languages[-1])
1495 # print (line, current_languages[-1])
1496 elif line.startswith('\\end_layout'):
1497 current_languages.pop()
1498 elif current_languages[-1] == 'hebrew' and not line.startswith('\\'):
1499 document.body[i] = line.replace('(','\x00').replace(')','(').replace('\x00',')')
1502 def revert_hebrew_parentheses(document):
1503 " Store parentheses in Hebrew text reversed"
1504 # This only exists to keep the convert/revert naming convention
1505 convert_hebrew_parentheses(document)
1508 def revert_malayalam(document):
1509 " Set the document language to English but assure Malayalam output "
1511 revert_language(document, "malayalam", "", "malayalam")
1514 def revert_soul(document):
1515 " Revert soul module flex insets to ERT "
1517 flexes = ["Spaceletters", "Strikethrough", "Underline", "Highlight", "Capitalize"]
1520 i = find_token(document.body, "\\begin_inset Flex %s" % flex, 0)
1522 add_to_preamble(document, ["\\usepackage{soul}"])
1524 i = find_token(document.body, "\\begin_inset Flex Highlight", 0)
1526 add_to_preamble(document, ["\\usepackage{color}"])
1528 revert_flex_inset(document.body, "Spaceletters", "\\so")
1529 revert_flex_inset(document.body, "Strikethrough", "\\st")
1530 revert_flex_inset(document.body, "Underline", "\\ul")
1531 revert_flex_inset(document.body, "Highlight", "\\hl")
1532 revert_flex_inset(document.body, "Capitalize", "\\caps")
1535 def revert_tablestyle(document):
1536 " Remove tablestyle params "
1539 i = find_token(document.header, "\\tablestyle")
1541 del document.header[i]
1544 def revert_bibfileencodings(document):
1545 " Revert individual Biblatex bibliography encodings "
1549 i = find_token(document.header, "\\cite_engine", 0)
1551 document.warning("Malformed document! Missing \\cite_engine")
1553 engine = get_value(document.header, "\\cite_engine", i)
1557 if engine in ["biblatex", "biblatex-natbib"]:
1560 # Map lyx to latex encoding names
1564 "armscii8" : "armscii8",
1565 "iso8859-1" : "latin1",
1566 "iso8859-2" : "latin2",
1567 "iso8859-3" : "latin3",
1568 "iso8859-4" : "latin4",
1569 "iso8859-5" : "iso88595",
1570 "iso8859-6" : "8859-6",
1571 "iso8859-7" : "iso-8859-7",
1572 "iso8859-8" : "8859-8",
1573 "iso8859-9" : "latin5",
1574 "iso8859-13" : "latin7",
1575 "iso8859-15" : "latin9",
1576 "iso8859-16" : "latin10",
1577 "applemac" : "applemac",
1579 "cp437de" : "cp437de",
1587 "cp1250" : "cp1250",
1588 "cp1251" : "cp1251",
1589 "cp1252" : "cp1252",
1590 "cp1255" : "cp1255",
1591 "cp1256" : "cp1256",
1592 "cp1257" : "cp1257",
1593 "koi8-r" : "koi8-r",
1594 "koi8-u" : "koi8-u",
1596 "utf8-platex" : "utf8",
1603 i = find_token(document.body, "\\begin_inset CommandInset bibtex", i+1)
1606 j = find_end_of_inset(document.body, i)
1608 document.warning("Can't find end of bibtex inset at line %d!!" %(i))
1610 encodings = get_quoted_value(document.body, "file_encodings", i, j)
1614 bibfiles = get_quoted_value(document.body, "bibfiles", i, j).split(",")
1615 opts = get_quoted_value(document.body, "biblatexopts", i, j)
1616 if len(bibfiles) == 0:
1617 document.warning("Bibtex inset at line %d does not have a bibfile!" %(i))
1618 # remove encoding line
1619 k = find_token(document.body, "file_encodings", i, j)
1621 del document.body[k]
1622 # Re-find inset end line
1623 j = find_end_of_inset(document.body, i)
1625 enclist = encodings.split("\t")
1628 ppp = pp.split(" ", 1)
1629 encmap[ppp[0]] = ppp[1]
1630 for bib in bibfiles:
1631 pr = "\\addbibresource"
1632 if bib in encmap.keys():
1633 pr += "[bibencoding=" + encmap[bib] + "]"
1634 pr += "{" + bib + "}"
1635 add_to_preamble(document, [pr])
1636 # Insert ERT \\printbibliography and wrap bibtex inset to a Note
1637 pcmd = "printbibliography"
1639 pcmd += "[" + opts + "]"
1640 repl = ["\\begin_inset ERT", "status open", "", "\\begin_layout Plain Layout",\
1641 "", "", "\\backslash", pcmd, "\\end_layout", "", "\\end_inset", "", "",\
1642 "\\end_layout", "", "\\begin_layout Standard", "\\begin_inset Note Note",\
1643 "status open", "", "\\begin_layout Plain Layout" ]
1644 repl += document.body[i:j+1]
1645 repl += ["", "\\end_layout", "", "\\end_inset", "", ""]
1646 document.body[i:j+1] = repl
1652 def revert_cmidruletrimming(document):
1653 " Remove \\cmidrule trimming "
1655 # FIXME: Revert to TeX code?
1658 # first, let's find out if we need to do anything
1659 i = find_token(document.body, '<cell ', i+1)
1662 j = document.body[i].find('trim="')
1665 rgx = re.compile(r' (bottom|top)line[lr]trim="true"')
1666 # remove trim option
1667 document.body[i] = rgx.sub('', document.body[i])
1671 r'### Inserted by lyx2lyx (ruby inset) ###',
1672 r'InsetLayout Flex:Ruby',
1673 r' LyxType charstyle',
1674 r' LatexType command',
1678 r' HTMLInnerTag rb',
1679 r' HTMLInnerAttr ""',
1681 r' LabelString "Ruby"',
1682 r' Decoration Conglomerate',
1684 r' \ifdefined\kanjiskip',
1685 r' \IfFileExists{okumacro.sty}{\usepackage{okumacro}}{}',
1686 r' \else \ifdefined\luatexversion',
1687 r' \usepackage{luatexja-ruby}',
1688 r' \else \ifdefined\XeTeXversion',
1689 r' \usepackage{ruby}%',
1691 r' \providecommand{\ruby}[2]{\shortstack{\tiny #2\\#1}}',
1693 r' Argument post:1',
1694 r' LabelString "ruby text"',
1695 r' MenuString "Ruby Text|R"',
1696 r' Tooltip "Reading aid (ruby, furigana) for Chinese characters."',
1697 r' Decoration Conglomerate',
1709 def convert_ruby_module(document):
1710 " Use ruby module instead of local module definition "
1711 if document.del_local_layout(ruby_inset_def):
1712 document.add_module("ruby")
1714 def revert_ruby_module(document):
1715 " Replace ruby module with local module definition "
1716 if document.del_module("ruby"):
1717 document.append_local_layout(ruby_inset_def)
1720 def convert_utf8_japanese(document):
1721 " Use generic utf8 with Japanese documents."
1722 lang = get_value(document.header, "\\language")
1723 if not lang.startswith("japanese"):
1725 inputenc = get_value(document.header, "\\inputencoding")
1726 if ((lang == "japanese" and inputenc == "utf8-platex")
1727 or (lang == "japanese-cjk" and inputenc == "utf8-cjk")):
1728 document.set_parameter("inputencoding", "utf8")
1730 def revert_utf8_japanese(document):
1731 " Use Japanese utf8 variants with Japanese documents."
1732 inputenc = get_value(document.header, "\\inputencoding")
1733 if inputenc != "utf8":
1735 lang = get_value(document.header, "\\language")
1736 if lang == "japanese":
1737 document.set_parameter("inputencoding", "utf8-platex")
1738 if lang == "japanese-cjk":
1739 document.set_parameter("inputencoding", "utf8-cjk")
1742 def revert_lineno(document):
1743 " Replace lineno setting with user-preamble code."
1745 options = get_quoted_value(document.header, "\\lineno_options",
1747 if not get_bool_value(document.header, "\\use_lineno", delete=True):
1750 options = "[" + options + "]"
1751 add_to_preamble(document, ["\\usepackage%s{lineno}" % options,
1754 def convert_lineno(document):
1755 " Replace user-preamble code with native lineno support."
1758 i = find_token(document.preamble, "\\linenumbers", 1)
1760 usepkg = re.match(r"\\usepackage(.*){lineno}", document.preamble[i-1])
1763 options = usepkg.group(1).strip("[]")
1764 del(document.preamble[i-1:i+1])
1765 del_token(document.preamble, "% Added by lyx2lyx", i-2, i-1)
1767 k = find_token(document.header, "\\index ")
1769 document.header[k:k] = ["\\use_lineno %d" % use_lineno]
1771 document.header[k:k] = ["\\use_lineno %d" % use_lineno,
1772 "\\lineno_options %s" % options]
1775 def revert_new_languages(document):
1776 """Emulate support for Azerbaijani, Bengali, Church Slavonic, Korean,
1777 and Russian (Petrine orthography)."""
1779 # lyxname: (babelname, polyglossianame)
1780 new_languages = {"azerbaijani": ("azerbaijani", ""),
1781 "bengali": ("", "bengali"),
1782 "churchslavonic": ("", "churchslavonic"),
1783 "oldrussian": ("", "russian"),
1784 "korean": ("", "korean"),
1786 used_languages = set()
1787 if document.language in new_languages:
1788 used_languages.add(document.language)
1791 i = find_token(document.body, "\\lang", i+1)
1794 if document.body[i][6:].strip() in new_languages:
1795 used_languages.add(document.language)
1797 # Korean is already supported via CJK, so leave as-is for Babel
1798 if ("korean" in used_languages
1799 and get_bool_value(document.header, "\\use_non_tex_fonts")
1800 and get_value(document.header, "\\language_package") in ("default", "auto")):
1801 revert_language(document, "korean", "", "korean")
1802 used_languages.discard("korean")
1804 for lang in used_languages:
1805 revert(lang, *new_languages[lang])
1809 r'### Inserted by lyx2lyx (deprecated ling glosses) ###',
1810 r'InsetLayout Flex:Glosse',
1812 r' LabelString "Gloss (old version)"',
1813 r' MenuString "Gloss (old version)"',
1814 r' LatexType environment',
1815 r' LatexName linggloss',
1816 r' Decoration minimalistic',
1821 r' CustomPars false',
1822 r' ForcePlain true',
1823 r' ParbreakIsNewline true',
1824 r' FreeSpacing true',
1825 r' Requires covington',
1828 r' \@ifundefined{linggloss}{%',
1829 r' \newenvironment{linggloss}[2][]{',
1830 r' \def\glosstr{\glt #1}%',
1832 r' {\glosstr\glend}}{}',
1835 r' ResetsFont true',
1837 r' Decoration conglomerate',
1838 r' LabelString "Translation"',
1839 r' MenuString "Glosse Translation|s"',
1840 r' Tooltip "Add a translation for the glosse"',
1845 glosss_inset_def = [
1846 r'### Inserted by lyx2lyx (deprecated ling glosses) ###',
1847 r'InsetLayout Flex:Tri-Glosse',
1849 r' LabelString "Tri-Gloss (old version)"',
1850 r' MenuString "Tri-Gloss (old version)"',
1851 r' LatexType environment',
1852 r' LatexName lingglosss',
1853 r' Decoration minimalistic',
1858 r' CustomPars false',
1859 r' ForcePlain true',
1860 r' ParbreakIsNewline true',
1861 r' FreeSpacing true',
1863 r' Requires covington',
1866 r' \@ifundefined{lingglosss}{%',
1867 r' \newenvironment{lingglosss}[2][]{',
1868 r' \def\glosstr{\glt #1}%',
1870 r' {\glosstr\glend}}{}',
1872 r' ResetsFont true',
1874 r' Decoration conglomerate',
1875 r' LabelString "Translation"',
1876 r' MenuString "Glosse Translation|s"',
1877 r' Tooltip "Add a translation for the glosse"',
1882 def convert_linggloss(document):
1883 " Move old ling glosses to local layout "
1884 if find_token(document.body, '\\begin_inset Flex Glosse', 0) != -1:
1885 document.append_local_layout(gloss_inset_def)
1886 if find_token(document.body, '\\begin_inset Flex Tri-Glosse', 0) != -1:
1887 document.append_local_layout(glosss_inset_def)
1889 def revert_linggloss(document):
1890 " Revert to old ling gloss definitions "
1891 if not "linguistics" in document.get_module_list():
1893 document.del_local_layout(gloss_inset_def)
1894 document.del_local_layout(glosss_inset_def)
1897 glosses = ["\\begin_inset Flex Interlinear Gloss (2 Lines)", "\\begin_inset Flex Interlinear Gloss (3 Lines)"]
1898 for glosse in glosses:
1901 i = find_token(document.body, glosse, i+1)
1904 j = find_end_of_inset(document.body, i)
1906 document.warning("Malformed LyX document: Can't find end of Gloss inset")
1909 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
1910 endarg = find_end_of_inset(document.body, arg)
1913 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
1914 if argbeginPlain == -1:
1915 document.warning("Malformed LyX document: Can't find optarg plain Layout")
1917 argendPlain = find_end_of_inset(document.body, argbeginPlain)
1918 optargcontent = document.body[argbeginPlain + 1 : argendPlain - 2]
1920 # remove Arg insets and paragraph, if it only contains this inset
1921 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
1922 del document.body[arg - 1 : endarg + 4]
1924 del document.body[arg : endarg + 1]
1926 arg = find_token(document.body, "\\begin_inset Argument post:1", i, j)
1927 endarg = find_end_of_inset(document.body, arg)
1930 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
1931 if argbeginPlain == -1:
1932 document.warning("Malformed LyX document: Can't find arg 1 plain Layout")
1934 argendPlain = find_end_of_inset(document.body, argbeginPlain)
1935 marg1content = document.body[argbeginPlain + 1 : argendPlain - 2]
1937 # remove Arg insets and paragraph, if it only contains this inset
1938 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
1939 del document.body[arg - 1 : endarg + 4]
1941 del document.body[arg : endarg + 1]
1943 arg = find_token(document.body, "\\begin_inset Argument post:2", i, j)
1944 endarg = find_end_of_inset(document.body, arg)
1947 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
1948 if argbeginPlain == -1:
1949 document.warning("Malformed LyX document: Can't find arg 2 plain Layout")
1951 argendPlain = find_end_of_inset(document.body, argbeginPlain)
1952 marg2content = document.body[argbeginPlain + 1 : argendPlain - 2]
1954 # remove Arg insets and paragraph, if it only contains this inset
1955 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
1956 del document.body[arg - 1 : endarg + 4]
1958 del document.body[arg : endarg + 1]
1960 arg = find_token(document.body, "\\begin_inset Argument post:3", i, j)
1961 endarg = find_end_of_inset(document.body, arg)
1964 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
1965 if argbeginPlain == -1:
1966 document.warning("Malformed LyX document: Can't find arg 3 plain Layout")
1968 argendPlain = find_end_of_inset(document.body, argbeginPlain)
1969 marg3content = document.body[argbeginPlain + 1 : argendPlain - 2]
1971 # remove Arg insets and paragraph, if it only contains this inset
1972 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
1973 del document.body[arg - 1 : endarg + 4]
1975 del document.body[arg : endarg + 1]
1978 if glosse == "\\begin_inset Flex Interlinear Gloss (3 Lines)":
1981 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1982 endInset = find_end_of_inset(document.body, i)
1983 endPlain = find_token_backwards(document.body, "\\end_layout", endInset)
1984 precontent = put_cmd_in_ert(cmd)
1985 if len(optargcontent) > 0:
1986 precontent += put_cmd_in_ert("[") + optargcontent + put_cmd_in_ert("]")
1987 precontent += put_cmd_in_ert("{")
1989 postcontent = put_cmd_in_ert("}{") + marg1content + put_cmd_in_ert("}{") + marg2content
1990 if cmd == "\\trigloss":
1991 postcontent += put_cmd_in_ert("}{") + marg3content
1992 postcontent += put_cmd_in_ert("}")
1994 document.body[endPlain:endInset + 1] = postcontent
1995 document.body[beginPlain + 1:beginPlain] = precontent
1996 del document.body[i : beginPlain + 1]
1998 document.append_local_layout("Requires covington")
2003 def revert_subexarg(document):
2004 " Revert linguistic subexamples with argument to ERT "
2006 if not "linguistics" in document.get_module_list():
2012 i = find_token(document.body, "\\begin_layout Subexample", i+1)
2015 j = find_end_of_layout(document.body, i)
2017 document.warning("Malformed LyX document: Can't find end of Subexample layout")
2020 # check for consecutive layouts
2021 k = find_token(document.body, "\\begin_layout", j)
2022 if k == -1 or document.body[k] != "\\begin_layout Subexample":
2024 j = find_end_of_layout(document.body, k)
2026 document.warning("Malformed LyX document: Can't find end of Subexample layout")
2029 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
2033 endarg = find_end_of_inset(document.body, arg)
2035 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
2036 if argbeginPlain == -1:
2037 document.warning("Malformed LyX document: Can't find optarg plain Layout")
2039 argendPlain = find_end_of_inset(document.body, argbeginPlain)
2040 optargcontent = lyx2latex(document, document.body[argbeginPlain + 1 : argendPlain - 2])
2042 # remove Arg insets and paragraph, if it only contains this inset
2043 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
2044 del document.body[arg - 1 : endarg + 4]
2046 del document.body[arg : endarg + 1]
2048 cmd = put_cmd_in_ert("\\begin{subexamples}[" + optargcontent + "]")
2050 # re-find end of layout
2051 j = find_end_of_layout(document.body, i)
2053 document.warning("Malformed LyX document: Can't find end of Subexample layout")
2056 # check for consecutive layouts
2057 k = find_token(document.body, "\\begin_layout", j)
2058 if k == -1 or document.body[k] != "\\begin_layout Subexample":
2060 document.body[k : k + 1] = ["\\begin_layout Standard"] + put_cmd_in_ert("\\item ")
2061 j = find_end_of_layout(document.body, k)
2063 document.warning("Malformed LyX document: Can't find end of Subexample layout")
2066 endev = put_cmd_in_ert("\\end{subexamples}")
2068 document.body[j : j] = ["\\end_layout", "", "\\begin_layout Standard"] + endev
2069 document.body[i : i + 1] = ["\\begin_layout Standard"] + cmd \
2070 + ["\\end_layout", "", "\\begin_layout Standard"] + put_cmd_in_ert("\\item ")
2072 document.append_local_layout("Requires covington")
2076 def revert_drs(document):
2077 " Revert DRS insets (linguistics) to ERT "
2079 if not "linguistics" in document.get_module_list():
2083 drses = ["\\begin_inset Flex DRS", "\\begin_inset Flex DRS*",
2084 "\\begin_inset Flex IfThen-DRS", "\\begin_inset Flex Cond-DRS",
2085 "\\begin_inset Flex QDRS", "\\begin_inset Flex NegDRS",
2086 "\\begin_inset Flex SDRS"]
2090 i = find_token(document.body, drs, i+1)
2093 j = find_end_of_inset(document.body, i)
2095 document.warning("Malformed LyX document: Can't find end of DRS inset")
2098 # Check for arguments
2099 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
2100 endarg = find_end_of_inset(document.body, arg)
2103 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
2104 if argbeginPlain == -1:
2105 document.warning("Malformed LyX document: Can't find Argument 1 plain Layout")
2107 argendPlain = find_end_of_inset(document.body, argbeginPlain)
2108 prearg1content = document.body[argbeginPlain + 1 : argendPlain - 2]
2110 # remove Arg insets and paragraph, if it only contains this inset
2111 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
2112 del document.body[arg - 1 : endarg + 4]
2114 del document.body[arg : endarg + 1]
2117 j = find_end_of_inset(document.body, i)
2119 document.warning("Malformed LyX document: Can't find end of DRS inset")
2122 arg = find_token(document.body, "\\begin_inset Argument 2", i, j)
2123 endarg = find_end_of_inset(document.body, arg)
2126 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
2127 if argbeginPlain == -1:
2128 document.warning("Malformed LyX document: Can't find Argument 2 plain Layout")
2130 argendPlain = find_end_of_inset(document.body, argbeginPlain)
2131 prearg2content = document.body[argbeginPlain + 1 : argendPlain - 2]
2133 # remove Arg insets and paragraph, if it only contains this inset
2134 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
2135 del document.body[arg - 1 : endarg + 4]
2137 del document.body[arg : endarg + 1]
2140 j = find_end_of_inset(document.body, i)
2142 document.warning("Malformed LyX document: Can't find end of DRS inset")
2145 arg = find_token(document.body, "\\begin_inset Argument post:1", i, j)
2146 endarg = find_end_of_inset(document.body, arg)
2147 postarg1content = []
2149 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
2150 if argbeginPlain == -1:
2151 document.warning("Malformed LyX document: Can't find Argument post:1 plain Layout")
2153 argendPlain = find_end_of_inset(document.body, argbeginPlain)
2154 postarg1content = document.body[argbeginPlain + 1 : argendPlain - 2]
2156 # remove Arg insets and paragraph, if it only contains this inset
2157 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
2158 del document.body[arg - 1 : endarg + 4]
2160 del document.body[arg : endarg + 1]
2163 j = find_end_of_inset(document.body, i)
2165 document.warning("Malformed LyX document: Can't find end of DRS inset")
2168 arg = find_token(document.body, "\\begin_inset Argument post:2", i, j)
2169 endarg = find_end_of_inset(document.body, arg)
2170 postarg2content = []
2172 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
2173 if argbeginPlain == -1:
2174 document.warning("Malformed LyX document: Can't find Argument post:2 plain Layout")
2176 argendPlain = find_end_of_inset(document.body, argbeginPlain)
2177 postarg2content = document.body[argbeginPlain + 1 : argendPlain - 2]
2179 # remove Arg insets and paragraph, if it only contains this inset
2180 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
2181 del document.body[arg - 1 : endarg + 4]
2183 del document.body[arg : endarg + 1]
2186 j = find_end_of_inset(document.body, i)
2188 document.warning("Malformed LyX document: Can't find end of DRS inset")
2191 arg = find_token(document.body, "\\begin_inset Argument post:3", i, j)
2192 endarg = find_end_of_inset(document.body, arg)
2193 postarg3content = []
2195 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
2196 if argbeginPlain == -1:
2197 document.warning("Malformed LyX document: Can't find Argument post:3 plain Layout")
2199 argendPlain = find_end_of_inset(document.body, argbeginPlain)
2200 postarg3content = document.body[argbeginPlain + 1 : argendPlain - 2]
2202 # remove Arg insets and paragraph, if it only contains this inset
2203 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
2204 del document.body[arg - 1 : endarg + 4]
2206 del document.body[arg : endarg + 1]
2209 j = find_end_of_inset(document.body, i)
2211 document.warning("Malformed LyX document: Can't find end of DRS inset")
2214 arg = find_token(document.body, "\\begin_inset Argument post:4", i, j)
2215 endarg = find_end_of_inset(document.body, arg)
2216 postarg4content = []
2218 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
2219 if argbeginPlain == -1:
2220 document.warning("Malformed LyX document: Can't find Argument post:4 plain Layout")
2222 argendPlain = find_end_of_inset(document.body, argbeginPlain)
2223 postarg4content = document.body[argbeginPlain + 1 : argendPlain - 2]
2225 # remove Arg insets and paragraph, if it only contains this inset
2226 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
2227 del document.body[arg - 1 : endarg + 4]
2229 del document.body[arg : endarg + 1]
2231 # The respective LaTeX command
2233 if drs == "\\begin_inset Flex DRS*":
2235 elif drs == "\\begin_inset Flex IfThen-DRS":
2237 elif drs == "\\begin_inset Flex Cond-DRS":
2239 elif drs == "\\begin_inset Flex QDRS":
2241 elif drs == "\\begin_inset Flex NegDRS":
2243 elif drs == "\\begin_inset Flex SDRS":
2246 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2247 endInset = find_end_of_inset(document.body, i)
2248 endPlain = find_token_backwards(document.body, "\\end_layout", endInset)
2249 precontent = put_cmd_in_ert(cmd)
2250 precontent += put_cmd_in_ert("{") + prearg1content + put_cmd_in_ert("}")
2251 if drs == "\\begin_inset Flex SDRS":
2252 precontent += put_cmd_in_ert("{") + prearg2content + put_cmd_in_ert("}")
2253 precontent += put_cmd_in_ert("{")
2256 if cmd == "\\qdrs" or cmd == "\\condrs" or cmd == "\\ifdrs":
2257 postcontent = put_cmd_in_ert("}{") + postarg1content + put_cmd_in_ert("}{") + postarg2content + put_cmd_in_ert("}")
2258 if cmd == "\\condrs" or cmd == "\\qdrs":
2259 postcontent += put_cmd_in_ert("{") + postarg3content + put_cmd_in_ert("}")
2261 postcontent += put_cmd_in_ert("{") + postarg4content + put_cmd_in_ert("}")
2263 postcontent = put_cmd_in_ert("}")
2265 document.body[endPlain:endInset + 1] = postcontent
2266 document.body[beginPlain + 1:beginPlain] = precontent
2267 del document.body[i : beginPlain + 1]
2269 document.append_local_layout("Provides covington 1")
2270 add_to_preamble(document, ["\\usepackage{drs,covington}"])
2276 def revert_babelfont(document):
2277 " Reverts the use of \\babelfont to user preamble "
2279 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2281 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2283 if not str2bool(get_value(document.header, "\\use_non_tex_fonts", i)):
2285 i = find_token(document.header, '\\language_package', 0)
2287 document.warning("Malformed LyX document: Missing \\language_package.")
2289 if get_value(document.header, "\\language_package", 0) != "babel":
2292 # check font settings
2294 roman = sans = typew = "default"
2296 sf_scale = tt_scale = 100.0
2298 j = find_token(document.header, "\\font_roman", 0)
2300 document.warning("Malformed LyX document: Missing \\font_roman.")
2302 # We need to use this regex since split() does not handle quote protection
2303 romanfont = re.findall(r'[^"\s]\S*|".+?"', document.header[j])
2304 roman = romanfont[2].strip('"')
2305 romanfont[2] = '"default"'
2306 document.header[j] = " ".join(romanfont)
2308 j = find_token(document.header, "\\font_sans", 0)
2310 document.warning("Malformed LyX document: Missing \\font_sans.")
2312 # We need to use this regex since split() does not handle quote protection
2313 sansfont = re.findall(r'[^"\s]\S*|".+?"', document.header[j])
2314 sans = sansfont[2].strip('"')
2315 sansfont[2] = '"default"'
2316 document.header[j] = " ".join(sansfont)
2318 j = find_token(document.header, "\\font_typewriter", 0)
2320 document.warning("Malformed LyX document: Missing \\font_typewriter.")
2322 # We need to use this regex since split() does not handle quote protection
2323 ttfont = re.findall(r'[^"\s]\S*|".+?"', document.header[j])
2324 typew = ttfont[2].strip('"')
2325 ttfont[2] = '"default"'
2326 document.header[j] = " ".join(ttfont)
2328 i = find_token(document.header, "\\font_osf", 0)
2330 document.warning("Malformed LyX document: Missing \\font_osf.")
2332 osf = str2bool(get_value(document.header, "\\font_osf", i))
2334 j = find_token(document.header, "\\font_sf_scale", 0)
2336 document.warning("Malformed LyX document: Missing \\font_sf_scale.")
2338 sfscale = document.header[j].split()
2341 document.header[j] = " ".join(sfscale)
2344 sf_scale = float(val)
2346 document.warning("Invalid font_sf_scale value: " + val)
2348 j = find_token(document.header, "\\font_tt_scale", 0)
2350 document.warning("Malformed LyX document: Missing \\font_tt_scale.")
2352 ttscale = document.header[j].split()
2355 document.header[j] = " ".join(ttscale)
2358 tt_scale = float(val)
2360 document.warning("Invalid font_tt_scale value: " + val)
2362 # set preamble stuff
2363 pretext = ['%% This document must be processed with xelatex or lualatex!']
2364 pretext.append('\\AtBeginDocument{%')
2365 if roman != "default":
2366 pretext.append('\\babelfont{rm}[Mapping=tex-text]{' + roman + '}')
2367 if sans != "default":
2368 sf = '\\babelfont{sf}['
2369 if sf_scale != 100.0:
2370 sf += 'Scale=' + str(sf_scale / 100.0) + ','
2371 sf += 'Mapping=tex-text]{' + sans + '}'
2373 if typew != "default":
2374 tw = '\\babelfont{tt}'
2375 if tt_scale != 100.0:
2376 tw += '[Scale=' + str(tt_scale / 100.0) + ']'
2377 tw += '{' + typew + '}'
2380 pretext.append('\\defaultfontfeatures{Numbers=OldStyle}')
2382 insert_to_preamble(document, pretext)
2385 def revert_minionpro(document):
2386 " Revert native MinionPro font definition (with extra options) to LaTeX "
2388 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2390 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2392 if str2bool(get_value(document.header, "\\use_non_tex_fonts", i)):
2395 regexp = re.compile(r'(\\font_roman_opts)')
2396 x = find_re(document.header, regexp, 0)
2400 # We need to use this regex since split() does not handle quote protection
2401 romanopts = re.findall(r'[^"\s]\S*|".+?"', document.header[x])
2402 opts = romanopts[1].strip('"')
2404 i = find_token(document.header, "\\font_roman", 0)
2406 document.warning("Malformed LyX document: Missing \\font_roman.")
2409 # We need to use this regex since split() does not handle quote protection
2410 romanfont = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2411 roman = romanfont[1].strip('"')
2412 if roman != "minionpro":
2414 romanfont[1] = '"default"'
2415 document.header[i] = " ".join(romanfont)
2417 j = find_token(document.header, "\\font_osf true", 0)
2420 preamble = "\\usepackage["
2422 document.header[j] = "\\font_osf false"
2426 preamble += "]{MinionPro}"
2427 add_to_preamble(document, [preamble])
2428 del document.header[x]
2431 def revert_font_opts(document):
2432 " revert font options by outputting \\setxxxfont or \\babelfont to the preamble "
2434 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2436 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2438 NonTeXFonts = str2bool(get_value(document.header, "\\use_non_tex_fonts", i))
2439 i = find_token(document.header, '\\language_package', 0)
2441 document.warning("Malformed LyX document: Missing \\language_package.")
2443 Babel = (get_value(document.header, "\\language_package", 0) == "babel")
2446 regexp = re.compile(r'(\\font_roman_opts)')
2447 i = find_re(document.header, regexp, 0)
2449 # We need to use this regex since split() does not handle quote protection
2450 romanopts = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2451 opts = romanopts[1].strip('"')
2452 del document.header[i]
2454 regexp = re.compile(r'(\\font_roman)')
2455 i = find_re(document.header, regexp, 0)
2457 # We need to use this regex since split() does not handle quote protection
2458 romanfont = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2459 font = romanfont[2].strip('"')
2460 romanfont[2] = '"default"'
2461 document.header[i] = " ".join(romanfont)
2462 if font != "default":
2464 preamble = "\\babelfont{rm}["
2466 preamble = "\\setmainfont["
2469 preamble += "Mapping=tex-text]{"
2472 add_to_preamble(document, [preamble])
2475 regexp = re.compile(r'(\\font_sans_opts)')
2476 i = find_re(document.header, regexp, 0)
2479 # We need to use this regex since split() does not handle quote protection
2480 sfopts = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2481 opts = sfopts[1].strip('"')
2482 del document.header[i]
2484 regexp = re.compile(r'(\\font_sf_scale)')
2485 i = find_re(document.header, regexp, 0)
2487 scaleval = get_value(document.header, "\\font_sf_scale" , i).split()[1]
2488 regexp = re.compile(r'(\\font_sans)')
2489 i = find_re(document.header, regexp, 0)
2491 # We need to use this regex since split() does not handle quote protection
2492 sffont = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2493 font = sffont[2].strip('"')
2494 sffont[2] = '"default"'
2495 document.header[i] = " ".join(sffont)
2496 if font != "default":
2498 preamble = "\\babelfont{sf}["
2500 preamble = "\\setsansfont["
2504 preamble += "Scale=0."
2505 preamble += scaleval
2507 preamble += "Mapping=tex-text]{"
2510 add_to_preamble(document, [preamble])
2513 regexp = re.compile(r'(\\font_typewriter_opts)')
2514 i = find_re(document.header, regexp, 0)
2517 # We need to use this regex since split() does not handle quote protection
2518 ttopts = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2519 opts = ttopts[1].strip('"')
2520 del document.header[i]
2522 regexp = re.compile(r'(\\font_tt_scale)')
2523 i = find_re(document.header, regexp, 0)
2525 scaleval = get_value(document.header, "\\font_tt_scale" , i).split()[1]
2526 regexp = re.compile(r'(\\font_typewriter)')
2527 i = find_re(document.header, regexp, 0)
2529 # We need to use this regex since split() does not handle quote protection
2530 ttfont = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2531 font = ttfont[2].strip('"')
2532 ttfont[2] = '"default"'
2533 document.header[i] = " ".join(ttfont)
2534 if font != "default":
2536 preamble = "\\babelfont{tt}["
2538 preamble = "\\setmonofont["
2542 preamble += "Scale=0."
2543 preamble += scaleval
2545 preamble += "Mapping=tex-text]{"
2548 add_to_preamble(document, [preamble])
2551 def revert_plainNotoFonts_xopts(document):
2552 " Revert native (straight) Noto font definition (with extra options) to LaTeX "
2554 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2556 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2558 if str2bool(get_value(document.header, "\\use_non_tex_fonts", i)):
2562 y = find_token(document.header, "\\font_osf true", 0)
2566 regexp = re.compile(r'(\\font_roman_opts)')
2567 x = find_re(document.header, regexp, 0)
2568 if x == -1 and not osf:
2573 # We need to use this regex since split() does not handle quote protection
2574 romanopts = re.findall(r'[^"\s]\S*|".+?"', document.header[x])
2575 opts = romanopts[1].strip('"')
2581 i = find_token(document.header, "\\font_roman", 0)
2585 # We need to use this regex since split() does not handle quote protection
2586 romanfont = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2587 roman = romanfont[1].strip('"')
2588 if roman != "NotoSerif-TLF":
2591 j = find_token(document.header, "\\font_sans", 0)
2595 # We need to use this regex since split() does not handle quote protection
2596 sffont = re.findall(r'[^"\s]\S*|".+?"', document.header[j])
2597 sf = sffont[1].strip('"')
2601 j = find_token(document.header, "\\font_typewriter", 0)
2605 # We need to use this regex since split() does not handle quote protection
2606 ttfont = re.findall(r'[^"\s]\S*|".+?"', document.header[j])
2607 tt = ttfont[1].strip('"')
2611 # So we have noto as "complete font"
2612 romanfont[1] = '"default"'
2613 document.header[i] = " ".join(romanfont)
2615 preamble = "\\usepackage["
2617 preamble += "]{noto}"
2618 add_to_preamble(document, [preamble])
2620 document.header[y] = "\\font_osf false"
2622 del document.header[x]
2625 def revert_notoFonts_xopts(document):
2626 " Revert native (extended) Noto font definition (with extra options) to LaTeX "
2628 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2630 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2632 if str2bool(get_value(document.header, "\\use_non_tex_fonts", i)):
2636 fm = createFontMapping(['Noto'])
2637 if revert_fonts(document, fm, fontmap, True):
2638 add_preamble_fonts(document, fontmap)
2641 def revert_IBMFonts_xopts(document):
2642 " Revert native IBM font definition (with extra options) to LaTeX "
2644 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2646 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2648 if str2bool(get_value(document.header, "\\use_non_tex_fonts", i)):
2652 fm = createFontMapping(['IBM'])
2654 if revert_fonts(document, fm, fontmap, True):
2655 add_preamble_fonts(document, fontmap)
2658 def revert_AdobeFonts_xopts(document):
2659 " Revert native Adobe font definition (with extra options) to LaTeX "
2661 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2663 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2665 if str2bool(get_value(document.header, "\\use_non_tex_fonts", i)):
2669 fm = createFontMapping(['Adobe'])
2671 if revert_fonts(document, fm, fontmap, True):
2672 add_preamble_fonts(document, fontmap)
2675 def convert_osf(document):
2676 " Convert \\font_osf param to new format "
2679 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2681 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2683 NonTeXFonts = str2bool(get_value(document.header, "\\use_non_tex_fonts", i))
2685 i = find_token(document.header, '\\font_osf', 0)
2687 document.warning("Malformed LyX document: Missing \\font_osf.")
2690 osfsf = ["biolinum", "ADOBESourceSansPro", "NotoSansRegular", "NotoSansMedium", "NotoSansThin", "NotoSansLight", "NotoSansExtralight" ]
2691 osftt = ["ADOBESourceCodePro", "NotoMonoRegular" ]
2693 osfval = str2bool(get_value(document.header, "\\font_osf", i))
2694 document.header[i] = document.header[i].replace("\\font_osf", "\\font_roman_osf")
2697 document.header.insert(i, "\\font_sans_osf false")
2698 document.header.insert(i + 1, "\\font_typewriter_osf false")
2702 x = find_token(document.header, "\\font_sans", 0)
2704 document.warning("Malformed LyX document: Missing \\font_sans.")
2706 # We need to use this regex since split() does not handle quote protection
2707 sffont = re.findall(r'[^"\s]\S*|".+?"', document.header[x])
2708 sf = sffont[1].strip('"')
2710 document.header.insert(i, "\\font_sans_osf true")
2712 document.header.insert(i, "\\font_sans_osf false")
2714 x = find_token(document.header, "\\font_typewriter", 0)
2716 document.warning("Malformed LyX document: Missing \\font_typewriter.")
2718 # We need to use this regex since split() does not handle quote protection
2719 ttfont = re.findall(r'[^"\s]\S*|".+?"', document.header[x])
2720 tt = ttfont[1].strip('"')
2722 document.header.insert(i + 1, "\\font_typewriter_osf true")
2724 document.header.insert(i + 1, "\\font_typewriter_osf false")
2727 document.header.insert(i, "\\font_sans_osf false")
2728 document.header.insert(i + 1, "\\font_typewriter_osf false")
2731 def revert_osf(document):
2732 " Revert \\font_*_osf params "
2735 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2737 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2739 NonTeXFonts = str2bool(get_value(document.header, "\\use_non_tex_fonts", i))
2741 i = find_token(document.header, '\\font_roman_osf', 0)
2743 document.warning("Malformed LyX document: Missing \\font_roman_osf.")
2746 osfval = str2bool(get_value(document.header, "\\font_roman_osf", i))
2747 document.header[i] = document.header[i].replace("\\font_roman_osf", "\\font_osf")
2749 i = find_token(document.header, '\\font_sans_osf', 0)
2751 document.warning("Malformed LyX document: Missing \\font_sans_osf.")
2754 osfval = str2bool(get_value(document.header, "\\font_sans_osf", i))
2755 del document.header[i]
2757 i = find_token(document.header, '\\font_typewriter_osf', 0)
2759 document.warning("Malformed LyX document: Missing \\font_typewriter_osf.")
2762 osfval |= str2bool(get_value(document.header, "\\font_typewriter_osf", i))
2763 del document.header[i]
2766 i = find_token(document.header, '\\font_osf', 0)
2768 document.warning("Malformed LyX document: Missing \\font_osf.")
2770 document.header[i] = "\\font_osf true"
2773 def revert_texfontopts(document):
2774 " Revert native TeX font definitions (with extra options) to LaTeX "
2776 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2778 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2780 if str2bool(get_value(document.header, "\\use_non_tex_fonts", i)):
2783 rmfonts = ["ccfonts", "cochineal", "utopia", "garamondx", "libertine", "lmodern", "palatino", "times", "xcharter" ]
2785 # First the sf (biolinum only)
2786 regexp = re.compile(r'(\\font_sans_opts)')
2787 x = find_re(document.header, regexp, 0)
2789 # We need to use this regex since split() does not handle quote protection
2790 sfopts = re.findall(r'[^"\s]\S*|".+?"', document.header[x])
2791 opts = sfopts[1].strip('"')
2792 i = find_token(document.header, "\\font_sans", 0)
2794 document.warning("Malformed LyX document: Missing \\font_sans.")
2796 # We need to use this regex since split() does not handle quote protection
2797 sffont = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2798 sans = sffont[1].strip('"')
2799 if sans == "biolinum":
2801 sffont[1] = '"default"'
2802 document.header[i] = " ".join(sffont)
2804 j = find_token(document.header, "\\font_sans_osf true", 0)
2807 k = find_token(document.header, "\\font_sf_scale", 0)
2809 document.warning("Malformed LyX document: Missing \\font_sf_scale.")
2811 sfscale = document.header[k].split()
2814 document.header[k] = " ".join(sfscale)
2817 sf_scale = float(val)
2819 document.warning("Invalid font_sf_scale value: " + val)
2820 preamble = "\\usepackage["
2822 document.header[j] = "\\font_sans_osf false"
2824 if sf_scale != 100.0:
2825 preamble += 'scaled=' + str(sf_scale / 100.0) + ','
2827 preamble += "]{biolinum}"
2828 add_to_preamble(document, [preamble])
2829 del document.header[x]
2831 regexp = re.compile(r'(\\font_roman_opts)')
2832 x = find_re(document.header, regexp, 0)
2836 # We need to use this regex since split() does not handle quote protection
2837 romanopts = re.findall(r'[^"\s]\S*|".+?"', document.header[x])
2838 opts = romanopts[1].strip('"')
2840 i = find_token(document.header, "\\font_roman", 0)
2842 document.warning("Malformed LyX document: Missing \\font_roman.")
2845 # We need to use this regex since split() does not handle quote protection
2846 romanfont = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2847 roman = romanfont[1].strip('"')
2848 if not roman in rmfonts:
2850 romanfont[1] = '"default"'
2851 document.header[i] = " ".join(romanfont)
2853 if roman == "utopia":
2855 elif roman == "palatino":
2856 package = "mathpazo"
2857 elif roman == "times":
2858 package = "mathptmx"
2859 elif roman == "xcharter":
2860 package = "XCharter"
2862 j = find_token(document.header, "\\font_roman_osf true", 0)
2864 if roman == "cochineal":
2865 osf = "proportional,osf,"
2866 elif roman == "utopia":
2868 elif roman == "garamondx":
2870 elif roman == "libertine":
2872 elif roman == "palatino":
2874 elif roman == "xcharter":
2876 document.header[j] = "\\font_roman_osf false"
2877 k = find_token(document.header, "\\font_sc true", 0)
2879 if roman == "utopia":
2881 if roman == "palatino" and osf == "":
2883 document.header[k] = "\\font_sc false"
2884 preamble = "\\usepackage["
2887 preamble += "]{" + package + "}"
2888 add_to_preamble(document, [preamble])
2889 del document.header[x]
2892 def convert_CantarellFont(document):
2893 " Handle Cantarell font definition to LaTeX "
2895 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
2896 fm = createFontMapping(['Cantarell'])
2897 convert_fonts(document, fm, "oldstyle")
2899 def revert_CantarellFont(document):
2900 " Revert native Cantarell font definition to LaTeX "
2902 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
2904 fm = createFontMapping(['Cantarell'])
2905 if revert_fonts(document, fm, fontmap, False, True):
2906 add_preamble_fonts(document, fontmap)
2913 supported_versions = ["2.4.0", "2.4"]
2915 [545, [convert_lst_literalparam]],
2920 [550, [convert_fontenc]],
2927 [557, [convert_vcsinfo]],
2928 [558, [removeFrontMatterStyles]],
2931 [561, [convert_latexFonts]], # Handle dejavu, ibmplex fonts in GUI
2935 [565, [convert_AdobeFonts]], # Handle adobe fonts in GUI
2936 [566, [convert_hebrew_parentheses]],
2942 [572, [convert_notoFonts]], # Added options thin, light, extralight for Noto
2943 [573, [convert_inputencoding_namechange]],
2944 [574, [convert_ruby_module, convert_utf8_japanese]],
2945 [575, [convert_lineno]],
2947 [577, [convert_linggloss]],
2951 [581, [convert_osf]],
2952 [582, [convert_AdobeFonts,convert_latexFonts,convert_notoFonts,convert_CantarellFont]],# old font re-converterted due to extra options
2955 revert = [[581, [revert_CantarellFont]],
2956 [580, [revert_texfontopts,revert_osf]],
2957 [579, [revert_minionpro, revert_plainNotoFonts_xopts, revert_notoFonts_xopts, revert_IBMFonts_xopts, revert_AdobeFonts_xopts, revert_font_opts]], # keep revert_font_opts last!
2958 [578, [revert_babelfont]],
2959 [577, [revert_drs]],
2960 [576, [revert_linggloss, revert_subexarg]],
2961 [575, [revert_new_languages]],
2962 [574, [revert_lineno]],
2963 [573, [revert_ruby_module, revert_utf8_japanese]],
2964 [572, [revert_inputencoding_namechange]],
2965 [571, [revert_notoFonts]],
2966 [570, [revert_cmidruletrimming]],
2967 [569, [revert_bibfileencodings]],
2968 [568, [revert_tablestyle]],
2969 [567, [revert_soul]],
2970 [566, [revert_malayalam]],
2971 [565, [revert_hebrew_parentheses]],
2972 [564, [revert_AdobeFonts]],
2973 [563, [revert_lformatinfo]],
2974 [562, [revert_listpargs]],
2975 [561, [revert_l7ninfo]],
2976 [560, [revert_latexFonts]], # Handle dejavu, ibmplex fonts in user preamble
2977 [559, [revert_timeinfo, revert_namenoextinfo]],
2978 [558, [revert_dateinfo]],
2979 [557, [addFrontMatterStyles]],
2980 [556, [revert_vcsinfo]],
2981 [555, [revert_bibencoding]],
2982 [554, [revert_vcolumns]],
2983 [553, [revert_stretchcolumn]],
2984 [552, [revert_tuftecite]],
2985 [551, [revert_floatpclass, revert_floatalignment]],
2986 [550, [revert_nospellcheck]],
2987 [549, [revert_fontenc]],
2988 [548, []],# dummy format change
2989 [547, [revert_lscape]],
2990 [546, [revert_xcharter]],
2991 [545, [revert_paratype]],
2992 [544, [revert_lst_literalparam]]
2996 if __name__ == "__main__":