]> git.lyx.org Git - lyx.git/blobdiff - lib/lyx2lyx/lyx2lyx_tools.py
Fix bug #7404.
[lyx.git] / lib / lyx2lyx / lyx2lyx_tools.py
index 8f3e8a3960e96029def440d3ae02636c95ad63bb..cb1996ecb58af99018351c6bdd20fb98bacfb7d5 100644 (file)
@@ -17,8 +17,8 @@
 # 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):
@@ -37,8 +37,8 @@ insert_to_preamble(document, text[, index]):
   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', ...)
@@ -47,24 +47,48 @@ put_cmd_in_ert(arg):
     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 
+  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
   the translated material.
 
+lyx2verbatim(document, lines):
+  Here, lines is a list of lines of LyX material we want to convert
+  to verbatim material (used in ERT an the like). We do the best we
+  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.
@@ -101,39 +125,42 @@ def add_to_preamble(document, text):
 # 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
 
 
-def get_ert(lines, i):
+def get_ert(lines, i, verbatim = False):
     'Convert an ERT inset into LaTeX.'
     if not lines[i].startswith("\\begin_inset ERT"):
         return ""
@@ -157,7 +184,10 @@ def get_ert(lines, i):
             while i + 1 < j and lines[i+1] == "":
                 i = i + 1
         elif lines[i] == "\\backslash":
-            ret = ret + "\\"
+            if verbatim:
+                ret = ret + "\n" + lines[i] + "\n"
+            else:
+                ret = ret + "\\"
         else:
             ret = ret + lines[i]
         i = i + 1
@@ -270,8 +300,17 @@ def lyx2latex(document, lines):
     return content
 
 
+def lyx2verbatim(document, lines):
+    'Convert some LyX stuff into corresponding verbatim stuff, as best we can.'
+
+    content = lyx2latex(document, lines)
+    content = re.sub(r'\\(?!backslash)', r'\n\\backslash\n', content)
+
+    return content
+
+
 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.
@@ -285,9 +324,14 @@ def latex_length(slen):
     # 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:
@@ -479,3 +523,84 @@ def str2bool(s):
   "'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