import unicodedata
import sys, os
-from parser_tools import find_token, find_end_of, find_tokens, get_value, get_value_string
+from parser_tools import find_token, find_end_of, find_tokens, get_value
####################################################################
# Private helper functions
+
+def get_value_string(lines, token, start, end = 0, trim = False, default = ""):
+ """ get_value_string(lines, token, start[[, end], trim, default]) -> string
+
+ Return tokens after token as string, in lines, where
+ token is the first element. When trim is used, the first and last character
+ of the string is trimmed."""
+
+ val = get_value(lines, token, start, end, "")
+ if not val:
+ return default
+ if trim:
+ return val[1:-1]
+ return val
+
+
def find_end_of_inset(lines, i):
" Find end of inset, where lines[i] is included."
return find_end_of(lines, i, "\\begin_inset", "\\end_inset")
+ dst + '\n\\end_layout\n\\end_inset\n')
def put_cmd_in_ert(string):
+ for rep in unicode_reps:
+ string = string.replace(rep[1], rep[0].replace('\\\\', '\\'))
string = string.replace('\\', "\\backslash\n")
string = "\\begin_inset ERT\nstatus collapsed\n\\begin_layout Standard\n" \
+ string + "\n\\end_layout\n\\end_inset"
# Two backslashes, followed by some non-word character, and then a character
# in brackets. The idea is to check for constructs like: \"{u}, which is how
# they are written in the unicodesymbols file; but they can also be written
- # as: \"u.
+ # as: \"u or even \" u.
r = re.compile(r'\\\\(\W)\{(\w)\}')
for line in fp.readlines():
if line[0] != '#' and line.strip() != "":
# since it is done that way in the LyX file.
if m.group(1) == "\"":
command += "\\"
+ commandbl = command
command += m.group(1) + m.group(2)
+ commandbl += m.group(1) + ' ' + m.group(2)
spec_chars.append([command, unichr(eval(ucs4))])
+ spec_chars.append([commandbl, unichr(eval(ucs4))])
fp.close()
return spec_chars
return (line[:pos + 1], line[pos + 1:])
-def latex2ert(line):
+def latex2ert(line, isindex):
'''Converts LaTeX commands into ERT. line may well be a multi-line
string when it is returned.'''
if not line:
retval += "\n" + cmd + "\n"
line = end
m = labelre.match(line)
+ # put all remaining braces in ERT
+ line = wrap_into_ert(line, '}', '}')
+ line = wrap_into_ert(line, '{', '{')
+ if isindex:
+ # active character that is not available in all font encodings
+ line = wrap_into_ert(line, '|', '|')
retval += line
return retval
+unicode_reps = read_unicodesymbols()
+
#Bug 5022....
#Might should do latex2ert first, then deal with stuff that DOESN'T
#end up inside ERT. That routine could be modified so that it returned
#a list of lines, and we could then skip ERT bits and only deal with
#the other bits.
-def latex2lyx(data):
+def latex2lyx(data, isindex):
'''Takes a string, possibly multi-line, and returns the result of
converting LaTeX constructs into LyX constructs. Returns a list of
- lines, suitable for insertion into document.body.'''
+ lines, suitable for insertion into document.body.
+ The bool isindex specifies whether we are in an index macro (which
+ has some specific active characters that need to be ERTed).'''
+ if not data:
+ return [""]
retval = []
# Convert LaTeX to Unicode
- reps = read_unicodesymbols()
# Commands of this sort need to be checked to make sure they are
# followed by a non-alpha character, lest we replace too much.
hardone = re.compile(r'^\\\\[a-zA-Z]+$')
- for rep in reps:
+ for rep in unicode_reps:
if hardone.match(rep[0]):
pos = 0
while True:
else:
data = data.replace(rep[0], rep[1])
- # Generic, \" -> ":
+ # Generic
+ # \" -> ":
data = wrap_into_ert(data, r'\"', '"')
+ # \\ -> \:
+ data = data.replace('\\\\', '\\')
# Math:
mathre = re.compile('^(.*?)(\$.*?\$)(.*)')
g = m.group(3)
if s:
# this is non-math!
- s = latex2ert(s)
+ s = latex2ert(s, isindex)
subst = s.split('\n')
retval += subst
retval.append("\\begin_inset Formula " + f)
retval.append("\\end_inset")
m = mathre.match(g)
# Handle whatever is left, which is just text
- g = latex2ert(g)
+ g = latex2ert(g, isindex)
subst = g.split('\n')
retval += subst
return retval
+def lyxline2latex(document, line, inert):
+ 'Convert some LyX stuff into corresponding LaTeX stuff line-wise, as best we can.'
+ if line.startswith("\\begin_inset Formula"):
+ line = line[20:]
+ elif line.startswith("\\begin_inset Quotes"):
+ # For now, we do a very basic reversion. Someone who understands
+ # quotes is welcome to fix it up.
+ qtype = line[20:].strip()
+ # lang = qtype[0]
+ side = qtype[1]
+ dbls = qtype[2]
+ if side == "l":
+ if dbls == "d":
+ line = "``"
+ else:
+ line = "`"
+ else:
+ if dbls == "d":
+ line = "''"
+ else:
+ line = "'"
+ elif line.isspace() or \
+ line.startswith("\\begin_layout") or \
+ line.startswith("\\end_layout") or \
+ line.startswith("\\begin_inset") or \
+ line.startswith("\\end_inset") or \
+ line.startswith("\\lang") or \
+ line.strip() == "status collapsed" or \
+ line.strip() == "status open":
+ #skip all that stuff
+ return ""
+
+ # this needs to be added to the preamble because of cases like
+ # \textmu, \textbackslash, etc.
+ add_to_preamble(document, ['% added by lyx2lyx for converted entries',
+ '\\@ifundefined{textmu}',
+ ' {\\usepackage{textcomp}}{}'])
+ # a lossless reversion is not possible
+ # try at least to handle some common insets and settings
+ if inert:
+ line = line.replace(r'\backslash', '\\')
+ else:
+ line = line.replace('&', '\\&{}')
+ line = line.replace('#', '\\#{}')
+ line = line.replace('^', '\\^{}')
+ line = line.replace('%', '\\%{}')
+ line = line.replace('_', '\\_{}')
+ line = line.replace('$', '\\${}')
+
+ # Do the LyX text --> LaTeX conversion
+ for rep in unicode_reps:
+ line = line.replace(rep[1], rep[0].replace('\\\\', '\\') + "{}")
+ line = line.replace(r'\backslash', r'\textbackslash{}')
+ line = line.replace(r'\series bold', r'\bfseries{}').replace(r'\series default', r'\mdseries{}')
+ line = line.replace(r'\shape italic', r'\itshape{}').replace(r'\shape smallcaps', r'\scshape{}')
+ line = line.replace(r'\shape slanted', r'\slshape{}').replace(r'\shape default', r'\upshape{}')
+ line = line.replace(r'\emph on', r'\em{}').replace(r'\emph default', r'\em{}')
+ line = line.replace(r'\noun on', r'\scshape{}').replace(r'\noun default', r'\upshape{}')
+ line = line.replace(r'\bar under', r'\underbar{').replace(r'\bar default', r'}')
+ line = line.replace(r'\family sans', r'\sffamily{}').replace(r'\family default', r'\normalfont{}')
+ line = line.replace(r'\family typewriter', r'\ttfamily{}').replace(r'\family roman', r'\rmfamily{}')
+ line = line.replace(r'\InsetSpace ', r'').replace(r'\SpecialChar ', r'')
+ return line
+
+
def lyx2latex(document, lines):
'Convert some LyX stuff into corresponding LaTeX stuff, as best we can.'
# clean up multiline stuff
content = ""
ert_end = 0
- reps = read_unicodesymbols()
for curline in range(len(lines)):
- line = lines[curline]
- if line.startswith("\\begin_inset ERT"):
- # We don't want to replace things inside ERT, so figure out
- # where the end of the inset is.
- ert_end = find_end_of_inset(lines, curline + 1)
- continue
- elif line.startswith("\\begin_inset Formula"):
- line = line[20:]
- elif line.startswith("\\begin_inset Quotes"):
- # For now, we do a very basic reversion. Someone who understands
- # quotes is welcome to fix it up.
- qtype = line[20:].strip()
- # lang = qtype[0]
- side = qtype[1]
- dbls = qtype[2]
- if side == "l":
- if dbls == "d":
- line = "``"
- else:
- line = "`"
- else:
- if dbls == "d":
- line = "''"
- else:
- line = "'"
- elif line.isspace() or \
- line.startswith("\\begin_layout") or \
- line.startswith("\\end_layout") or \
- line.startswith("\\begin_inset") or \
- line.startswith("\\end_inset") or \
- line.startswith("\\lang") or \
- line.strip() == "status collapsed" or \
- line.strip() == "status open":
- #skip all that stuff
- continue
-
- # this needs to be added to the preamble because of cases like
- # \textmu, \textbackslash, etc.
- add_to_preamble(document, ['% added by lyx2lyx for converted index entries',
- '\\@ifundefined{textmu}',
- ' {\\usepackage{textcomp}}{}'])
- # a lossless reversion is not possible
- # try at least to handle some common insets and settings
- if ert_end >= curline:
- line = line.replace(r'\backslash', r'\\')
- else:
- line = line.replace('&', '\\&{}')
- line = line.replace('#', '\\#{}')
- line = line.replace('^', '\\^{}')
- line = line.replace('%', '\\%{}')
- line = line.replace('_', '\\_{}')
- line = line.replace('$', '\\${}')
-
- # Do the LyX text --> LaTeX conversion
- for rep in reps:
- line = line.replace(rep[1], rep[0] + "{}")
- line = line.replace(r'\backslash', r'\textbackslash{}')
- line = line.replace(r'\series bold', r'\bfseries{}').replace(r'\series default', r'\mdseries{}')
- line = line.replace(r'\shape italic', r'\itshape{}').replace(r'\shape smallcaps', r'\scshape{}')
- line = line.replace(r'\shape slanted', r'\slshape{}').replace(r'\shape default', r'\upshape{}')
- line = line.replace(r'\emph on', r'\em{}').replace(r'\emph default', r'\em{}')
- line = line.replace(r'\noun on', r'\scshape{}').replace(r'\noun default', r'\upshape{}')
- line = line.replace(r'\bar under', r'\underbar{').replace(r'\bar default', r'}')
- line = line.replace(r'\family sans', r'\sffamily{}').replace(r'\family default', r'\normalfont{}')
- line = line.replace(r'\family typewriter', r'\ttfamily{}').replace(r'\family roman', r'\rmfamily{}')
- line = line.replace(r'\InsetSpace ', r'').replace(r'\SpecialChar ', r'')
- content += line
+ line = lines[curline]
+ if line.startswith("\\begin_inset ERT"):
+ # We don't want to replace things inside ERT, so figure out
+ # where the end of the inset is.
+ ert_end = find_end_of_inset(lines, curline + 1)
+ continue
+ inert = ert_end >= curline
+ content += lyxline2latex(document, lines[curline], inert)
+
return content
j = find_end_of_inset(document.body, i + 1)
if j == -1:
document.warning("Malformed LyX document: Could not find end of tabular.")
+ i += 1
continue
m = i + 1
i = find_token(document.body, "\\begin_inset Tabular", i)
if i == -1:
return
- j = find_end_of_inset(document.body, i + 1)
+ j = find_end_of_inset(document.body, i)
if j == -1:
document.warning("Malformed LyX document: Could not find end of tabular.")
+ i += 1
continue
m = i + 1
document.body[i] = document.body[i].replace('\\begin_inset Flex', '\\begin_inset CharStyle')
-# Discard PDF options for hyperref
def revert_pdf_options(document):
"Revert PDF options for hyperref."
# store the PDF options and delete the entries from the Lyx file
m = r.match(nextline)
if not m:
document.warning("Malformed LyX document: Missing LatexCommand in " + document.body[i] + ".")
+ i += 1
continue
cmdName = m.group(1)
insertion = ["\\begin_inset LatexCommand " + cmdName]
if i == -1:
return
if document.body[i + 1] != "LatexCommand index": # Might also be index_print
- return
- j = find_end_of_inset(document.body, i + 2)
+ i += 1
+ continue
+ j = find_end_of_inset(document.body, i + 1)
if j == -1:
- document.warning("Unable to find end of index inset at line " + i + "!")
+ document.warning("Unable to find end of index inset at line " + str(i) + "!")
i += 2
continue
m = r1.match(document.body[i + 2])
linelist = [""]
else:
fullcontent = m.group(1)
- linelist = latex2lyx(fullcontent)
+ linelist = latex2lyx(fullcontent, True)
#document.warning(fullcontent)
linelist = ["\\begin_inset Index", "status collapsed", "\\begin_layout Standard", ""] + \
arg = ''
for k in range(i, j+1):
if document.body[k].startswith("arg"):
- arg = document.body[k][3:].strip().strip('"')
+ arg = document.body[k][3:].strip()
+ # remove embracing quotation marks
+ if arg[0] == '"':
+ arg = arg[1:]
+ if arg[len(arg) - 1] == '"':
+ arg = arg[:len(arg) - 1]
+ # \" to straight quote
+ arg = arg.replace(r'\"', '"')
+ # \ to \backslash
+ arg = arg.replace(r'\\', "\\backslash\n")
if document.body[k].startswith("type"):
type = document.body[k][4:].strip().strip('"')
# I think there is a newline after \\end_inset, which should be removed.
j = find_token(document.body, "target", i)
if j == -1:
document.warning("Malformed LyX document: Can't find target for url inset")
- i = j
+ i += 1
continue
target = document.body[j][8:-1]
k = find_token(document.body, "\\end_inset", j)
if k == -1:
document.warning("Malformed LyX document: Can't find end of url inset")
- i = k
+ i = j
continue
newstuff = ["\\begin_inset Flex URL",
"status collapsed", "",
"\\end_layout",
""]
document.body[i:k] = newstuff
- i = k
+ i = i + len(newstuff)
def convert_ams_classes(document):
tc = document.textclass
# math macros are nowadays already defined \global, so that an additional
# \global would make the document uncompilable, see
# http://bugzilla.lyx.org/show_bug.cgi?id=5371
+ # We're looking for something like this:
+ # \begin_inset ERT
+ # status collapsed
+ #
+ # \begin_layout Plain Layout
+ #
+ #
+ # \backslash
+ # global
+ # \end_layout
+ #
+ # \end_inset
+ #
+ #
+ # \begin_inset FormulaMacro
+ # \renewcommand{\foo}{123}
+ # \end_inset
i = 0
while True:
i = find_token(document.body, "\\begin_inset FormulaMacro", i)
- if i != -1 and i > 13:
- if document.body[i-6] == "global":
- del document.body[i-13 : i]
- else:
+ if i == -1:
return
- i = i - 12
+ # if i <= 13, then there isn't enough room for the ERT
+ if i <= 12:
+ i += 1
+ continue
+ if document.body[i-6] == "global":
+ del document.body[i-13 : i]
+ i = i - 12
+ else:
+ i += 1
def revert_macro_optional_params(document):
return
newmodlist = []
for mod in modlist:
- if modulemap.has_key(mod):
+ if mod in modulemap:
newmodlist.append(modulemap[mod])
else:
document.warning("Can't find module %s in the module map!" % mod)
def revert_slash(document):
'Revert \\SpecialChar \\slash{} to ERT'
- r = re.compile(r'\\SpecialChar \\slash{}')
i = 0
while i < len(document.body):
- m = r.match(document.body[i])
+ m = re.match(r'(.*)\\SpecialChar \\slash{}(.*)', document.body[i])
if m:
- subst = ['\\begin_inset ERT',
- 'status collapsed', '',
- '\\begin_layout Standard',
- '', '', '\\backslash',
- 'slash{}',
- '\\end_layout', '',
- '\\end_inset', '']
- document.body[i: i+1] = subst
- i = i + len(subst)
+ before = m.group(1)
+ after = m.group(2)
+ subst = [before,
+ '\\begin_inset ERT',
+ 'status collapsed', '',
+ '\\begin_layout Standard',
+ '', '', '\\backslash',
+ 'slash{}',
+ '\\end_layout', '',
+ '\\end_inset', '',
+ after]
+ document.body[i: i+1] = subst
+ i = i + len(subst)
else:
- i = i + 1
+ i = i + 1
def revert_nobreakdash(document):
'Revert \\SpecialChar \\nobreakdash- to ERT'
i = 0
while i < len(document.body):
- line = document.body[i]
- r = re.compile(r'\\SpecialChar \\nobreakdash-')
- m = r.match(line)
+ m = re.match(r'(.*)\\SpecialChar \\nobreakdash-(.*)', document.body[i])
if m:
- subst = ['\\begin_inset ERT',
+ before = m.group(1)
+ after = m.group(2)
+ subst = [before,
+ '\\begin_inset ERT',
'status collapsed', '',
'\\begin_layout Standard', '', '',
'\\backslash',
'nobreakdash-',
'\\end_layout', '',
- '\\end_inset', '']
- document.body[i:i+1] = subst
+ '\\end_inset', '',
+ after]
+ document.body[i: i+1] = subst
i = i + len(subst)
j = find_token(document.header, "\\use_amsmath", 0)
if j == -1:
" Convert subfigures to subfloats. "
i = 0
while 1:
+ addedLines = 0
i = find_token(document.body, '\\begin_inset Graphics', i)
if i == -1:
return
continue
l = find_token(document.body, '\tsubcaptionText', i, endInset)
if l == -1:
- document.warning("Malformed lyx document: Can't find subcaptionText!")
- i = endInset
- continue
- caption = document.body[l][16:].strip('"')
- del document.body[l]
+ caption = ""
+ else:
+ caption = document.body[l][16:].strip('"')
+ del document.body[l]
+ addedLines -= 1
del document.body[k]
- addedLines = -2
+ addedLines -= 1
subst = ['\\begin_inset Float figure', 'wide false', 'sideways false',
'status open', '', '\\begin_layout Plain Layout', '\\begin_inset Caption',
- '', '\\begin_layout Plain Layout'] + latex2lyx(caption) + \
+ '', '\\begin_layout Plain Layout'] + latex2lyx(caption, False) + \
[ '\\end_layout', '', '\\end_inset', '',
'\\end_layout', '', '\\begin_layout Plain Layout']
document.body[i : i] = subst
if k == -1:
break
# is the subfloat aligned?
- al = find_token(document.body, '\\align ', k - 1)
+ al = find_token(document.body, '\\align ', k - 1, j)
alignment_beg = ""
alignment_end = ""
if al != -1:
if l == -1:
document.warning("Malformed lyx document: Missing '\\end_inset' (embedded float).")
i += 1
- j == -1
+ j = -1
continue # escape to the outer loop
m = find_default_layout(document, k + 1, l)
# caption?
continue
elif line in document.body[opt:optend]:
continue
- elif not line.startswith('\\'):
- caption += line.strip()
+ else:
+ inert = True
+ caption += lyxline2latex(document, line, inert)
if len(label) > 0:
- caption += "\\backslash\nlabel{" + label + "}"
- subst = '\\begin_layout Plain Layout\n\\begin_inset ERT\nstatus collapsed\n\n' \
- '\\begin_layout Plain Layout\n\n}' + alignment_end + \
+ caption += "\n\\backslash\nlabel{" + label + "}"
+ subst = '\\begin_layout PlainLayout\n\\begin_inset ERT\nstatus collapsed\n\n' \
+ '\\begin_layout PlainLayout\n\n}' + alignment_end + \
'\n\\end_layout\n\n\\end_inset\n\n' \
- '\\end_layout\n\n\\begin_layout Plain Layout\n'
+ '\\end_layout\n\n\\begin_layout PlainLayout\n'
subst = subst.split('\n')
document.body[l : l+1] = subst
addedLines = len(subst) - 1
del document.body[k+1:m-1]
addedLines -= (m - 1 - (k + 1))
insertion = '\\begin_inset ERT\nstatus collapsed\n\n' \
- '\\begin_layout Plain Layout\n\n' + alignment_beg + '\\backslash\n' \
+ '\\begin_layout PlainLayout\n\n' + alignment_beg + '\n\\backslash\n' \
'subfloat'
if len(shortcap) > 0:
insertion = insertion + "[" + shortcap + "]"
insertion = insertion.split('\n')
document.body[k : k + 1] = insertion
addedLines += len(insertion) - 1
+ al = find_token(document.body, '\\align ', k - 1, j + addedLines)
if al != -1:
del document.body[al]
addedLines -= 1
document.warning("Malformed LyX document: Couldn't find placement parameter of wrap float.")
i += 1
continue
- r = re.compile("placement (o|i|l|r)")
+ r = re.compile("placement (o|i|l|r|O|I|L|R)")
m = r.match(document.body[j])
if m == None:
document.warning("Malformed LyX document: Placement option isn't O|I|R|L!")
- document.body[j] = "placement " + m.group(1).lower()
+ else:
+ document.body[j] = "placement " + m.group(1).lower()
i = j
def revert_plain_layout(document):
- " Convert 'PlainLayout' to 'Plain Layout'"
+ " Revert 'Plain Layout' to 'PlainLayout'"
i = 0
while True:
i = find_token(document.body, '\\begin_layout Plain Layout', i)
def revert_plainlayout(document):
- " Convert 'PlainLayout' to 'Plain Layout'"
+ " Revert 'PlainLayout' to 'Standard'"
i = 0
while True:
i = find_token(document.body, '\\begin_layout PlainLayout', i)
elif k != -1 and j != -1:
document.header[k] = "\\pdf_backref section"
+
+def convert_charstyle_element(document):
+ "Convert CharStyle to Element for docbook backend"
+ if document.backend != "docbook":
+ return
+ i = 0
+ while True:
+ i = find_token(document.body, "\\begin_inset Flex CharStyle:", i)
+ if i == -1:
+ return
+ document.body[i] = document.body[i].replace('\\begin_inset Flex CharStyle:',
+ '\\begin_inset Flex Element:')
+
+def revert_charstyle_element(document):
+ "Convert Element to CharStyle for docbook backend"
+ if document.backend != "docbook":
+ return
+ i = 0
+ while True:
+ i = find_token(document.body, "\\begin_inset Flex Element:", i)
+ if i == -1:
+ return
+ document.body[i] = document.body[i].replace('\\begin_inset Flex Element:',
+ '\\begin_inset Flex CharStyle:')
+
##
# Conversion hub
#
[341, []],
[342, []],
[343, [convert_default_options]],
- [344, [convert_backref_options]]
+ [344, [convert_backref_options]],
+ [345, [convert_charstyle_element]]
]
-revert = [[343, [revert_backref_options]],
+revert = [[344, [revert_charstyle_element]],
+ [343, [revert_backref_options]],
[342, [revert_default_options]],
[341, [revert_mongolian]],
[340, [revert_tabulators, revert_tabsize]],