# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
'''
-This module offers several free functions to help with lyx2lyx'ing.
-More documentaton is below, but here is a quick guide to what
+This module offers several free functions to help with lyx2lyx'ing.
+More documentaton is below, but here is a quick guide to what
they do. Optional arguments are marked by brackets.
add_to_preamble(document, text):
default index is 0, so the material is inserted at the beginning.
Prepends a comment "% Added by lyx2lyx" to text.
-put_cmd_in_ert(arg):
- Here arg should be a list of strings (lines), which we want to
+put_cmd_in_ert(cmd):
+ Here cmd should be a list of strings (lines), which we want to
wrap in ERT. Returns a list of strings so wrapped.
A call to this routine will often go something like this:
i = find_token('\\begin_inset FunkyInset', ...)
ert = put_cmd_in_ert(content)
document.body[i:j+1] = ert
+get_ert(lines, i[, verbatim]):
+ Here, lines is a list of lines of LyX material containing an ERT inset,
+ whose content we want to convert to LaTeX. The ERT starts at index i.
+ If the optional (by default: False) bool verbatim is True, the content
+ of the ERT is returned verbatim, that is in LyX syntax (not LaTeX syntax)
+ for the use in verbatim insets.
+
lyx2latex(document, lines):
Here, lines is a list of lines of LyX material we want to convert
to LaTeX. We do the best we can and return a string containing
can and return a string containing the translated material.
latex_length(slen):
- Convert lengths (in LyX form) to their LaTeX representation. Returns
- (bool, length), where the bool tells us if it was a percentage, and
- the length is the LaTeX representation.
+ Convert lengths (in LyX form) to their LaTeX representation. Returns
+ (bool, length), where the bool tells us if it was a percentage, and
+ the length is the LaTeX representation.
+
+convert_info_insets(document, type, func):
+ Applies func to the argument of all info insets matching certain types
+ type : the type to match. This can be a regular expression.
+ func : function from string to string to apply to the "arg" field of
+ the info insets.
+
+is_document_option(document, option):
+ Find if _option_ is a document option (\\options in the header).
+insert_document_option(document, option):
+ Insert _option_ as a document option.
+
+remove_document_option(document, option):
+ Remove _option_ as a document option.
'''
import re
-import string
from parser_tools import find_token, find_end_of_inset
from unicode_symbols import unicode_reps
-
# This will accept either a list of lines or a single line.
# It is bad practice to pass something with embedded newlines,
# though we will handle that.
# It should really be a list.
def insert_to_preamble(document, text, index = 0):
""" Insert text to the preamble at a given line"""
-
+
if not type(text) is list:
# split on \n just in case
# it'll give us the one element list we want
# if there's no \n, too
text = text.split('\n')
-
+
text.insert(0, "% Added by lyx2lyx")
document.preamble[index:index] = text
-def put_cmd_in_ert(arg):
- '''
- arg should be a list of lines we want to wrap in ERT.
- Returns a list of strings, with the lines so wrapped.
- '''
-
+# A dictionary of Unicode->LICR mappings for use in a Unicode string's translate() method
+# Created from the reversed list to keep the first of alternative definitions.
+licr_table = dict((ord(ch), cmd) for cmd, ch in unicode_reps[::-1])
+
+def put_cmd_in_ert(cmd):
+ """
+ Return ERT inset wrapping `cmd` as a list of strings.
+
+ `cmd` can be a string or list of lines. Non-ASCII characters are converted
+ to the respective LICR macros if defined in unicodesymbols.
+ """
ret = ["\\begin_inset ERT", "status collapsed", "", "\\begin_layout Plain Layout", ""]
- # It will be faster for us to work with a single string internally.
- # That way, we only go through the unicode_reps loop once.
- if type(arg) is list:
- s = "\n".join(arg)
+ # It will be faster to work with a single string internally.
+ if isinstance(cmd, list):
+ cmd = u"\n".join(cmd)
else:
- s = arg
- for rep in unicode_reps:
- s = s.replace(rep[1], rep[0])
- s = s.replace('\\', "\\backslash\n")
- ret += s.splitlines()
+ cmd = u"%s" % cmd # ensure it is an unicode instance
+ cmd = cmd.translate(licr_table)
+ cmd = cmd.replace("\\", "\n\\backslash\n")
+ ret += cmd.splitlines()
ret += ["\\end_layout", "", "\\end_inset"]
return ret
elif lines[i] == "\\end_layout":
while i + 1 < j and lines[i+1] == "":
i = i + 1
- elif lines[i] == "\\backslash" and not verbatim:
- ret = ret + "\\"
+ elif lines[i] == "\\backslash":
+ if verbatim:
+ ret = ret + "\n" + lines[i] + "\n"
+ else:
+ ret = ret + "\\"
else:
ret = ret + lines[i]
i = i + 1
def latex_length(slen):
- '''
+ '''
Convert lengths to their LaTeX representation. Returns (bool, length),
where the bool tells us if it was a percentage, and the length is the
LaTeX representation.
# the + always precedes the -
# Convert relative lengths to LaTeX units
- units = {"text%":"\\textwidth", "col%":"\\columnwidth",
- "page%":"\\paperwidth", "line%":"\\linewidth",
- "theight%":"\\textheight", "pheight%":"\\paperheight"}
+ units = {"col%": "\\columnwidth",
+ "text%": "\\textwidth",
+ "page%": "\\paperwidth",
+ "line%": "\\linewidth",
+ "theight%": "\\textheight",
+ "pheight%": "\\paperheight",
+ "baselineskip%": "\\baselineskip"
+ }
for unit in list(units.keys()):
i = slen.find(unit)
if i == -1:
"'true' goes to True, case-insensitively, and we strip whitespace."
s = s.strip().lower()
return s == "true"
+
+
+def convert_info_insets(document, type, func):
+ "Convert info insets matching type using func."
+ i = 0
+ type_re = re.compile(r'^type\s+"(%s)"$' % type)
+ arg_re = re.compile(r'^arg\s+"(.*)"$')
+ while True:
+ i = find_token(document.body, "\\begin_inset Info", i)
+ if i == -1:
+ return
+ t = type_re.match(document.body[i + 1])
+ if t:
+ arg = arg_re.match(document.body[i + 2])
+ if arg:
+ new_arg = func(arg.group(1))
+ document.body[i + 2] = 'arg "%s"' % new_arg
+ i += 3
+
+
+def insert_document_option(document, option):
+ "Insert _option_ as a document option."
+
+ # Find \options in the header
+ options_line = find_token(document.header, "\\options", 0)
+
+ # if the options does not exists add it after the textclass
+ if options_line == -1:
+ textclass_line = find_token(document.header, "\\textclass", 0)
+ document.header.insert(textclass_line +1,
+ r"\options %s" % option)
+ return
+
+ # add it to the end of the options
+ document.header[options_line] += " ,%s" % option
+
+
+def remove_document_option(document, option):
+ """ Remove _option_ as a document option.
+
+ It is assumed that option belongs to the \options.
+ That can be done running is_document_option(document, option)."""
+
+ options_line = find_token(document.header, "\\options", 0)
+ option_pos = document.header[options_line].find(option)
+
+ # Remove option from \options
+ comma_before_pos = document.header[options_line].rfind(',', 0, option_pos)
+ comma_after_pos = document.header[options_line].find(',', option_pos)
+
+ # if there are no commas then it is the single option
+ # and the options line should be removed since it will be empty
+ if comma_before_pos == comma_after_pos == -1:
+ del document.header[options_line]
+ return
+
+ # last option
+ options = document.header[options_line]
+ if comma_after_pos == -1:
+ document.header[options_line] = options[:comma_before_pos].rsplit()
+ return
+
+ document.header[options_line] = options[comma_before_pos: comma_after_pos]
+
+
+def is_document_option(document, option):
+ "Find if _option_ is a document option"
+
+ # Find \options in the header
+ options_line = find_token(document.header, "\\options", 0)
+
+ # \options is not present in the header
+ if options_line == -1:
+ return False
+
+ option_pos = document.header[options_line].find(option)
+ # option is not present in the \options
+ if option_pos == -1:
+ return False
+
+ return True