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):
182 " Handle font definition (LaTeX preamble -> native) "
184 rpkg = re.compile(r'^\\usepackage(\[([^\]]*)\])?\{([^\}]+)\}')
185 rscaleopt = re.compile(r'^scaled?=(.*)')
188 while i < len(document.preamble):
189 i = find_re(document.preamble, rpkg, i+1)
192 mo = rpkg.search(document.preamble[i])
193 if mo == None or mo.group(2) == None:
196 options = mo.group(2).replace(' ', '').split(",")
202 while o < len(options):
203 if options[o] == osfoption:
207 mo = rscaleopt.search(options[o])
215 if not pkg in fm.pkginmap:
218 fn = fm.getfontname(pkg, options)
221 del document.preamble[i]
222 fontinfo = fm.font2pkgmap[fn]
223 if fontinfo.scaletype == None:
226 fontscale = "\\font_" + fontinfo.scaletype + "_scale"
227 fontinfo.scaleval = oscale
229 if fontinfo.osfopt == None:
230 options.extend("osf")
232 osf = find_token(document.header, "\\font_osf false")
234 document.header[osf] = "\\font_osf true"
235 if i > 0 and document.preamble[i-1] == "% Added by lyx2lyx":
236 del document.preamble[i-1]
238 if fontscale != None:
239 j = find_token(document.header, fontscale, 0)
241 val = get_value(document.header, fontscale, j)
245 scale = "%03d" % int(float(oscale) * 100)
246 document.header[j] = fontscale + " " + scale + " " + vals[1]
247 ft = "\\font_" + fontinfo.fonttype
248 j = find_token(document.header, ft, 0)
250 val = get_value(document.header, ft, j)
251 words = val.split() # ! splits also values like '"DejaVu Sans"'
252 words[0] = '"' + fn + '"'
253 document.header[j] = ft + ' ' + ' '.join(words)
255 def revert_fonts(document, fm, fontmap, OnlyWithXOpts = False):
256 " Revert native font definition to LaTeX "
257 # fonlist := list of fonts created from the same package
258 # Empty package means that the font-name is the same as the package-name
259 # fontmap (key = package, val += found options) will be filled
260 # and used later in add_preamble_fonts() to be added to user-preamble
262 rfontscale = re.compile(r'^\s*(\\font_(roman|sans|typewriter|math))\s+')
263 rscales = re.compile(r'^\s*(\d+)\s+(\d+)')
265 while i < len(document.header):
266 i = find_re(document.header, rfontscale, i+1)
269 mo = rfontscale.search(document.header[i])
272 ft = mo.group(1) # 'roman', 'sans', 'typewriter', 'math'
273 val = get_value(document.header, ft, i)
274 words = val.split(' ') # ! splits also values like '"DejaVu Sans"'
275 font = words[0].strip('"') # TeX font name has no whitespace
276 if not font in fm.font2pkgmap:
278 fontinfo = fm.font2pkgmap[font]
279 val = fontinfo.package
280 if not val in fontmap:
284 if ft == "\\font_math":
286 regexp = re.compile(r'^\s*(\\font_roman_opts)\s+')
287 if ft == "\\font_sans":
288 regexp = re.compile(r'^\s*(\\font_sans_opts)\s+')
289 elif ft == "\\font_typewriter":
290 regexp = re.compile(r'^\s*(\\font_typewriter_opts)\s+')
291 x = find_re(document.header, regexp, 0)
295 # We need to use this regex since split() does not handle quote protection
296 xopts = re.findall(r'[^"\s]\S*|".+?"', document.header[x])
297 opts = xopts[1].strip('"').split(",")
298 fontmap[val].extend(opts)
299 del document.header[x]
300 words[0] = '"default"'
301 document.header[i] = ft + ' ' + ' '.join(words)
302 if fontinfo.scaleopt != None:
303 xval = get_value(document.header, "\\font_" + fontinfo.scaletype + "_scale", 0)
304 mo = rscales.search(xval)
309 # set correct scale option
310 fontmap[val].extend([fontinfo.scaleopt + "=" + format(float(xval1) / 100, '.2f')])
311 if fontinfo.osfopt != None:
312 osf = find_token(document.header, "\\font_osf true")
314 fontmap[val].extend([fontinfo.osfopt])
315 if len(fontinfo.options) > 0:
316 fontmap[val].extend(fontinfo.options)
319 ###############################################################################
321 ### Conversion and reversion routines
323 ###############################################################################
325 def convert_inputencoding_namechange(document):
326 " Rename inputencoding settings. "
327 i = find_token(document.header, "\\inputencoding", 0)
330 s = document.header[i].replace("auto", "auto-legacy")
331 document.header[i] = s.replace("default", "auto-legacy-plain")
333 def revert_inputencoding_namechange(document):
334 " Rename inputencoding settings. "
335 i = find_token(document.header, "\\inputencoding", 0)
338 s = document.header[i].replace("auto-legacy-plain", "default")
339 document.header[i] = s.replace("auto-legacy", "auto")
341 def convert_notoFonts(document):
342 " Handle Noto fonts definition to LaTeX "
344 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
345 fm = createFontMapping(['Noto'])
346 convert_fonts(document, fm)
348 def revert_notoFonts(document):
349 " Revert native Noto font definition to LaTeX "
351 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
353 fm = createFontMapping(['Noto'])
354 if revert_fonts(document, fm, fontmap):
355 add_preamble_fonts(document, fontmap)
357 def convert_latexFonts(document):
358 " Handle DejaVu and IBMPlex fonts definition to LaTeX "
360 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
361 fm = createFontMapping(['DejaVu', 'IBM'])
362 convert_fonts(document, fm)
364 def revert_latexFonts(document):
365 " Revert native DejaVu font definition to LaTeX "
367 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
369 fm = createFontMapping(['DejaVu', 'IBM'])
370 if revert_fonts(document, fm, fontmap):
371 add_preamble_fonts(document, fontmap)
373 def convert_AdobeFonts(document):
374 " Handle Adobe Source fonts definition to LaTeX "
376 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
377 fm = createFontMapping(['Adobe'])
378 convert_fonts(document, fm)
380 def revert_AdobeFonts(document):
381 " Revert Adobe Source font definition to LaTeX "
383 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
385 fm = createFontMapping(['Adobe'])
386 if revert_fonts(document, fm, fontmap):
387 add_preamble_fonts(document, fontmap)
389 def convert_CantarellFont(document):
390 " Handle Cantarell font definition to LaTeX "
392 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
393 fm = createFontMapping(['Cantarell'])
394 convert_fonts(document, fm)
396 def revert_CantarellFont(document):
397 " Revert native Cantarell font definition to LaTeX "
399 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
401 fm = createFontMapping(['Cantarell'])
402 if revert_fonts(document, fm, fontmap, True):
403 add_preamble_fonts(document, fontmap)
405 def removeFrontMatterStyles(document):
406 " Remove styles Begin/EndFrontmatter"
408 layouts = ['BeginFrontmatter', 'EndFrontmatter']
409 tokenend = len('\\begin_layout ')
412 i = find_token_exact(document.body, '\\begin_layout ', i+1)
415 layout = document.body[i][tokenend:].strip()
416 if layout not in layouts:
418 j = find_end_of_layout(document.body, i)
420 document.warning("Malformed LyX document: Can't find end of layout at line %d" % i)
422 while document.body[j+1].strip() == '':
424 document.body[i:j+1] = []
426 def addFrontMatterStyles(document):
427 " Use styles Begin/EndFrontmatter for elsarticle"
429 if document.textclass != "elsarticle":
432 def insertFrontmatter(prefix, line):
434 while above > 0 and document.body[above-1].strip() == '':
437 while document.body[below].strip() == '':
439 document.body[above:below] = ['', '\\begin_layout ' + prefix + 'Frontmatter',
440 '\\begin_inset Note Note',
442 '\\begin_layout Plain Layout',
445 '\\end_inset', '', '',
448 layouts = ['Title', 'Title footnote', 'Author', 'Author footnote',
449 'Corresponding author', 'Address', 'Email', 'Abstract', 'Keywords']
450 tokenend = len('\\begin_layout ')
454 i = find_token_exact(document.body, '\\begin_layout ', i+1)
457 layout = document.body[i][tokenend:].strip()
458 if layout not in layouts:
460 k = find_end_of_layout(document.body, i)
462 document.warning("Malformed LyX document: Can't find end of layout at line %d" % i)
469 insertFrontmatter('End', k+1)
470 insertFrontmatter('Begin', first)
473 def convert_lst_literalparam(document):
474 " Add param literal to include inset "
478 i = find_token(document.body, '\\begin_inset CommandInset include', i+1)
481 j = find_end_of_inset(document.body, i)
483 document.warning("Malformed LyX document: Can't find end of command inset at line %d" % i)
485 while i < j and document.body[i].strip() != '':
487 document.body.insert(i, 'literal "true"')
490 def revert_lst_literalparam(document):
491 " Remove param literal from include inset "
495 i = find_token(document.body, '\\begin_inset CommandInset include', i+1)
498 j = find_end_of_inset(document.body, i)
500 document.warning("Malformed LyX document: Can't find end of include inset at line %d" % i)
502 del_token(document.body, 'literal', i, j)
505 def revert_paratype(document):
506 " Revert ParaType font definitions to LaTeX "
508 if find_token(document.header, "\\use_non_tex_fonts false", 0) != -1:
510 i1 = find_token(document.header, "\\font_roman \"PTSerif-TLF\"", 0)
511 i2 = find_token(document.header, "\\font_sans \"default\"", 0)
512 i3 = find_token(document.header, "\\font_typewriter \"default\"", 0)
513 j = find_token(document.header, "\\font_sans \"PTSans-TLF\"", 0)
516 sfval = find_token(document.header, "\\font_sf_scale", 0)
518 document.warning("Malformed LyX document: Missing \\font_sf_scale.")
520 sfscale = document.header[sfval].split()
523 document.header[sfval] = " ".join(sfscale)
526 sf_scale = float(val)
528 document.warning("Invalid font_sf_scale value: " + val)
531 if sf_scale != "100.0":
532 sfoption = "scaled=" + str(sf_scale / 100.0)
533 k = find_token(document.header, "\\font_typewriter \"PTMono-TLF\"", 0)
534 ttval = get_value(document.header, "\\font_tt_scale", 0)
539 ttoption = "scaled=" + format(float(ttval) / 100, '.2f')
540 if i1 != -1 and i2 != -1 and i3!= -1:
541 add_to_preamble(document, ["\\usepackage{paratype}"])
544 add_to_preamble(document, ["\\usepackage{PTSerif}"])
545 document.header[i1] = document.header[i1].replace("PTSerif-TLF", "default")
548 add_to_preamble(document, ["\\usepackage[" + sfoption + "]{PTSans}"])
550 add_to_preamble(document, ["\\usepackage{PTSans}"])
551 document.header[j] = document.header[j].replace("PTSans-TLF", "default")
554 add_to_preamble(document, ["\\usepackage[" + ttoption + "]{PTMono}"])
556 add_to_preamble(document, ["\\usepackage{PTMono}"])
557 document.header[k] = document.header[k].replace("PTMono-TLF", "default")
560 def revert_xcharter(document):
561 " Revert XCharter font definitions to LaTeX "
563 i = find_token(document.header, "\\font_roman \"xcharter\"", 0)
567 # replace unsupported font setting
568 document.header[i] = document.header[i].replace("xcharter", "default")
569 # no need for preamble code with system fonts
570 if get_bool_value(document.header, "\\use_non_tex_fonts"):
573 # transfer old style figures setting to package options
574 j = find_token(document.header, "\\font_osf true")
577 document.header[j] = "\\font_osf false"
581 add_to_preamble(document, ["\\usepackage%s{XCharter}"%options])
584 def revert_lscape(document):
585 " Reverts the landscape environment (Landscape module) to TeX-code "
587 if not "landscape" in document.get_module_list():
592 i = find_token(document.body, "\\begin_inset Flex Landscape", i+1)
595 j = find_end_of_inset(document.body, i)
597 document.warning("Malformed LyX document: Can't find end of Landscape inset")
600 if document.body[i] == "\\begin_inset Flex Landscape (Floating)":
601 document.body[j - 2 : j + 1] = put_cmd_in_ert("\\end{landscape}}")
602 document.body[i : i + 4] = put_cmd_in_ert("\\afterpage{\\begin{landscape}")
603 add_to_preamble(document, ["\\usepackage{afterpage}"])
605 document.body[j - 2 : j + 1] = put_cmd_in_ert("\\end{landscape}")
606 document.body[i : i + 4] = put_cmd_in_ert("\\begin{landscape}")
608 add_to_preamble(document, ["\\usepackage{pdflscape}"])
611 def convert_fontenc(document):
612 " Convert default fontenc setting "
614 i = find_token(document.header, "\\fontencoding global", 0)
618 document.header[i] = document.header[i].replace("global", "auto")
621 def revert_fontenc(document):
622 " Revert default fontenc setting "
624 i = find_token(document.header, "\\fontencoding auto", 0)
628 document.header[i] = document.header[i].replace("auto", "global")
631 def revert_nospellcheck(document):
632 " Remove nospellcheck font info param "
636 i = find_token(document.body, '\\nospellcheck', i)
642 def revert_floatpclass(document):
643 " Remove float placement params 'document' and 'class' "
645 del_token(document.header, "\\float_placement class")
649 i = find_token(document.body, '\\begin_inset Float', i+1)
652 j = find_end_of_inset(document.body, i)
653 k = find_token(document.body, 'placement class', i, i + 2)
655 k = find_token(document.body, 'placement document', i, i + 2)
662 def revert_floatalignment(document):
663 " Remove float alignment params "
665 galignment = get_value(document.header, "\\float_alignment", delete=True)
669 i = find_token(document.body, '\\begin_inset Float', i+1)
672 j = find_end_of_inset(document.body, i)
674 document.warning("Malformed LyX document: Can't find end of inset at line " + str(i))
676 k = find_token(document.body, 'alignment', i, i+4)
680 alignment = get_value(document.body, "alignment", k)
681 if alignment == "document":
682 alignment = galignment
684 l = find_token(document.body, "\\begin_layout Plain Layout", i, j)
686 document.warning("Can't find float layout!")
689 if alignment == "left":
690 alcmd = put_cmd_in_ert("\\raggedright{}")
691 elif alignment == "center":
692 alcmd = put_cmd_in_ert("\\centering{}")
693 elif alignment == "right":
694 alcmd = put_cmd_in_ert("\\raggedleft{}")
696 document.body[l+1:l+1] = alcmd
699 def revert_tuftecite(document):
700 " Revert \cite commands in tufte classes "
702 tufte = ["tufte-book", "tufte-handout"]
703 if document.textclass not in tufte:
708 i = find_token(document.body, "\\begin_inset CommandInset citation", i+1)
711 j = find_end_of_inset(document.body, i)
713 document.warning("Can't find end of citation inset at line %d!!" %(i))
715 k = find_token(document.body, "LatexCommand", i, j)
717 document.warning("Can't find LatexCommand for citation inset at line %d!" %(i))
720 cmd = get_value(document.body, "LatexCommand", k)
724 pre = get_quoted_value(document.body, "before", i, j)
725 post = get_quoted_value(document.body, "after", i, j)
726 key = get_quoted_value(document.body, "key", i, j)
728 document.warning("Citation inset at line %d does not have a key!" %(i))
730 # Replace command with ERT
733 res += "[" + pre + "]"
735 res += "[" + post + "]"
738 res += "{" + key + "}"
739 document.body[i:j+1] = put_cmd_in_ert([res])
743 def revert_stretchcolumn(document):
744 " We remove the column varwidth flags or everything else will become a mess. "
747 i = find_token(document.body, "\\begin_inset Tabular", i+1)
750 j = find_end_of_inset(document.body, i+1)
752 document.warning("Malformed LyX document: Could not find end of tabular.")
754 for k in range(i, j):
755 if re.search('^<column.*varwidth="[^"]+".*>$', document.body[k]):
756 document.warning("Converting 'tabularx'/'xltabular' table to normal table.")
757 document.body[k] = document.body[k].replace(' varwidth="true"', '')
760 def revert_vcolumns(document):
761 " Revert standard columns with line breaks etc. "
767 i = find_token(document.body, "\\begin_inset Tabular", i+1)
770 j = find_end_of_inset(document.body, i)
772 document.warning("Malformed LyX document: Could not find end of tabular.")
775 # Collect necessary column information
777 nrows = int(document.body[i+1].split('"')[3])
778 ncols = int(document.body[i+1].split('"')[5])
780 for k in range(ncols):
781 m = find_token(document.body, "<column", m)
782 width = get_option_value(document.body[m], 'width')
783 varwidth = get_option_value(document.body[m], 'varwidth')
784 alignment = get_option_value(document.body[m], 'alignment')
785 special = get_option_value(document.body[m], 'special')
786 col_info.append([width, varwidth, alignment, special, m])
791 for row in range(nrows):
792 for col in range(ncols):
793 m = find_token(document.body, "<cell", m)
794 multicolumn = get_option_value(document.body[m], 'multicolumn')
795 multirow = get_option_value(document.body[m], 'multirow')
796 width = get_option_value(document.body[m], 'width')
797 rotate = get_option_value(document.body[m], 'rotate')
798 # Check for: linebreaks, multipars, non-standard environments
800 endcell = find_token(document.body, "</cell>", begcell)
802 if find_token(document.body, "\\begin_inset Newline", begcell, endcell) != -1:
804 elif count_pars_in_inset(document.body, begcell + 2) > 1:
806 elif get_value(document.body, "\\begin_layout", begcell) != "Plain Layout":
808 if vcand and rotate == "" and ((multicolumn == "" and multirow == "") or width == ""):
809 if col_info[col][0] == "" and col_info[col][1] == "" and col_info[col][3] == "":
811 alignment = col_info[col][2]
812 col_line = col_info[col][4]
814 if alignment == "center":
815 vval = ">{\\centering}"
816 elif alignment == "left":
817 vval = ">{\\raggedright}"
818 elif alignment == "right":
819 vval = ">{\\raggedleft}"
822 vval += "V{\\linewidth}"
824 document.body[col_line] = document.body[col_line][:-1] + " special=\"" + vval + "\">"
825 # ERT newlines and linebreaks (since LyX < 2.4 automatically inserts parboxes
826 # with newlines, and we do not want that)
828 endcell = find_token(document.body, "</cell>", begcell)
830 nl = find_token(document.body, "\\begin_inset Newline newline", begcell, endcell)
832 nl = find_token(document.body, "\\begin_inset Newline linebreak", begcell, endcell)
836 nle = find_end_of_inset(document.body, nl)
837 del(document.body[nle:nle+1])
839 document.body[nl:nl+1] = put_cmd_in_ert("\\linebreak{}")
841 document.body[nl:nl+1] = put_cmd_in_ert("\\\\")
847 if needarray == True:
848 add_to_preamble(document, ["\\usepackage{array}"])
849 if needvarwidth == True:
850 add_to_preamble(document, ["\\usepackage{varwidth}"])
853 def revert_bibencoding(document):
854 " Revert bibliography encoding "
858 i = find_token(document.header, "\\cite_engine", 0)
860 document.warning("Malformed document! Missing \\cite_engine")
862 engine = get_value(document.header, "\\cite_engine", i)
866 if engine in ["biblatex", "biblatex-natbib"]:
869 # Map lyx to latex encoding names
873 "armscii8" : "armscii8",
874 "iso8859-1" : "latin1",
875 "iso8859-2" : "latin2",
876 "iso8859-3" : "latin3",
877 "iso8859-4" : "latin4",
878 "iso8859-5" : "iso88595",
879 "iso8859-6" : "8859-6",
880 "iso8859-7" : "iso-8859-7",
881 "iso8859-8" : "8859-8",
882 "iso8859-9" : "latin5",
883 "iso8859-13" : "latin7",
884 "iso8859-15" : "latin9",
885 "iso8859-16" : "latin10",
886 "applemac" : "applemac",
888 "cp437de" : "cp437de",
905 "utf8-platex" : "utf8",
912 i = find_token(document.body, "\\begin_inset CommandInset bibtex", i+1)
915 j = find_end_of_inset(document.body, i)
917 document.warning("Can't find end of bibtex inset at line %d!!" %(i))
919 encoding = get_quoted_value(document.body, "encoding", i, j)
922 # remove encoding line
923 k = find_token(document.body, "encoding", i, j)
926 if encoding == "default":
928 # Re-find inset end line
929 j = find_end_of_inset(document.body, i)
932 h = find_token(document.header, "\\biblio_options", 0)
934 biblio_options = get_value(document.header, "\\biblio_options", h)
935 if not "bibencoding" in biblio_options:
936 document.header[h] += ",bibencoding=%s" % encodings[encoding]
938 bs = find_token(document.header, "\\biblatex_bibstyle", 0)
940 # this should not happen
941 document.warning("Malformed LyX document! No \\biblatex_bibstyle header found!")
943 document.header[bs-1 : bs-1] = ["\\biblio_options bibencoding=" + encodings[encoding]]
945 document.body[j+1:j+1] = put_cmd_in_ert("\\egroup")
946 document.body[i:i] = put_cmd_in_ert("\\bgroup\\inputencoding{" + encodings[encoding] + "}")
952 def convert_vcsinfo(document):
953 " Separate vcs Info inset from buffer Info inset. "
956 "vcs-revision" : "revision",
957 "vcs-tree-revision" : "tree-revision",
958 "vcs-author" : "author",
964 i = find_token(document.body, "\\begin_inset Info", i+1)
967 j = find_end_of_inset(document.body, i+1)
969 document.warning("Malformed LyX document: Could not find end of Info inset.")
971 tp = find_token(document.body, 'type', i, j)
972 tpv = get_quoted_value(document.body, "type", tp)
975 arg = find_token(document.body, 'arg', i, j)
976 argv = get_quoted_value(document.body, "arg", arg)
977 if argv not in list(types.keys()):
979 document.body[tp] = "type \"vcs\""
980 document.body[arg] = "arg \"" + types[argv] + "\""
983 def revert_vcsinfo(document):
984 " Merge vcs Info inset to buffer Info inset. "
986 args = ["revision", "tree-revision", "author", "time", "date" ]
989 i = find_token(document.body, "\\begin_inset Info", i+1)
992 j = find_end_of_inset(document.body, i+1)
994 document.warning("Malformed LyX document: Could not find end of Info inset.")
996 tp = find_token(document.body, 'type', i, j)
997 tpv = get_quoted_value(document.body, "type", tp)
1000 arg = find_token(document.body, 'arg', i, j)
1001 argv = get_quoted_value(document.body, "arg", arg)
1002 if argv not in args:
1003 document.warning("Malformed Info inset. Invalid vcs arg.")
1005 document.body[tp] = "type \"buffer\""
1006 document.body[arg] = "arg \"vcs-" + argv + "\""
1009 def revert_dateinfo(document):
1010 " Revert date info insets to static text. "
1012 # FIXME This currently only considers the main language and uses the system locale
1013 # Ideally, it should honor context languages and switch the locale accordingly.
1015 # The date formats for each language using strftime syntax:
1016 # long, short, loclong, locmedium, locshort
1018 "afrikaans" : ["%A, %d %B %Y", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y/%m/%d"],
1019 "albanian" : ["%A, %d %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1020 "american" : ["%A, %B %d, %Y", "%m/%d/%y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1021 "amharic" : ["%A ፣%d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1022 "ancientgreek" : ["%A, %d %B %Y", "%d %b %Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1023 "arabic_arabi" : ["%A، %d %B، %Y", "%d/%m/%Y", "%d %B، %Y", "%d/%m/%Y", "%d/%m/%Y"],
1024 "arabic_arabtex" : ["%A، %d %B، %Y", "%d/%m/%Y", "%d %B، %Y", "%d/%m/%Y", "%d/%m/%Y"],
1025 "armenian" : ["%Y թ. %B %d, %A", "%d.%m.%y", "%d %B، %Y", "%d %b، %Y", "%d/%m/%Y"],
1026 "asturian" : ["%A, %d %B de %Y", "%d/%m/%y", "%d de %B de %Y", "%d %b %Y", "%d/%m/%Y"],
1027 "australian" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1028 "austrian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1029 "bahasa" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1030 "bahasam" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1031 "basque" : ["%Y(e)ko %B %d, %A", "%y/%m/%d", "%Y %B %d", "%Y %b %d", "%Y/%m/%d"],
1032 "belarusian" : ["%A, %d %B %Y г.", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1033 "bosnian" : ["%A, %d. %B %Y.", "%d.%m.%y.", "%d. %B %Y", "%d. %b %Y", "%Y-%m-%d"],
1034 "brazilian" : ["%A, %d de %B de %Y", "%d/%m/%Y", "%d de %B de %Y", "%d de %b de %Y", "%d/%m/%Y"],
1035 "breton" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
1036 "british" : ["%A, %d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1037 "bulgarian" : ["%A, %d %B %Y г.", "%d.%m.%y г.", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
1038 "canadian" : ["%A, %B %d, %Y", "%Y-%m-%d", "%B %d, %Y", "%d %b %Y", "%Y-%m-%d"],
1039 "canadien" : ["%A %d %B %Y", "%y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
1040 "catalan" : ["%A, %d %B de %Y", "%d/%m/%y", "%d / %B / %Y", "%d / %b / %Y", "%d/%m/%Y"],
1041 "chinese-simplified" : ["%Y年%m月%d日%A", "%Y/%m/%d", "%Y年%m月%d日", "%Y-%m-%d", "%y-%m-%d"],
1042 "chinese-traditional" : ["%Y年%m月%d日 %A", "%Y/%m/%d", "%Y年%m月%d日", "%Y年%m月%d日", "%y年%m月%d日"],
1043 "coptic" : ["%A, %d %B %Y", "%d %b %Y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1044 "croatian" : ["%A, %d. %B %Y.", "%d. %m. %Y.", "%d. %B %Y.", "%d. %b. %Y.", "%d.%m.%Y."],
1045 "czech" : ["%A %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b. %Y", "%d.%m.%Y"],
1046 "danish" : ["%A den %d. %B %Y", "%d/%m/%Y", "%d. %B %Y", "%d. %b %Y", "%d/%m/%Y"],
1047 "divehi" : ["%Y %B %d, %A", "%Y-%m-%d", "%Y %B %d", "%Y %b %d", "%d/%m/%Y"],
1048 "dutch" : ["%A %d %B %Y", "%d-%m-%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1049 "english" : ["%A, %B %d, %Y", "%m/%d/%y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1050 "esperanto" : ["%A, %d %B %Y", "%d %b %Y", "la %d de %B %Y", "la %d de %b %Y", "%m/%d/%Y"],
1051 "estonian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1052 "farsi" : ["%A %d %B %Y", "%Y/%m/%d", "%d %B %Y", "%d %b %Y", "%Y/%m/%d"],
1053 "finnish" : ["%A %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1054 "french" : ["%A %d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1055 "friulan" : ["%A %d di %B dal %Y", "%d/%m/%y", "%d di %B dal %Y", "%d di %b dal %Y", "%d/%m/%Y"],
1056 "galician" : ["%A, %d de %B de %Y", "%d/%m/%y", "%d de %B de %Y", "%d de %b de %Y", "%d/%m/%Y"],
1057 "georgian" : ["%A, %d %B, %Y", "%d.%m.%y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1058 "german" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1059 "german-ch" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1060 "german-ch-old" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1061 "greek" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1062 "hebrew" : ["%A, %d ב%B %Y", "%d.%m.%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1063 "hindi" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1064 "icelandic" : ["%A, %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1065 "interlingua" : ["%Y %B %d, %A", "%Y-%m-%d", "le %d de %B %Y", "le %d de %b %Y", "%Y-%m-%d"],
1066 "irish" : ["%A %d %B %Y", "%d/%m/%Y", "%d. %B %Y", "%d. %b %Y", "%d/%m/%Y"],
1067 "italian" : ["%A %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d/%b/%Y", "%d/%m/%Y"],
1068 "japanese" : ["%Y年%m月%d日%A", "%Y/%m/%d", "%Y年%m月%d日", "%Y/%m/%d", "%y/%m/%d"],
1069 "japanese-cjk" : ["%Y年%m月%d日%A", "%Y/%m/%d", "%Y年%m月%d日", "%Y/%m/%d", "%y/%m/%d"],
1070 "kannada" : ["%A, %B %d, %Y", "%d/%m/%y", "%d %B %Y", "%d %B %Y", "%d-%m-%Y"],
1071 "kazakh" : ["%Y ж. %d %B, %A", "%d.%m.%y", "%d %B %Y", "%d %B %Y", "%Y-%d-%m"],
1072 "khmer" : ["%A %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %B %Y", "%d/%m/%Y"],
1073 "korean" : ["%Y년 %m월 %d일 %A", "%y. %m. %d.", "%Y년 %m월 %d일", "%Y. %m. %d.", "%y. %m. %d."],
1074 "kurmanji" : ["%A, %d %B %Y", "%d %b %Y", "%d. %B %Y", "%d. %m. %Y", "%Y-%m-%d"],
1075 "lao" : ["%A ທີ %d %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %B %Y", "%d/%m/%Y"],
1076 "latin" : ["%A, %d %B %Y", "%d %b %Y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1077 "latvian" : ["%A, %Y. gada %d. %B", "%d.%m.%y", "%Y. gada %d. %B", "%Y. gada %d. %b", "%d.%m.%Y"],
1078 "lithuanian" : ["%Y m. %B %d d., %A", "%Y-%m-%d", "%Y m. %B %d d.", "%Y m. %B %d d.", "%Y-%m-%d"],
1079 "lowersorbian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1080 "macedonian" : ["%A, %d %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1081 "magyar" : ["%Y. %B %d., %A", "%Y. %m. %d.", "%Y. %B %d.", "%Y. %b %d.", "%Y.%m.%d."],
1082 "malayalam" : ["%A, %d %B, %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1083 "marathi" : ["%A, %d %B, %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1084 "mongolian" : ["%A, %Y оны %m сарын %d", "%Y-%m-%d", "%Y оны %m сарын %d", "%d-%m-%Y", "%d-%m-%Y"],
1085 "naustrian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1086 "newzealand" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1087 "ngerman" : ["%A, %d. %B %Y", "%d.%m.%y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1088 "norsk" : ["%A %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1089 "nynorsk" : ["%A %d. %B %Y", "%d.%m.%Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1090 "occitan" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1091 "piedmontese" : ["%A, %d %B %Y", "%d %b %Y", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1092 "polish" : ["%A, %d %B %Y", "%d.%m.%Y", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
1093 "polutonikogreek" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1094 "portuguese" : ["%A, %d de %B de %Y", "%d/%m/%y", "%d de %B de %Y", "%d de %b de %Y", "%Y/%m/%d"],
1095 "romanian" : ["%A, %d %B %Y", "%d.%m.%Y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1096 "romansh" : ["%A, ils %d da %B %Y", "%d-%m-%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1097 "russian" : ["%A, %d %B %Y г.", "%d.%m.%Y", "%d %B %Y г.", "%d %b %Y г.", "%d.%m.%Y"],
1098 "samin" : ["%Y %B %d, %A", "%Y-%m-%d", "%B %d. b. %Y", "%b %d. b. %Y", "%d.%m.%Y"],
1099 "sanskrit" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1100 "scottish" : ["%A, %dmh %B %Y", "%d/%m/%Y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1101 "serbian" : ["%A, %d. %B %Y.", "%d.%m.%y.", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1102 "serbian-latin" : ["%A, %d. %B %Y.", "%d.%m.%y.", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1103 "slovak" : ["%A, %d. %B %Y", "%d. %m. %Y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1104 "slovene" : ["%A, %d. %B %Y", "%d. %m. %y", "%d. %B %Y", "%d. %b %Y", "%d.%m.%Y"],
1105 "spanish" : ["%A, %d de %B de %Y", "%d/%m/%y", "%d de %B %de %Y", "%d %b %Y", "%d/%m/%Y"],
1106 "spanish-mexico" : ["%A, %d de %B %de %Y", "%d/%m/%y", "%d de %B de %Y", "%d %b %Y", "%d/%m/%Y"],
1107 "swedish" : ["%A %d %B %Y", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%Y-%m-%d"],
1108 "syriac" : ["%Y %B %d, %A", "%Y-%m-%d", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1109 "tamil" : ["%A, %d %B, %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1110 "telugu" : ["%d, %B %Y, %A", "%d-%m-%y", "%d %B %Y", "%d %b %Y", "%d-%m-%Y"],
1111 "thai" : ["%Aที่ %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1112 "tibetan" : ["%Y %Bའི་ཚེས་%d, %A", "%Y-%m-%d", "%B %d, %Y", "%b %d, %Y", "%m/%d/%Y"],
1113 "turkish" : ["%d %B %Y %A", "%d.%m.%Y", "%d %B %Y", "%d.%b.%Y", "%d.%m.%Y"],
1114 "turkmen" : ["%d %B %Y %A", "%d.%m.%Y", "%Y ý. %B %d", "%d.%m.%Y ý.", "%d.%m.%y ý."],
1115 "ukrainian" : ["%A, %d %B %Y р.", "%d.%m.%y", "%d %B %Y", "%d %m %Y", "%d.%m.%Y"],
1116 "uppersorbian" : ["%A, %d. %B %Y", "%d.%m.%y", "%d %B %Y", "%d %b %Y", "%d.%m.%Y"],
1117 "urdu" : ["%A، %d %B، %Y", "%d/%m/%y", "%d %B, %Y", "%d %b %Y", "%d/%m/%Y"],
1118 "vietnamese" : ["%A, %d %B, %Y", "%d/%m/%Y", "%d tháng %B %Y", "%d-%m-%Y", "%d/%m/%Y"],
1119 "welsh" : ["%A, %d %B %Y", "%d/%m/%y", "%d %B %Y", "%d %b %Y", "%d/%m/%Y"],
1122 types = ["date", "fixdate", "moddate" ]
1123 lang = get_value(document.header, "\\language")
1125 document.warning("Malformed LyX document! No \\language header found!")
1130 i = find_token(document.body, "\\begin_inset Info", i+1)
1133 j = find_end_of_inset(document.body, i+1)
1135 document.warning("Malformed LyX document: Could not find end of Info inset.")
1137 tp = find_token(document.body, 'type', i, j)
1138 tpv = get_quoted_value(document.body, "type", tp)
1139 if tpv not in types:
1141 arg = find_token(document.body, 'arg', i, j)
1142 argv = get_quoted_value(document.body, "arg", arg)
1145 if tpv == "fixdate":
1146 datecomps = argv.split('@')
1147 if len(datecomps) > 1:
1149 isodate = datecomps[1]
1150 m = re.search('(\d\d\d\d)-(\d\d)-(\d\d)', isodate)
1152 dte = date(int(m.group(1)), int(m.group(2)), int(m.group(3)))
1153 # FIXME if we had the path to the original document (not the one in the tmp dir),
1154 # we could use the mtime.
1155 # elif tpv == "moddate":
1156 # dte = date.fromtimestamp(os.path.getmtime(document.dir))
1159 result = dte.isodate()
1160 elif argv == "long":
1161 result = dte.strftime(dateformats[lang][0])
1162 elif argv == "short":
1163 result = dte.strftime(dateformats[lang][1])
1164 elif argv == "loclong":
1165 result = dte.strftime(dateformats[lang][2])
1166 elif argv == "locmedium":
1167 result = dte.strftime(dateformats[lang][3])
1168 elif argv == "locshort":
1169 result = dte.strftime(dateformats[lang][4])
1171 fmt = argv.replace("MMMM", "%b").replace("MMM", "%b").replace("MM", "%m").replace("M", "%m")
1172 fmt = fmt.replace("yyyy", "%Y").replace("yy", "%y")
1173 fmt = fmt.replace("dddd", "%A").replace("ddd", "%a").replace("dd", "%d")
1174 fmt = re.sub('[^\'%]d', '%d', fmt)
1175 fmt = fmt.replace("'", "")
1176 result = dte.strftime(fmt)
1177 if sys.version_info < (3,0):
1178 # In Python 2, datetime module works with binary strings,
1179 # our dateformat strings are utf8-encoded:
1180 result = result.decode('utf-8')
1181 document.body[i : j+1] = [result]
1184 def revert_timeinfo(document):
1185 " Revert time info insets to static text. "
1187 # FIXME This currently only considers the main language and uses the system locale
1188 # Ideally, it should honor context languages and switch the locale accordingly.
1189 # Also, the time object is "naive", i.e., it does not know of timezones (%Z will
1192 # The time formats for each language using strftime syntax:
1195 "afrikaans" : ["%H:%M:%S %Z", "%H:%M"],
1196 "albanian" : ["%I:%M:%S %p, %Z", "%I:%M %p"],
1197 "american" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1198 "amharic" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1199 "ancientgreek" : ["%H:%M:%S %Z", "%H:%M:%S"],
1200 "arabic_arabi" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1201 "arabic_arabtex" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1202 "armenian" : ["%H:%M:%S %Z", "%H:%M"],
1203 "asturian" : ["%H:%M:%S %Z", "%H:%M"],
1204 "australian" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1205 "austrian" : ["%H:%M:%S %Z", "%H:%M"],
1206 "bahasa" : ["%H.%M.%S %Z", "%H.%M"],
1207 "bahasam" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1208 "basque" : ["%H:%M:%S (%Z)", "%H:%M"],
1209 "belarusian" : ["%H:%M:%S, %Z", "%H:%M"],
1210 "bosnian" : ["%H:%M:%S %Z", "%H:%M"],
1211 "brazilian" : ["%H:%M:%S %Z", "%H:%M"],
1212 "breton" : ["%H:%M:%S %Z", "%H:%M"],
1213 "british" : ["%H:%M:%S %Z", "%H:%M"],
1214 "bulgarian" : ["%H:%M:%S %Z", "%H:%M"],
1215 "canadian" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1216 "canadien" : ["%H:%M:%S %Z", "%H h %M"],
1217 "catalan" : ["%H:%M:%S %Z", "%H:%M"],
1218 "chinese-simplified" : ["%Z %p%I:%M:%S", "%p%I:%M"],
1219 "chinese-traditional" : ["%p%I:%M:%S [%Z]", "%p%I:%M"],
1220 "coptic" : ["%H:%M:%S %Z", "%H:%M:%S"],
1221 "croatian" : ["%H:%M:%S (%Z)", "%H:%M"],
1222 "czech" : ["%H:%M:%S %Z", "%H:%M"],
1223 "danish" : ["%H.%M.%S %Z", "%H.%M"],
1224 "divehi" : ["%H:%M:%S %Z", "%H:%M"],
1225 "dutch" : ["%H:%M:%S %Z", "%H:%M"],
1226 "english" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1227 "esperanto" : ["%H:%M:%S %Z", "%H:%M:%S"],
1228 "estonian" : ["%H:%M:%S %Z", "%H:%M"],
1229 "farsi" : ["%H:%M:%S (%Z)", "%H:%M"],
1230 "finnish" : ["%H.%M.%S %Z", "%H.%M"],
1231 "french" : ["%H:%M:%S %Z", "%H:%M"],
1232 "friulan" : ["%H:%M:%S %Z", "%H:%M"],
1233 "galician" : ["%H:%M:%S %Z", "%H:%M"],
1234 "georgian" : ["%H:%M:%S %Z", "%H:%M"],
1235 "german" : ["%H:%M:%S %Z", "%H:%M"],
1236 "german-ch" : ["%H:%M:%S %Z", "%H:%M"],
1237 "german-ch-old" : ["%H:%M:%S %Z", "%H:%M"],
1238 "greek" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1239 "hebrew" : ["%H:%M:%S %Z", "%H:%M"],
1240 "hindi" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1241 "icelandic" : ["%H:%M:%S %Z", "%H:%M"],
1242 "interlingua" : ["%H:%M:%S %Z", "%H:%M"],
1243 "irish" : ["%H:%M:%S %Z", "%H:%M"],
1244 "italian" : ["%H:%M:%S %Z", "%H:%M"],
1245 "japanese" : ["%H時%M分%S秒 %Z", "%H:%M"],
1246 "japanese-cjk" : ["%H時%M分%S秒 %Z", "%H:%M"],
1247 "kannada" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1248 "kazakh" : ["%H:%M:%S %Z", "%H:%M"],
1249 "khmer" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1250 "korean" : ["%p %I시%M분 %S초 %Z", "%p %I:%M"],
1251 "kurmanji" : ["%H:%M:%S %Z", "%H:%M:%S"],
1252 "lao" : ["%H ໂມງ%M ນາທີ %S ວິນາທີ %Z", "%H:%M"],
1253 "latin" : ["%H:%M:%S %Z", "%H:%M:%S"],
1254 "latvian" : ["%H:%M:%S %Z", "%H:%M"],
1255 "lithuanian" : ["%H:%M:%S %Z", "%H:%M"],
1256 "lowersorbian" : ["%H:%M:%S %Z", "%H:%M"],
1257 "macedonian" : ["%H:%M:%S %Z", "%H:%M"],
1258 "magyar" : ["%H:%M:%S %Z", "%H:%M"],
1259 "malayalam" : ["%p %I:%M:%S %Z", "%p %I:%M"],
1260 "marathi" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1261 "mongolian" : ["%H:%M:%S %Z", "%H:%M"],
1262 "naustrian" : ["%H:%M:%S %Z", "%H:%M"],
1263 "newzealand" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1264 "ngerman" : ["%H:%M:%S %Z", "%H:%M"],
1265 "norsk" : ["%H:%M:%S %Z", "%H:%M"],
1266 "nynorsk" : ["kl. %H:%M:%S %Z", "%H:%M"],
1267 "occitan" : ["%H:%M:%S %Z", "%H:%M"],
1268 "piedmontese" : ["%H:%M:%S %Z", "%H:%M:%S"],
1269 "polish" : ["%H:%M:%S %Z", "%H:%M"],
1270 "polutonikogreek" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1271 "portuguese" : ["%H:%M:%S %Z", "%H:%M"],
1272 "romanian" : ["%H:%M:%S %Z", "%H:%M"],
1273 "romansh" : ["%H:%M:%S %Z", "%H:%M"],
1274 "russian" : ["%H:%M:%S %Z", "%H:%M"],
1275 "samin" : ["%H:%M:%S %Z", "%H:%M"],
1276 "sanskrit" : ["%H:%M:%S %Z", "%H:%M"],
1277 "scottish" : ["%H:%M:%S %Z", "%H:%M"],
1278 "serbian" : ["%H:%M:%S %Z", "%H:%M"],
1279 "serbian-latin" : ["%H:%M:%S %Z", "%H:%M"],
1280 "slovak" : ["%H:%M:%S %Z", "%H:%M"],
1281 "slovene" : ["%H:%M:%S %Z", "%H:%M"],
1282 "spanish" : ["%H:%M:%S (%Z)", "%H:%M"],
1283 "spanish-mexico" : ["%H:%M:%S %Z", "%H:%M"],
1284 "swedish" : ["kl. %H:%M:%S %Z", "%H:%M"],
1285 "syriac" : ["%H:%M:%S %Z", "%H:%M"],
1286 "tamil" : ["%p %I:%M:%S %Z", "%p %I:%M"],
1287 "telugu" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1288 "thai" : ["%H นาฬิกา %M นาที %S วินาที %Z", "%H:%M"],
1289 "tibetan" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1290 "turkish" : ["%H:%M:%S %Z", "%H:%M"],
1291 "turkmen" : ["%H:%M:%S %Z", "%H:%M"],
1292 "ukrainian" : ["%H:%M:%S %Z", "%H:%M"],
1293 "uppersorbian" : ["%H:%M:%S %Z", "%H:%M hodź."],
1294 "urdu" : ["%I:%M:%S %p %Z", "%I:%M %p"],
1295 "vietnamese" : ["%H:%M:%S %Z", "%H:%M"],
1296 "welsh" : ["%H:%M:%S %Z", "%H:%M"]
1299 types = ["time", "fixtime", "modtime" ]
1301 i = find_token(document.header, "\\language", 0)
1303 # this should not happen
1304 document.warning("Malformed LyX document! No \\language header found!")
1306 lang = get_value(document.header, "\\language", i)
1310 i = find_token(document.body, "\\begin_inset Info", i+1)
1313 j = find_end_of_inset(document.body, i+1)
1315 document.warning("Malformed LyX document: Could not find end of Info inset.")
1317 tp = find_token(document.body, 'type', i, j)
1318 tpv = get_quoted_value(document.body, "type", tp)
1319 if tpv not in types:
1321 arg = find_token(document.body, 'arg', i, j)
1322 argv = get_quoted_value(document.body, "arg", arg)
1324 dtme = datetime.now()
1326 if tpv == "fixtime":
1327 timecomps = argv.split('@')
1328 if len(timecomps) > 1:
1330 isotime = timecomps[1]
1331 m = re.search('(\d\d):(\d\d):(\d\d)', isotime)
1333 tme = time(int(m.group(1)), int(m.group(2)), int(m.group(3)))
1335 m = re.search('(\d\d):(\d\d)', isotime)
1337 tme = time(int(m.group(1)), int(m.group(2)))
1338 # FIXME if we had the path to the original document (not the one in the tmp dir),
1339 # we could use the mtime.
1340 # elif tpv == "moddate":
1341 # dte = date.fromtimestamp(os.path.getmtime(document.dir))
1344 result = tme.isoformat()
1345 elif argv == "long":
1346 result = tme.strftime(timeformats[lang][0])
1347 elif argv == "short":
1348 result = tme.strftime(timeformats[lang][1])
1350 fmt = argv.replace("HH", "%H").replace("H", "%H").replace("hh", "%I").replace("h", "%I")
1351 fmt = fmt.replace("mm", "%M").replace("m", "%M").replace("ss", "%S").replace("s", "%S")
1352 fmt = fmt.replace("zzz", "%f").replace("z", "%f").replace("t", "%Z")
1353 fmt = fmt.replace("AP", "%p").replace("ap", "%p").replace("A", "%p").replace("a", "%p")
1354 fmt = fmt.replace("'", "")
1355 result = dte.strftime(fmt)
1356 document.body[i : j+1] = result
1359 def revert_namenoextinfo(document):
1360 " Merge buffer Info inset type name-noext to name. "
1364 i = find_token(document.body, "\\begin_inset Info", i+1)
1367 j = find_end_of_inset(document.body, i+1)
1369 document.warning("Malformed LyX document: Could not find end of Info inset.")
1371 tp = find_token(document.body, 'type', i, j)
1372 tpv = get_quoted_value(document.body, "type", tp)
1375 arg = find_token(document.body, 'arg', i, j)
1376 argv = get_quoted_value(document.body, "arg", arg)
1377 if argv != "name-noext":
1379 document.body[arg] = "arg \"name\""
1382 def revert_l7ninfo(document):
1383 " Revert l7n Info inset to text. "
1387 i = find_token(document.body, "\\begin_inset Info", i+1)
1390 j = find_end_of_inset(document.body, i+1)
1392 document.warning("Malformed LyX document: Could not find end of Info inset.")
1394 tp = find_token(document.body, 'type', i, j)
1395 tpv = get_quoted_value(document.body, "type", tp)
1398 arg = find_token(document.body, 'arg', i, j)
1399 argv = get_quoted_value(document.body, "arg", arg)
1400 # remove trailing colons, menu accelerator (|...) and qt accelerator (&), while keeping literal " & "
1401 argv = argv.rstrip(':').split('|')[0].replace(" & ", "</amp;>").replace("&", "").replace("</amp;>", " & ")
1402 document.body[i : j+1] = argv
1405 def revert_listpargs(document):
1406 " Reverts listpreamble arguments to TeX-code "
1409 i = find_token(document.body, "\\begin_inset Argument listpreamble:", i+1)
1412 j = find_end_of_inset(document.body, i)
1413 # Find containing paragraph layout
1414 parent = get_containing_layout(document.body, i)
1416 document.warning("Malformed LyX document: Can't find parent paragraph layout")
1419 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1420 endPlain = find_end_of_layout(document.body, beginPlain)
1421 content = document.body[beginPlain + 1 : endPlain]
1422 del document.body[i:j+1]
1423 subst = ["\\begin_inset ERT", "status collapsed", "", "\\begin_layout Plain Layout",
1424 "{"] + content + ["}", "\\end_layout", "", "\\end_inset", ""]
1425 document.body[parbeg : parbeg] = subst
1428 def revert_lformatinfo(document):
1429 " Revert layout format Info inset to text. "
1433 i = find_token(document.body, "\\begin_inset Info", i+1)
1436 j = find_end_of_inset(document.body, i+1)
1438 document.warning("Malformed LyX document: Could not find end of Info inset.")
1440 tp = find_token(document.body, 'type', i, j)
1441 tpv = get_quoted_value(document.body, "type", tp)
1442 if tpv != "lyxinfo":
1444 arg = find_token(document.body, 'arg', i, j)
1445 argv = get_quoted_value(document.body, "arg", arg)
1446 if argv != "layoutformat":
1449 document.body[i : j+1] = "69"
1452 def convert_hebrew_parentheses(document):
1453 """ Swap opening/closing parentheses in Hebrew text.
1455 Up to LyX 2.4, "(" was used as closing parenthesis and
1456 ")" as opening parenthesis for Hebrew in the LyX source.
1458 # print("convert hebrew parentheses")
1459 current_languages = [document.language]
1460 for i, line in enumerate(document.body):
1461 if line.startswith('\\lang '):
1462 current_languages[-1] = line.lstrip('\\lang ')
1463 elif line.startswith('\\begin_layout'):
1464 current_languages.append(current_languages[-1])
1465 # print (line, current_languages[-1])
1466 elif line.startswith('\\end_layout'):
1467 current_languages.pop()
1468 elif current_languages[-1] == 'hebrew' and not line.startswith('\\'):
1469 document.body[i] = line.replace('(','\x00').replace(')','(').replace('\x00',')')
1472 def revert_hebrew_parentheses(document):
1473 " Store parentheses in Hebrew text reversed"
1474 # This only exists to keep the convert/revert naming convention
1475 convert_hebrew_parentheses(document)
1478 def revert_malayalam(document):
1479 " Set the document language to English but assure Malayalam output "
1481 revert_language(document, "malayalam", "", "malayalam")
1484 def revert_soul(document):
1485 " Revert soul module flex insets to ERT "
1487 flexes = ["Spaceletters", "Strikethrough", "Underline", "Highlight", "Capitalize"]
1490 i = find_token(document.body, "\\begin_inset Flex %s" % flex, 0)
1492 add_to_preamble(document, ["\\usepackage{soul}"])
1494 i = find_token(document.body, "\\begin_inset Flex Highlight", 0)
1496 add_to_preamble(document, ["\\usepackage{color}"])
1498 revert_flex_inset(document.body, "Spaceletters", "\\so")
1499 revert_flex_inset(document.body, "Strikethrough", "\\st")
1500 revert_flex_inset(document.body, "Underline", "\\ul")
1501 revert_flex_inset(document.body, "Highlight", "\\hl")
1502 revert_flex_inset(document.body, "Capitalize", "\\caps")
1505 def revert_tablestyle(document):
1506 " Remove tablestyle params "
1509 i = find_token(document.header, "\\tablestyle")
1511 del document.header[i]
1514 def revert_bibfileencodings(document):
1515 " Revert individual Biblatex bibliography encodings "
1519 i = find_token(document.header, "\\cite_engine", 0)
1521 document.warning("Malformed document! Missing \\cite_engine")
1523 engine = get_value(document.header, "\\cite_engine", i)
1527 if engine in ["biblatex", "biblatex-natbib"]:
1530 # Map lyx to latex encoding names
1534 "armscii8" : "armscii8",
1535 "iso8859-1" : "latin1",
1536 "iso8859-2" : "latin2",
1537 "iso8859-3" : "latin3",
1538 "iso8859-4" : "latin4",
1539 "iso8859-5" : "iso88595",
1540 "iso8859-6" : "8859-6",
1541 "iso8859-7" : "iso-8859-7",
1542 "iso8859-8" : "8859-8",
1543 "iso8859-9" : "latin5",
1544 "iso8859-13" : "latin7",
1545 "iso8859-15" : "latin9",
1546 "iso8859-16" : "latin10",
1547 "applemac" : "applemac",
1549 "cp437de" : "cp437de",
1557 "cp1250" : "cp1250",
1558 "cp1251" : "cp1251",
1559 "cp1252" : "cp1252",
1560 "cp1255" : "cp1255",
1561 "cp1256" : "cp1256",
1562 "cp1257" : "cp1257",
1563 "koi8-r" : "koi8-r",
1564 "koi8-u" : "koi8-u",
1566 "utf8-platex" : "utf8",
1573 i = find_token(document.body, "\\begin_inset CommandInset bibtex", i+1)
1576 j = find_end_of_inset(document.body, i)
1578 document.warning("Can't find end of bibtex inset at line %d!!" %(i))
1580 encodings = get_quoted_value(document.body, "file_encodings", i, j)
1584 bibfiles = get_quoted_value(document.body, "bibfiles", i, j).split(",")
1585 opts = get_quoted_value(document.body, "biblatexopts", i, j)
1586 if len(bibfiles) == 0:
1587 document.warning("Bibtex inset at line %d does not have a bibfile!" %(i))
1588 # remove encoding line
1589 k = find_token(document.body, "file_encodings", i, j)
1591 del document.body[k]
1592 # Re-find inset end line
1593 j = find_end_of_inset(document.body, i)
1595 enclist = encodings.split("\t")
1598 ppp = pp.split(" ", 1)
1599 encmap[ppp[0]] = ppp[1]
1600 for bib in bibfiles:
1601 pr = "\\addbibresource"
1602 if bib in encmap.keys():
1603 pr += "[bibencoding=" + encmap[bib] + "]"
1604 pr += "{" + bib + "}"
1605 add_to_preamble(document, [pr])
1606 # Insert ERT \\printbibliography and wrap bibtex inset to a Note
1607 pcmd = "printbibliography"
1609 pcmd += "[" + opts + "]"
1610 repl = ["\\begin_inset ERT", "status open", "", "\\begin_layout Plain Layout",\
1611 "", "", "\\backslash", pcmd, "\\end_layout", "", "\\end_inset", "", "",\
1612 "\\end_layout", "", "\\begin_layout Standard", "\\begin_inset Note Note",\
1613 "status open", "", "\\begin_layout Plain Layout" ]
1614 repl += document.body[i:j+1]
1615 repl += ["", "\\end_layout", "", "\\end_inset", "", ""]
1616 document.body[i:j+1] = repl
1622 def revert_cmidruletrimming(document):
1623 " Remove \\cmidrule trimming "
1625 # FIXME: Revert to TeX code?
1628 # first, let's find out if we need to do anything
1629 i = find_token(document.body, '<cell ', i+1)
1632 j = document.body[i].find('trim="')
1635 rgx = re.compile(r' (bottom|top)line[lr]trim="true"')
1636 # remove trim option
1637 document.body[i] = rgx.sub('', document.body[i])
1641 r'### Inserted by lyx2lyx (ruby inset) ###',
1642 r'InsetLayout Flex:Ruby',
1643 r' LyxType charstyle',
1644 r' LatexType command',
1648 r' HTMLInnerTag rb',
1649 r' HTMLInnerAttr ""',
1651 r' LabelString "Ruby"',
1652 r' Decoration Conglomerate',
1654 r' \ifdefined\kanjiskip',
1655 r' \IfFileExists{okumacro.sty}{\usepackage{okumacro}}{}',
1656 r' \else \ifdefined\luatexversion',
1657 r' \usepackage{luatexja-ruby}',
1658 r' \else \ifdefined\XeTeXversion',
1659 r' \usepackage{ruby}%',
1661 r' \providecommand{\ruby}[2]{\shortstack{\tiny #2\\#1}}',
1663 r' Argument post:1',
1664 r' LabelString "ruby text"',
1665 r' MenuString "Ruby Text|R"',
1666 r' Tooltip "Reading aid (ruby, furigana) for Chinese characters."',
1667 r' Decoration Conglomerate',
1679 def convert_ruby_module(document):
1680 " Use ruby module instead of local module definition "
1681 if document.del_local_layout(ruby_inset_def):
1682 document.add_module("ruby")
1684 def revert_ruby_module(document):
1685 " Replace ruby module with local module definition "
1686 if document.del_module("ruby"):
1687 document.append_local_layout(ruby_inset_def)
1690 def convert_utf8_japanese(document):
1691 " Use generic utf8 with Japanese documents."
1692 lang = get_value(document.header, "\\language")
1693 if not lang.startswith("japanese"):
1695 inputenc = get_value(document.header, "\\inputencoding")
1696 if ((lang == "japanese" and inputenc == "utf8-platex")
1697 or (lang == "japanese-cjk" and inputenc == "utf8-cjk")):
1698 document.set_parameter("inputencoding", "utf8")
1700 def revert_utf8_japanese(document):
1701 " Use Japanese utf8 variants with Japanese documents."
1702 inputenc = get_value(document.header, "\\inputencoding")
1703 if inputenc != "utf8":
1705 lang = get_value(document.header, "\\language")
1706 if lang == "japanese":
1707 document.set_parameter("inputencoding", "utf8-platex")
1708 if lang == "japanese-cjk":
1709 document.set_parameter("inputencoding", "utf8-cjk")
1712 def revert_lineno(document):
1713 " Replace lineno setting with user-preamble code."
1715 options = get_quoted_value(document.header, "\\lineno_options",
1717 if not get_bool_value(document.header, "\\use_lineno", delete=True):
1720 options = "[" + options + "]"
1721 add_to_preamble(document, ["\\usepackage%s{lineno}" % options,
1724 def convert_lineno(document):
1725 " Replace user-preamble code with native lineno support."
1728 i = find_token(document.preamble, "\\linenumbers", 1)
1730 usepkg = re.match(r"\\usepackage(.*){lineno}", document.preamble[i-1])
1733 options = usepkg.group(1).strip("[]")
1734 del(document.preamble[i-1:i+1])
1735 del_token(document.preamble, "% Added by lyx2lyx", i-2, i-1)
1737 k = find_token(document.header, "\\index ")
1739 document.header[k:k] = ["\\use_lineno %d" % use_lineno]
1741 document.header[k:k] = ["\\use_lineno %d" % use_lineno,
1742 "\\lineno_options %s" % options]
1745 def revert_new_languages(document):
1746 """Emulate support for Azerbaijani, Bengali, Church Slavonic, Korean,
1747 and Russian (Petrine orthography)."""
1749 # lyxname: (babelname, polyglossianame)
1750 new_languages = {"azerbaijani": ("azerbaijani", ""),
1751 "bengali": ("", "bengali"),
1752 "churchslavonic": ("", "churchslavonic"),
1753 "oldrussian": ("", "russian"),
1754 "korean": ("", "korean"),
1756 used_languages = set()
1757 if document.language in new_languages:
1758 used_languages.add(document.language)
1761 i = find_token(document.body, "\\lang", i+1)
1764 if document.body[i][6:].strip() in new_languages:
1765 used_languages.add(document.language)
1767 # Korean is already supported via CJK, so leave as-is for Babel
1768 if ("korean" in used_languages
1769 and get_bool_value(document.header, "\\use_non_tex_fonts")
1770 and get_value(document.header, "\\language_package") in ("default", "auto")):
1771 revert_language(document, "korean", "", "korean")
1772 used_languages.discard("korean")
1774 for lang in used_languages:
1775 revert(lang, *new_languages[lang])
1779 r'### Inserted by lyx2lyx (deprecated ling glosses) ###',
1780 r'InsetLayout Flex:Glosse',
1782 r' LabelString "Gloss (old version)"',
1783 r' MenuString "Gloss (old version)"',
1784 r' LatexType environment',
1785 r' LatexName linggloss',
1786 r' Decoration minimalistic',
1791 r' CustomPars false',
1792 r' ForcePlain true',
1793 r' ParbreakIsNewline true',
1794 r' FreeSpacing true',
1795 r' Requires covington',
1798 r' \@ifundefined{linggloss}{%',
1799 r' \newenvironment{linggloss}[2][]{',
1800 r' \def\glosstr{\glt #1}%',
1802 r' {\glosstr\glend}}{}',
1805 r' ResetsFont true',
1807 r' Decoration conglomerate',
1808 r' LabelString "Translation"',
1809 r' MenuString "Glosse Translation|s"',
1810 r' Tooltip "Add a translation for the glosse"',
1815 glosss_inset_def = [
1816 r'### Inserted by lyx2lyx (deprecated ling glosses) ###',
1817 r'InsetLayout Flex:Tri-Glosse',
1819 r' LabelString "Tri-Gloss (old version)"',
1820 r' MenuString "Tri-Gloss (old version)"',
1821 r' LatexType environment',
1822 r' LatexName lingglosss',
1823 r' Decoration minimalistic',
1828 r' CustomPars false',
1829 r' ForcePlain true',
1830 r' ParbreakIsNewline true',
1831 r' FreeSpacing true',
1833 r' Requires covington',
1836 r' \@ifundefined{lingglosss}{%',
1837 r' \newenvironment{lingglosss}[2][]{',
1838 r' \def\glosstr{\glt #1}%',
1840 r' {\glosstr\glend}}{}',
1842 r' ResetsFont true',
1844 r' Decoration conglomerate',
1845 r' LabelString "Translation"',
1846 r' MenuString "Glosse Translation|s"',
1847 r' Tooltip "Add a translation for the glosse"',
1852 def convert_linggloss(document):
1853 " Move old ling glosses to local layout "
1854 if find_token(document.body, '\\begin_inset Flex Glosse', 0) != -1:
1855 document.append_local_layout(gloss_inset_def)
1856 if find_token(document.body, '\\begin_inset Flex Tri-Glosse', 0) != -1:
1857 document.append_local_layout(glosss_inset_def)
1859 def revert_linggloss(document):
1860 " Revert to old ling gloss definitions "
1861 if not "linguistics" in document.get_module_list():
1863 document.del_local_layout(gloss_inset_def)
1864 document.del_local_layout(glosss_inset_def)
1867 glosses = ["\\begin_inset Flex Interlinear Gloss (2 Lines)", "\\begin_inset Flex Interlinear Gloss (3 Lines)"]
1868 for glosse in glosses:
1871 i = find_token(document.body, glosse, i+1)
1874 j = find_end_of_inset(document.body, i)
1876 document.warning("Malformed LyX document: Can't find end of Gloss inset")
1879 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
1880 endarg = find_end_of_inset(document.body, arg)
1883 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
1884 if argbeginPlain == -1:
1885 document.warning("Malformed LyX document: Can't find optarg plain Layout")
1887 argendPlain = find_end_of_inset(document.body, argbeginPlain)
1888 optargcontent = document.body[argbeginPlain + 1 : argendPlain - 2]
1890 # remove Arg insets and paragraph, if it only contains this inset
1891 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
1892 del document.body[arg - 1 : endarg + 4]
1894 del document.body[arg : endarg + 1]
1896 arg = find_token(document.body, "\\begin_inset Argument post:1", i, j)
1897 endarg = find_end_of_inset(document.body, arg)
1900 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
1901 if argbeginPlain == -1:
1902 document.warning("Malformed LyX document: Can't find arg 1 plain Layout")
1904 argendPlain = find_end_of_inset(document.body, argbeginPlain)
1905 marg1content = document.body[argbeginPlain + 1 : argendPlain - 2]
1907 # remove Arg insets and paragraph, if it only contains this inset
1908 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
1909 del document.body[arg - 1 : endarg + 4]
1911 del document.body[arg : endarg + 1]
1913 arg = find_token(document.body, "\\begin_inset Argument post:2", i, j)
1914 endarg = find_end_of_inset(document.body, arg)
1917 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
1918 if argbeginPlain == -1:
1919 document.warning("Malformed LyX document: Can't find arg 2 plain Layout")
1921 argendPlain = find_end_of_inset(document.body, argbeginPlain)
1922 marg2content = document.body[argbeginPlain + 1 : argendPlain - 2]
1924 # remove Arg insets and paragraph, if it only contains this inset
1925 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
1926 del document.body[arg - 1 : endarg + 4]
1928 del document.body[arg : endarg + 1]
1930 arg = find_token(document.body, "\\begin_inset Argument post:3", i, j)
1931 endarg = find_end_of_inset(document.body, arg)
1934 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
1935 if argbeginPlain == -1:
1936 document.warning("Malformed LyX document: Can't find arg 3 plain Layout")
1938 argendPlain = find_end_of_inset(document.body, argbeginPlain)
1939 marg3content = document.body[argbeginPlain + 1 : argendPlain - 2]
1941 # remove Arg insets and paragraph, if it only contains this inset
1942 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
1943 del document.body[arg - 1 : endarg + 4]
1945 del document.body[arg : endarg + 1]
1948 if glosse == "\\begin_inset Flex Interlinear Gloss (3 Lines)":
1951 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
1952 endInset = find_end_of_inset(document.body, i)
1953 endPlain = find_token_backwards(document.body, "\\end_layout", endInset)
1954 precontent = put_cmd_in_ert(cmd)
1955 if len(optargcontent) > 0:
1956 precontent += put_cmd_in_ert("[") + optargcontent + put_cmd_in_ert("]")
1957 precontent += put_cmd_in_ert("{")
1959 postcontent = put_cmd_in_ert("}{") + marg1content + put_cmd_in_ert("}{") + marg2content
1960 if cmd == "\\trigloss":
1961 postcontent += put_cmd_in_ert("}{") + marg3content
1962 postcontent += put_cmd_in_ert("}")
1964 document.body[endPlain:endInset + 1] = postcontent
1965 document.body[beginPlain + 1:beginPlain] = precontent
1966 del document.body[i : beginPlain + 1]
1968 document.append_local_layout("Requires covington")
1973 def revert_subexarg(document):
1974 " Revert linguistic subexamples with argument to ERT "
1976 if not "linguistics" in document.get_module_list():
1982 i = find_token(document.body, "\\begin_layout Subexample", i+1)
1985 j = find_end_of_layout(document.body, i)
1987 document.warning("Malformed LyX document: Can't find end of Subexample layout")
1990 # check for consecutive layouts
1991 k = find_token(document.body, "\\begin_layout", j)
1992 if k == -1 or document.body[k] != "\\begin_layout Subexample":
1994 j = find_end_of_layout(document.body, k)
1996 document.warning("Malformed LyX document: Can't find end of Subexample layout")
1999 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
2003 endarg = find_end_of_inset(document.body, arg)
2005 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
2006 if argbeginPlain == -1:
2007 document.warning("Malformed LyX document: Can't find optarg plain Layout")
2009 argendPlain = find_end_of_inset(document.body, argbeginPlain)
2010 optargcontent = lyx2latex(document, document.body[argbeginPlain + 1 : argendPlain - 2])
2012 # remove Arg insets and paragraph, if it only contains this inset
2013 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
2014 del document.body[arg - 1 : endarg + 4]
2016 del document.body[arg : endarg + 1]
2018 cmd = put_cmd_in_ert("\\begin{subexamples}[" + optargcontent + "]")
2020 # re-find end of layout
2021 j = find_end_of_layout(document.body, i)
2023 document.warning("Malformed LyX document: Can't find end of Subexample layout")
2026 # check for consecutive layouts
2027 k = find_token(document.body, "\\begin_layout", j)
2028 if k == -1 or document.body[k] != "\\begin_layout Subexample":
2030 document.body[k : k + 1] = ["\\begin_layout Standard"] + put_cmd_in_ert("\\item ")
2031 j = find_end_of_layout(document.body, k)
2033 document.warning("Malformed LyX document: Can't find end of Subexample layout")
2036 endev = put_cmd_in_ert("\\end{subexamples}")
2038 document.body[j : j] = ["\\end_layout", "", "\\begin_layout Standard"] + endev
2039 document.body[i : i + 1] = ["\\begin_layout Standard"] + cmd \
2040 + ["\\end_layout", "", "\\begin_layout Standard"] + put_cmd_in_ert("\\item ")
2042 document.append_local_layout("Requires covington")
2046 def revert_drs(document):
2047 " Revert DRS insets (linguistics) to ERT "
2049 if not "linguistics" in document.get_module_list():
2053 drses = ["\\begin_inset Flex DRS", "\\begin_inset Flex DRS*",
2054 "\\begin_inset Flex IfThen-DRS", "\\begin_inset Flex Cond-DRS",
2055 "\\begin_inset Flex QDRS", "\\begin_inset Flex NegDRS",
2056 "\\begin_inset Flex SDRS"]
2060 i = find_token(document.body, drs, i+1)
2063 j = find_end_of_inset(document.body, i)
2065 document.warning("Malformed LyX document: Can't find end of DRS inset")
2068 # Check for arguments
2069 arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
2070 endarg = find_end_of_inset(document.body, arg)
2073 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
2074 if argbeginPlain == -1:
2075 document.warning("Malformed LyX document: Can't find Argument 1 plain Layout")
2077 argendPlain = find_end_of_inset(document.body, argbeginPlain)
2078 prearg1content = document.body[argbeginPlain + 1 : argendPlain - 2]
2080 # remove Arg insets and paragraph, if it only contains this inset
2081 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
2082 del document.body[arg - 1 : endarg + 4]
2084 del document.body[arg : endarg + 1]
2087 j = find_end_of_inset(document.body, i)
2089 document.warning("Malformed LyX document: Can't find end of DRS inset")
2092 arg = find_token(document.body, "\\begin_inset Argument 2", i, j)
2093 endarg = find_end_of_inset(document.body, arg)
2096 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
2097 if argbeginPlain == -1:
2098 document.warning("Malformed LyX document: Can't find Argument 2 plain Layout")
2100 argendPlain = find_end_of_inset(document.body, argbeginPlain)
2101 prearg2content = document.body[argbeginPlain + 1 : argendPlain - 2]
2103 # remove Arg insets and paragraph, if it only contains this inset
2104 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
2105 del document.body[arg - 1 : endarg + 4]
2107 del document.body[arg : endarg + 1]
2110 j = find_end_of_inset(document.body, i)
2112 document.warning("Malformed LyX document: Can't find end of DRS inset")
2115 arg = find_token(document.body, "\\begin_inset Argument post:1", i, j)
2116 endarg = find_end_of_inset(document.body, arg)
2117 postarg1content = []
2119 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
2120 if argbeginPlain == -1:
2121 document.warning("Malformed LyX document: Can't find Argument post:1 plain Layout")
2123 argendPlain = find_end_of_inset(document.body, argbeginPlain)
2124 postarg1content = document.body[argbeginPlain + 1 : argendPlain - 2]
2126 # remove Arg insets and paragraph, if it only contains this inset
2127 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
2128 del document.body[arg - 1 : endarg + 4]
2130 del document.body[arg : endarg + 1]
2133 j = find_end_of_inset(document.body, i)
2135 document.warning("Malformed LyX document: Can't find end of DRS inset")
2138 arg = find_token(document.body, "\\begin_inset Argument post:2", i, j)
2139 endarg = find_end_of_inset(document.body, arg)
2140 postarg2content = []
2142 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
2143 if argbeginPlain == -1:
2144 document.warning("Malformed LyX document: Can't find Argument post:2 plain Layout")
2146 argendPlain = find_end_of_inset(document.body, argbeginPlain)
2147 postarg2content = document.body[argbeginPlain + 1 : argendPlain - 2]
2149 # remove Arg insets and paragraph, if it only contains this inset
2150 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
2151 del document.body[arg - 1 : endarg + 4]
2153 del document.body[arg : endarg + 1]
2156 j = find_end_of_inset(document.body, i)
2158 document.warning("Malformed LyX document: Can't find end of DRS inset")
2161 arg = find_token(document.body, "\\begin_inset Argument post:3", i, j)
2162 endarg = find_end_of_inset(document.body, arg)
2163 postarg3content = []
2165 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
2166 if argbeginPlain == -1:
2167 document.warning("Malformed LyX document: Can't find Argument post:3 plain Layout")
2169 argendPlain = find_end_of_inset(document.body, argbeginPlain)
2170 postarg3content = document.body[argbeginPlain + 1 : argendPlain - 2]
2172 # remove Arg insets and paragraph, if it only contains this inset
2173 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
2174 del document.body[arg - 1 : endarg + 4]
2176 del document.body[arg : endarg + 1]
2179 j = find_end_of_inset(document.body, i)
2181 document.warning("Malformed LyX document: Can't find end of DRS inset")
2184 arg = find_token(document.body, "\\begin_inset Argument post:4", i, j)
2185 endarg = find_end_of_inset(document.body, arg)
2186 postarg4content = []
2188 argbeginPlain = find_token(document.body, "\\begin_layout Plain Layout", arg, endarg)
2189 if argbeginPlain == -1:
2190 document.warning("Malformed LyX document: Can't find Argument post:4 plain Layout")
2192 argendPlain = find_end_of_inset(document.body, argbeginPlain)
2193 postarg4content = document.body[argbeginPlain + 1 : argendPlain - 2]
2195 # remove Arg insets and paragraph, if it only contains this inset
2196 if document.body[arg - 1] == "\\begin_layout Plain Layout" and find_end_of_layout(document.body, arg - 1) == endarg + 3:
2197 del document.body[arg - 1 : endarg + 4]
2199 del document.body[arg : endarg + 1]
2201 # The respective LaTeX command
2203 if drs == "\\begin_inset Flex DRS*":
2205 elif drs == "\\begin_inset Flex IfThen-DRS":
2207 elif drs == "\\begin_inset Flex Cond-DRS":
2209 elif drs == "\\begin_inset Flex QDRS":
2211 elif drs == "\\begin_inset Flex NegDRS":
2213 elif drs == "\\begin_inset Flex SDRS":
2216 beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
2217 endInset = find_end_of_inset(document.body, i)
2218 endPlain = find_token_backwards(document.body, "\\end_layout", endInset)
2219 precontent = put_cmd_in_ert(cmd)
2220 precontent += put_cmd_in_ert("{") + prearg1content + put_cmd_in_ert("}")
2221 if drs == "\\begin_inset Flex SDRS":
2222 precontent += put_cmd_in_ert("{") + prearg2content + put_cmd_in_ert("}")
2223 precontent += put_cmd_in_ert("{")
2226 if cmd == "\\qdrs" or cmd == "\\condrs" or cmd == "\\ifdrs":
2227 postcontent = put_cmd_in_ert("}{") + postarg1content + put_cmd_in_ert("}{") + postarg2content + put_cmd_in_ert("}")
2228 if cmd == "\\condrs" or cmd == "\\qdrs":
2229 postcontent += put_cmd_in_ert("{") + postarg3content + put_cmd_in_ert("}")
2231 postcontent += put_cmd_in_ert("{") + postarg4content + put_cmd_in_ert("}")
2233 postcontent = put_cmd_in_ert("}")
2235 document.body[endPlain:endInset + 1] = postcontent
2236 document.body[beginPlain + 1:beginPlain] = precontent
2237 del document.body[i : beginPlain + 1]
2239 document.append_local_layout("Provides covington 1")
2240 add_to_preamble(document, ["\\usepackage{drs,covington}"])
2246 def revert_babelfont(document):
2247 " Reverts the use of \\babelfont to user preamble "
2249 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2251 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2253 if not str2bool(get_value(document.header, "\\use_non_tex_fonts", i)):
2255 i = find_token(document.header, '\\language_package', 0)
2257 document.warning("Malformed LyX document: Missing \\language_package.")
2259 if get_value(document.header, "\\language_package", 0) != "babel":
2262 # check font settings
2264 roman = sans = typew = "default"
2266 sf_scale = tt_scale = 100.0
2268 j = find_token(document.header, "\\font_roman", 0)
2270 document.warning("Malformed LyX document: Missing \\font_roman.")
2272 # We need to use this regex since split() does not handle quote protection
2273 romanfont = re.findall(r'[^"\s]\S*|".+?"', document.header[j])
2274 roman = romanfont[2].strip('"')
2275 romanfont[2] = '"default"'
2276 document.header[j] = " ".join(romanfont)
2278 j = find_token(document.header, "\\font_sans", 0)
2280 document.warning("Malformed LyX document: Missing \\font_sans.")
2282 # We need to use this regex since split() does not handle quote protection
2283 sansfont = re.findall(r'[^"\s]\S*|".+?"', document.header[j])
2284 sans = sansfont[2].strip('"')
2285 sansfont[2] = '"default"'
2286 document.header[j] = " ".join(sansfont)
2288 j = find_token(document.header, "\\font_typewriter", 0)
2290 document.warning("Malformed LyX document: Missing \\font_typewriter.")
2292 # We need to use this regex since split() does not handle quote protection
2293 ttfont = re.findall(r'[^"\s]\S*|".+?"', document.header[j])
2294 typew = ttfont[2].strip('"')
2295 ttfont[2] = '"default"'
2296 document.header[j] = " ".join(ttfont)
2298 i = find_token(document.header, "\\font_osf", 0)
2300 document.warning("Malformed LyX document: Missing \\font_osf.")
2302 osf = str2bool(get_value(document.header, "\\font_osf", i))
2304 j = find_token(document.header, "\\font_sf_scale", 0)
2306 document.warning("Malformed LyX document: Missing \\font_sf_scale.")
2308 sfscale = document.header[j].split()
2311 document.header[j] = " ".join(sfscale)
2314 sf_scale = float(val)
2316 document.warning("Invalid font_sf_scale value: " + val)
2318 j = find_token(document.header, "\\font_tt_scale", 0)
2320 document.warning("Malformed LyX document: Missing \\font_tt_scale.")
2322 ttscale = document.header[j].split()
2325 document.header[j] = " ".join(ttscale)
2328 tt_scale = float(val)
2330 document.warning("Invalid font_tt_scale value: " + val)
2332 # set preamble stuff
2333 pretext = ['%% This document must be processed with xelatex or lualatex!']
2334 pretext.append('\\AtBeginDocument{%')
2335 if roman != "default":
2336 pretext.append('\\babelfont{rm}[Mapping=tex-text]{' + roman + '}')
2337 if sans != "default":
2338 sf = '\\babelfont{sf}['
2339 if sf_scale != 100.0:
2340 sf += 'Scale=' + str(sf_scale / 100.0) + ','
2341 sf += 'Mapping=tex-text]{' + sans + '}'
2343 if typew != "default":
2344 tw = '\\babelfont{tt}'
2345 if tt_scale != 100.0:
2346 tw += '[Scale=' + str(tt_scale / 100.0) + ']'
2347 tw += '{' + typew + '}'
2350 pretext.append('\\defaultfontfeatures{Numbers=OldStyle}')
2352 insert_to_preamble(document, pretext)
2355 def revert_minionpro(document):
2356 " Revert native MinionPro font definition (with extra options) to LaTeX "
2358 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2360 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2362 if str2bool(get_value(document.header, "\\use_non_tex_fonts", i)):
2365 regexp = re.compile(r'(\\font_roman_opts)')
2366 x = find_re(document.header, regexp, 0)
2370 # We need to use this regex since split() does not handle quote protection
2371 romanopts = re.findall(r'[^"\s]\S*|".+?"', document.header[x])
2372 opts = romanopts[1].strip('"')
2374 i = find_token(document.header, "\\font_roman", 0)
2376 document.warning("Malformed LyX document: Missing \\font_roman.")
2379 # We need to use this regex since split() does not handle quote protection
2380 romanfont = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2381 roman = romanfont[1].strip('"')
2382 if roman != "minionpro":
2384 romanfont[1] = '"default"'
2385 document.header[i] = " ".join(romanfont)
2387 j = find_token(document.header, "\\font_osf true", 0)
2390 preamble = "\\usepackage["
2392 document.header[j] = "\\font_osf false"
2396 preamble += "]{MinionPro}"
2397 add_to_preamble(document, [preamble])
2398 del document.header[x]
2401 def revert_font_opts(document):
2402 " revert font options by outputting \\setxxxfont or \\babelfont to the preamble "
2404 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2406 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2408 NonTeXFonts = str2bool(get_value(document.header, "\\use_non_tex_fonts", i))
2409 i = find_token(document.header, '\\language_package', 0)
2411 document.warning("Malformed LyX document: Missing \\language_package.")
2413 Babel = (get_value(document.header, "\\language_package", 0) == "babel")
2416 regexp = re.compile(r'(\\font_roman_opts)')
2417 i = find_re(document.header, regexp, 0)
2419 # We need to use this regex since split() does not handle quote protection
2420 romanopts = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2421 opts = romanopts[1].strip('"')
2422 del document.header[i]
2424 regexp = re.compile(r'(\\font_roman)')
2425 i = find_re(document.header, regexp, 0)
2427 # We need to use this regex since split() does not handle quote protection
2428 romanfont = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2429 font = romanfont[2].strip('"')
2430 romanfont[2] = '"default"'
2431 document.header[i] = " ".join(romanfont)
2432 if font != "default":
2434 preamble = "\\babelfont{rm}["
2436 preamble = "\\setmainfont["
2439 preamble += "Mapping=tex-text]{"
2442 add_to_preamble(document, [preamble])
2445 regexp = re.compile(r'(\\font_sans_opts)')
2446 i = find_re(document.header, regexp, 0)
2449 # We need to use this regex since split() does not handle quote protection
2450 sfopts = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2451 opts = sfopts[1].strip('"')
2452 del document.header[i]
2454 regexp = re.compile(r'(\\font_sf_scale)')
2455 i = find_re(document.header, regexp, 0)
2457 scaleval = get_value(document.header, "\\font_sf_scale" , i).split()[1]
2458 regexp = re.compile(r'(\\font_sans)')
2459 i = find_re(document.header, regexp, 0)
2461 # We need to use this regex since split() does not handle quote protection
2462 sffont = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2463 font = sffont[2].strip('"')
2464 sffont[2] = '"default"'
2465 document.header[i] = " ".join(sffont)
2466 if font != "default":
2468 preamble = "\\babelfont{sf}["
2470 preamble = "\\setsansfont["
2474 preamble += "Scale=0."
2475 preamble += scaleval
2477 preamble += "Mapping=tex-text]{"
2480 add_to_preamble(document, [preamble])
2483 regexp = re.compile(r'(\\font_typewriter_opts)')
2484 i = find_re(document.header, regexp, 0)
2487 # We need to use this regex since split() does not handle quote protection
2488 ttopts = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2489 opts = ttopts[1].strip('"')
2490 del document.header[i]
2492 regexp = re.compile(r'(\\font_tt_scale)')
2493 i = find_re(document.header, regexp, 0)
2495 scaleval = get_value(document.header, "\\font_tt_scale" , i).split()[1]
2496 regexp = re.compile(r'(\\font_typewriter)')
2497 i = find_re(document.header, regexp, 0)
2499 # We need to use this regex since split() does not handle quote protection
2500 ttfont = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2501 font = ttfont[2].strip('"')
2502 ttfont[2] = '"default"'
2503 document.header[i] = " ".join(ttfont)
2504 if font != "default":
2506 preamble = "\\babelfont{tt}["
2508 preamble = "\\setmonofont["
2512 preamble += "Scale=0."
2513 preamble += scaleval
2515 preamble += "Mapping=tex-text]{"
2518 add_to_preamble(document, [preamble])
2521 def revert_plainNotoFonts_xopts(document):
2522 " Revert native (straight) Noto font definition (with extra options) to LaTeX "
2524 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2526 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2528 if str2bool(get_value(document.header, "\\use_non_tex_fonts", i)):
2532 y = find_token(document.header, "\\font_osf true", 0)
2536 regexp = re.compile(r'(\\font_roman_opts)')
2537 x = find_re(document.header, regexp, 0)
2538 if x == -1 and not osf:
2543 # We need to use this regex since split() does not handle quote protection
2544 romanopts = re.findall(r'[^"\s]\S*|".+?"', document.header[x])
2545 opts = romanopts[1].strip('"')
2551 i = find_token(document.header, "\\font_roman", 0)
2555 # We need to use this regex since split() does not handle quote protection
2556 romanfont = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2557 roman = romanfont[1].strip('"')
2558 if roman != "NotoSerif-TLF":
2561 j = find_token(document.header, "\\font_sans", 0)
2565 # We need to use this regex since split() does not handle quote protection
2566 sffont = re.findall(r'[^"\s]\S*|".+?"', document.header[j])
2567 sf = sffont[1].strip('"')
2571 j = find_token(document.header, "\\font_typewriter", 0)
2575 # We need to use this regex since split() does not handle quote protection
2576 ttfont = re.findall(r'[^"\s]\S*|".+?"', document.header[j])
2577 tt = ttfont[1].strip('"')
2581 # So we have noto as "complete font"
2582 romanfont[1] = '"default"'
2583 document.header[i] = " ".join(romanfont)
2585 preamble = "\\usepackage["
2587 preamble += "]{noto}"
2588 add_to_preamble(document, [preamble])
2590 document.header[y] = "\\font_osf false"
2592 del document.header[x]
2595 def revert_notoFonts_xopts(document):
2596 " Revert native (extended) Noto font definition (with extra options) to LaTeX "
2598 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2600 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2602 if str2bool(get_value(document.header, "\\use_non_tex_fonts", i)):
2606 fm = createFontMapping(['Noto'])
2607 if revert_fonts(document, fm, fontmap, True):
2608 add_preamble_fonts(document, fontmap)
2611 def revert_IBMFonts_xopts(document):
2612 " Revert native IBM font definition (with extra options) to LaTeX "
2614 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2616 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2618 if str2bool(get_value(document.header, "\\use_non_tex_fonts", i)):
2622 fm = createFontMapping(['IBM'])
2624 if revert_fonts(document, fm, fontmap, True):
2625 add_preamble_fonts(document, fontmap)
2628 def revert_AdobeFonts_xopts(document):
2629 " Revert native Adobe font definition (with extra options) to LaTeX "
2631 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2633 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2635 if str2bool(get_value(document.header, "\\use_non_tex_fonts", i)):
2639 fm = createFontMapping(['Adobe'])
2641 if revert_fonts(document, fm, fontmap, True):
2642 add_preamble_fonts(document, fontmap)
2645 def revert_CantarellFont_xopts(document):
2646 " Revert native (extended) Cantarell font definition (with extra options) to LaTeX "
2648 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2650 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2652 if str2bool(get_value(document.header, "\\use_non_tex_fonts", i)):
2656 fm = createFontMapping(['Cantarell'])
2657 if revert_fonts(document, fm, fontmap, True):
2658 add_preamble_fonts(document, fontmap)
2661 def convert_osf(document):
2662 " Convert \\font_osf param to new format "
2665 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2667 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2669 NonTeXFonts = str2bool(get_value(document.header, "\\use_non_tex_fonts", i))
2671 i = find_token(document.header, '\\font_osf', 0)
2673 document.warning("Malformed LyX document: Missing \\font_osf.")
2676 osfsf = ["biolinum", "ADOBESourceSansPro", "NotoSansRegular", "NotoSansMedium", "NotoSansThin", "NotoSansLight", "NotoSansExtralight" ]
2677 osftt = ["ADOBESourceCodePro", "NotoMonoRegular" ]
2679 osfval = str2bool(get_value(document.header, "\\font_osf", i))
2680 document.header[i] = document.header[i].replace("\\font_osf", "\\font_roman_osf")
2683 document.header.insert(i, "\\font_sans_osf false")
2684 document.header.insert(i + 1, "\\font_typewriter_osf false")
2688 x = find_token(document.header, "\\font_sans", 0)
2690 document.warning("Malformed LyX document: Missing \\font_sans.")
2692 # We need to use this regex since split() does not handle quote protection
2693 sffont = re.findall(r'[^"\s]\S*|".+?"', document.header[x])
2694 sf = sffont[1].strip('"')
2696 document.header.insert(i, "\\font_sans_osf true")
2698 document.header.insert(i, "\\font_sans_osf false")
2700 x = find_token(document.header, "\\font_typewriter", 0)
2702 document.warning("Malformed LyX document: Missing \\font_typewriter.")
2704 # We need to use this regex since split() does not handle quote protection
2705 ttfont = re.findall(r'[^"\s]\S*|".+?"', document.header[x])
2706 tt = ttfont[1].strip('"')
2708 document.header.insert(i + 1, "\\font_sans_osf true")
2710 document.header.insert(i + 1, "\\font_sans_osf false")
2713 document.header.insert(i, "\\font_sans_osf false")
2714 document.header.insert(i + 1, "\\font_typewriter_osf false")
2717 def revert_osf(document):
2718 " Revert \\font_*_osf params "
2721 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2723 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2725 NonTeXFonts = str2bool(get_value(document.header, "\\use_non_tex_fonts", i))
2727 i = find_token(document.header, '\\font_roman_osf', 0)
2729 document.warning("Malformed LyX document: Missing \\font_roman_osf.")
2732 osfval = str2bool(get_value(document.header, "\\font_roman_osf", i))
2733 document.header[i] = document.header[i].replace("\\font_roman_osf", "\\font_osf")
2735 i = find_token(document.header, '\\font_sans_osf', 0)
2737 document.warning("Malformed LyX document: Missing \\font_sans_osf.")
2740 osfval = str2bool(get_value(document.header, "\\font_sans_osf", i))
2741 del document.header[i]
2743 i = find_token(document.header, '\\font_typewriter_osf', 0)
2745 document.warning("Malformed LyX document: Missing \\font_typewriter_osf.")
2748 osfval |= str2bool(get_value(document.header, "\\font_typewriter_osf", i))
2749 del document.header[i]
2752 i = find_token(document.header, '\\font_osf', 0)
2754 document.warning("Malformed LyX document: Missing \\font_osf.")
2756 document.header[i] = "\\font_osf true"
2759 def revert_texfontopts(document):
2760 " Revert native TeX font definitions (with extra options) to LaTeX "
2762 i = find_token(document.header, '\\use_non_tex_fonts', 0)
2764 document.warning("Malformed LyX document: Missing \\use_non_tex_fonts.")
2766 if str2bool(get_value(document.header, "\\use_non_tex_fonts", i)):
2769 rmfonts = ["ccfonts", "cochineal", "utopia", "garamondx", "libertine", "lmodern", "palatino", "times", "xcharter" ]
2771 # First the sf (biolinum only)
2772 regexp = re.compile(r'(\\font_sans_opts)')
2773 x = find_re(document.header, regexp, 0)
2775 # We need to use this regex since split() does not handle quote protection
2776 sfopts = re.findall(r'[^"\s]\S*|".+?"', document.header[x])
2777 opts = sfopts[1].strip('"')
2778 i = find_token(document.header, "\\font_sans", 0)
2780 document.warning("Malformed LyX document: Missing \\font_sans.")
2782 # We need to use this regex since split() does not handle quote protection
2783 sffont = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2784 sans = sffont[1].strip('"')
2785 if sans == "biolinum":
2787 sffont[1] = '"default"'
2788 document.header[i] = " ".join(sffont)
2790 j = find_token(document.header, "\\font_sans_osf true", 0)
2793 k = find_token(document.header, "\\font_sf_scale", 0)
2795 document.warning("Malformed LyX document: Missing \\font_sf_scale.")
2797 sfscale = document.header[k].split()
2800 document.header[k] = " ".join(sfscale)
2803 sf_scale = float(val)
2805 document.warning("Invalid font_sf_scale value: " + val)
2806 preamble = "\\usepackage["
2808 document.header[j] = "\\font_sans_osf false"
2810 if sf_scale != 100.0:
2811 preamble += 'scaled=' + str(sf_scale / 100.0) + ','
2813 preamble += "]{biolinum}"
2814 add_to_preamble(document, [preamble])
2815 del document.header[x]
2817 regexp = re.compile(r'(\\font_roman_opts)')
2818 x = find_re(document.header, regexp, 0)
2822 # We need to use this regex since split() does not handle quote protection
2823 romanopts = re.findall(r'[^"\s]\S*|".+?"', document.header[x])
2824 opts = romanopts[1].strip('"')
2826 i = find_token(document.header, "\\font_roman", 0)
2828 document.warning("Malformed LyX document: Missing \\font_roman.")
2831 # We need to use this regex since split() does not handle quote protection
2832 romanfont = re.findall(r'[^"\s]\S*|".+?"', document.header[i])
2833 roman = romanfont[1].strip('"')
2834 if not roman in rmfonts:
2836 romanfont[1] = '"default"'
2837 document.header[i] = " ".join(romanfont)
2839 if roman == "utopia":
2841 elif roman == "palatino":
2842 package = "mathpazo"
2843 elif roman == "times":
2844 package = "mathptmx"
2845 elif roman == "xcharter":
2846 package = "XCharter"
2848 j = find_token(document.header, "\\font_roman_osf true", 0)
2850 if roman == "cochineal":
2851 osf = "proportional,osf,"
2852 elif roman == "utopia":
2854 elif roman == "garamondx":
2856 elif roman == "libertine":
2858 elif roman == "palatino":
2860 elif roman == "xcharter":
2862 document.header[j] = "\\font_roman_osf false"
2863 k = find_token(document.header, "\\font_sc true", 0)
2865 if roman == "utopia":
2867 if roman == "palatino" and osf == "":
2869 document.header[k] = "\\font_sc false"
2870 preamble = "\\usepackage["
2873 preamble += "]{" + package + "}"
2874 add_to_preamble(document, [preamble])
2875 del document.header[x]
2882 supported_versions = ["2.4.0", "2.4"]
2884 [545, [convert_lst_literalparam]],
2889 [550, [convert_fontenc]],
2896 [557, [convert_vcsinfo]],
2897 [558, [removeFrontMatterStyles]],
2900 [561, [convert_latexFonts]], # Handle dejavu, ibmplex fonts in GUI
2904 [565, [convert_AdobeFonts]], # Handle adobe fonts in GUI
2905 [566, [convert_hebrew_parentheses]],
2911 [572, [convert_notoFonts]], # Added options thin, light, extralight for Noto
2912 [573, [convert_inputencoding_namechange]],
2913 [574, [convert_ruby_module, convert_utf8_japanese]],
2914 [575, [convert_lineno]],
2916 [577, [convert_linggloss]],
2920 [581, [convert_osf]],
2921 [582, [convert_CantarellFont]],
2924 revert = [[581, [revert_CantarellFont, revert_CantarellFont_xopts]],
2925 [580, [revert_texfontopts,revert_osf]],
2926 [579, [revert_minionpro, revert_plainNotoFonts_xopts, revert_notoFonts_xopts, revert_IBMFonts_xopts, revert_AdobeFonts_xopts, revert_font_opts]], # keep revert_font_opts last!
2927 [578, [revert_babelfont]],
2928 [577, [revert_drs]],
2929 [576, [revert_linggloss, revert_subexarg]],
2930 [575, [revert_new_languages]],
2931 [574, [revert_lineno]],
2932 [573, [revert_ruby_module, revert_utf8_japanese]],
2933 [572, [revert_inputencoding_namechange]],
2934 [571, [revert_notoFonts]],
2935 [570, [revert_cmidruletrimming]],
2936 [569, [revert_bibfileencodings]],
2937 [568, [revert_tablestyle]],
2938 [567, [revert_soul]],
2939 [566, [revert_malayalam]],
2940 [565, [revert_hebrew_parentheses]],
2941 [564, [revert_AdobeFonts]],
2942 [563, [revert_lformatinfo]],
2943 [562, [revert_listpargs]],
2944 [561, [revert_l7ninfo]],
2945 [560, [revert_latexFonts]], # Handle dejavu, ibmplex fonts in user preamble
2946 [559, [revert_timeinfo, revert_namenoextinfo]],
2947 [558, [revert_dateinfo]],
2948 [557, [addFrontMatterStyles]],
2949 [556, [revert_vcsinfo]],
2950 [555, [revert_bibencoding]],
2951 [554, [revert_vcolumns]],
2952 [553, [revert_stretchcolumn]],
2953 [552, [revert_tuftecite]],
2954 [551, [revert_floatpclass, revert_floatalignment]],
2955 [550, [revert_nospellcheck]],
2956 [549, [revert_fontenc]],
2957 [548, []],# dummy format change
2958 [547, [revert_lscape]],
2959 [546, [revert_xcharter]],
2960 [545, [revert_paratype]],
2961 [544, [revert_lst_literalparam]]
2965 if __name__ == "__main__":