]> git.lyx.org Git - lyx.git/blobdiff - lib/lyx2lyx/lyx_2_2.py
New lyx2lyx tools.
[lyx.git] / lib / lyx2lyx / lyx_2_2.py
index c1fe09857122789cf69538715e796b366ddd8cd8..1c7f3dc37ff1ccd401cf0b3a3ca1777353530243 100644 (file)
@@ -1,6 +1,5 @@
 # -*- coding: utf-8 -*-
 # This file is part of lyx2lyx
-# -*- coding: utf-8 -*-
 # Copyright (C) 2015 The LyX team
 #
 # This program is free software; you can redistribute it and/or
@@ -30,20 +29,21 @@ import sys, os
 #  find_token_backwards, is_in_inset, get_value, get_quoted_value, \
 #  del_token, check_token, get_option_value
 
-from lyx2lyx_tools import add_to_preamble, put_cmd_in_ert, get_ert, lyx2latex, \
-  lyx2verbatim, length_in_bp, convert_info_insets
-#  insert_to_preamble, latex_length, revert_flex_inset, \
-#  revert_font_attrs, hex2ratio, str2bool
+from lyx2lyx_tools import (add_to_preamble, put_cmd_in_ert, get_ert,
+    lyx2latex, lyx2verbatim, length_in_bp, convert_info_insets)
+#   insert_to_preamble, latex_length, revert_flex_inset,
+#   revert_font_attrs, hex2ratio, str2bool
 
-from parser_tools import find_token, find_token_backwards, find_re, \
-     find_end_of_inset, find_end_of_layout, find_nonempty_line, \
-     get_containing_layout, get_value, check_token
+from parser_tools import (del_complete_lines,
+    find_end_of_inset, find_end_of_layout, find_nonempty_line, find_re,
+    find_token, find_token_backwards, get_containing_layout,
+    get_value, check_token)
 
 ####################################################################
 # Private helper functions
 
 def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment, opt, nolastopt):
-    '''
+    """
     Reverts an InsetArgument to TeX-code
     usage:
     revert_Argument_to_TeX_brace(document, LineOfBegin, LineOfEnd, StartArgument, EndArgument, isEnvironment, isOpt, notLastOpt)
@@ -54,7 +54,7 @@ def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment,
     isEnvironment must be true, if the layout is for a LaTeX environment
     isOpt must be true, if the argument is an optional one
     notLastOpt must be true if the argument is mandatory and followed by optional ones
-    '''
+    """
     lineArg = 0
     wasOpt = False
     while lineArg != -1 and n < nmax + 1:
@@ -78,7 +78,7 @@ def revert_Argument_to_TeX_brace(document, line, endline, n, nmax, environment,
             if nolastopt == False:
               document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}{")
             else:
-              document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}") 
+              document.body[endInset - 2 : endInset + 1] = put_cmd_in_ert("}")
             del(document.body[lineArg : beginPlain + 1])
             wasOpt = False
           else:
@@ -616,77 +616,105 @@ def convert_dashes(document):
     if document.backend != "latex":
         return
 
+    lines = document.body
     i = 0
-    while i < len(document.body):
-        words = document.body[i].split()
-        if len(words) > 1 and words[0] == "\\begin_inset" and \
-           words[1] in ["CommandInset", "ERT", "External", "Formula", "FormulaMacro", "Graphics", "IPA", "listings"]:
+    while i+1 < len(lines):
+        i += 1
+        line = lines[i]
+        words = line.split()
+        if (len(words) > 1 and words[0] == "\\begin_inset"
+            and (words[1] in ["CommandInset", "ERT", "External", "Formula",
+                              "FormulaMacro", "Graphics", "IPA", "listings"]
+                 or line.endswith("Flex Code"))):
             # must not replace anything in insets that store LaTeX contents in .lyx files
-            # (math and command insets withut overridden read() and write() methods
+            # (math and command insets without overridden read() and write() methods
             # filtering out IPA makes Text::readParToken() more simple
             # skip ERT as well since it is not needed there
-            j = find_end_of_inset(document.body, i)
+            # Flex Code is logical markup, typically rendered as typewriter
+            j = find_end_of_inset(lines, i)
             if j == -1:
-                document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
-                i += 1
+                document.warning("Malformed LyX document: Can't find end of " +
+                                 words[1] + " inset at line " + str(i))
             else:
                 i = j
             continue
-        if len(words) > 0 and words[0] in ["\\leftindent", "\\paragraph_spacing", "\\align", "\\labelwidthstring"]:
-            # skip paragraph parameters (bug 10243)
-            i += 1
-            continue
-        while True:
-            j = document.body[i].find("--")
+        if lines[i] == "\\begin_layout LyX-Code":
+            j = find_end_of_layout(lines, i)
             if j == -1:
-                break
-            front = document.body[i][:j]
-            back = document.body[i][j+2:]
-            # We can have an arbitrary number of consecutive hyphens.
-            # These must be split into the corresponding number of two and three hyphens
-            # We must match what LaTeX does: First try emdash, then endash, then single hyphen
-            if back.find("-") == 0:
-                back = back[1:]
-                if len(back) > 0:
-                    document.body.insert(i+1, back)
-                document.body[i] = front + "\\threehyphens"
+                document.warning("Malformed LyX document: "
+                    "Can't find end of %s layout at line %d" % (words[1],i))
             else:
-                if len(back) > 0:
-                    document.body.insert(i+1, back)
-                document.body[i] = front + "\\twohyphens"
-        i += 1
+                i = j
+            continue
+        if line.startswith("\\labelwidthstring"):
+            # skip label width string (bug 10243)
+            continue
 
+        if "--" in line:
+            # We can have an arbitrary number of consecutive hyphens.
+            # Replace as LaTeX does: First try emdash, then endash
+            line = line.replace("---", "\\threehyphens\n")
+            line = line.replace("--", "\\twohyphens\n")
+            lines[i:i+1] = line.splitlines()
+
+    # remove ligature breaks between dashes
+    i = 1
+    while i < len(lines):
+        line = lines[i]
+        if (line.endswith(r"-\SpecialChar \textcompwordmark{}") and
+            lines[i+1].startswith("-")):
+            lines[i] = line.replace(r"\SpecialChar \textcompwordmark{}",
+                                    lines.pop(i+1))
+        else:
+            i += 1
 
-def revert_dashes(document):
-    "convert \\twohyphens and \\threehyphens to -- and ---"
 
+def revert_dashes(document):
+    """
+    Prevent ligatures of existing --- and --.
+    Revert \\twohyphens and \\threehyphens to -- and ---.
+    Remove preamble code from 2.3->2.2 conversion.
+    """
+    del_complete_lines(document.preamble,
+                       ['% Added by lyx2lyx',
+                        r'\renewcommand{\textendash}{--}',
+                        r'\renewcommand{\textemdash}{---}'])
+    # Insert ligature breaks to prevent ligation of hyphens to dashes:
+    lines = document.body
     i = 0
-    while i < len(document.body):
-        words = document.body[i].split()
-        if len(words) > 1 and words[0] == "\\begin_inset" and \
-           words[1] in ["CommandInset", "ERT", "External", "Formula", "Graphics", "IPA", "listings"]:
-            # see convert_dashes
-            j = find_end_of_inset(document.body, i)
-            if j == -1:
-                document.warning("Malformed LyX document: Can't find end of " + words[1] + " inset at line " + str(i))
-                i += 1
-            else:
-                i = j
+    while i+1 < len(lines):
+        i += 1
+        line = lines[i]
+        # skip label width string (bug 10243):
+        if line.startswith("\\labelwidthstring"):
             continue
-        replaced = False
-        if document.body[i].find("\\twohyphens") >= 0:
-            document.body[i] = document.body[i].replace("\\twohyphens", "--")
-            replaced = True
-        if document.body[i].find("\\threehyphens") >= 0:
-            document.body[i] = document.body[i].replace("\\threehyphens", "---")
-            replaced = True
-        if replaced and i+1 < len(document.body) and \
-           (document.body[i+1].find("\\") != 0 or \
-            document.body[i+1].find("\\twohyphens") == 0 or
-            document.body[i+1].find("\\threehyphens") == 0) and \
-           len(document.body[i]) + len(document.body[i+1]) <= 80:
-            document.body[i] = document.body[i] + document.body[i+1]
-            document.body[i+1:i+2] = []
+        # do not touch hyphens in some insets (cf. convert_dashes):
+        if line.startswith("\\begin_inset"):
+            try:
+                if line.split()[1] in ["CommandInset", "ERT", "External",
+                                       "Formula", "FormulaMacro", "Graphics",
+                                       "IPA", "listings"]:
+                    j = find_end_of_inset(lines, i)
+                    if j == -1:
+                        document.warning("Malformed LyX document: Can't find "
+                                    "end of %s inset at line %d." % (itype, i))
+                        continue
+                    i = j
+            except IndexError:
+                continue
+        if "--" in line:
+            line = line.replace("--", "-\\SpecialChar \\textcompwordmark{}\n-")
+            document.body[i:i+1] = line.split('\n')
+    # Revert \twohyphens and \threehyphens:
+    i = 1
+    while i < len(lines):
+        line = lines[i]
+        if not line.endswith("hyphens"):
+            i +=1
+        elif line.endswith("\\twohyphens") or line.endswith("\\threehyphens"):
+            line = line.replace("\\twohyphens", "--")
+            line = line.replace("\\threehyphens", "---")
+            lines[i] = line + lines.pop(i+1)
         else:
             i += 1
 
@@ -827,16 +855,16 @@ def revert_georgian(document):
         document.language = "english"
         i = find_token(document.header, "\\language georgian", 0)
         if i != -1:
-           document.header[i] = "\\language english"
+            document.header[i] = "\\language english"
         j = find_token(document.header, "\\language_package default", 0)
         if j != -1:
-           document.header[j] = "\\language_package babel"
+            document.header[j] = "\\language_package babel"
         k = find_token(document.header, "\\options", 0)
         if k != -1:
-           document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
+            document.header[k] = document.header[k].replace("\\options", "\\options georgian,")
         else:
-           l = find_token(document.header, "\\use_default_options", 0)
-           document.header.insert(l + 1, "\\options georgian")
+            l = find_token(document.header, "\\use_default_options", 0)
+            document.header.insert(l + 1, "\\options georgian")
 
 
 def revert_sigplan_doi(document):
@@ -1089,43 +1117,59 @@ def revert_BoxFeatures(document):
     defaultThick = "0.4pt"
     defaultShadow = "4pt"
     while True:
-        i = find_token(document.body, "height_special", i)
+        i = find_token(document.body, "thickness", i)
         if i == -1:
             return
+        binset = find_token(document.body, "\\begin_inset Box", i - 11)
+        if binset == -1 or binset != i - 11:
+            i = i + 1
+            continue # then "thickness" is is just a word in the text
+        einset = find_end_of_inset(document.body, binset)
+        if einset == -1:
+            document.warning("Malformed LyX document: Can't find end of box inset!")
+            i = i + 1
+            continue
         # read out the values
+        beg = document.body[i].find('"');
+        end = document.body[i].rfind('"');
+        thickness = document.body[i][beg+1:end];
         beg = document.body[i+1].find('"');
         end = document.body[i+1].rfind('"');
-        thickness = document.body[i+1][beg+1:end];
+        separation = document.body[i+1][beg+1:end];
         beg = document.body[i+2].find('"');
         end = document.body[i+2].rfind('"');
-        separation = document.body[i+2][beg+1:end];
-        beg = document.body[i+3].find('"');
-        end = document.body[i+3].rfind('"');
-        shadowsize = document.body[i+3][beg+1:end];
+        shadowsize = document.body[i+2][beg+1:end];
         # delete the specification
-        del document.body[i+1:i+4]
+        del document.body[i:i+3]
         # output ERT
         # first output the closing brace
         if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
-            document.body[i + 10 : i + 10] = put_cmd_in_ert("}")
+            document.body[einset -1 : einset - 1] = put_cmd_in_ert("}")
+        # we have now the problem that if there is already \(f)colorbox in ERT around the inset
+        # the ERT from this routine must be around it
+        regexp = re.compile(r'^.*colorbox{.*$')
+        pos = find_re(document.body, regexp, binset - 4)
+        if pos != -1 and pos == binset - 4:
+            pos = i - 11 - 10
+        else:
+            pos = i - 11
         # now output the lengths
         if shadowsize != defaultShadow or separation != defaultSep or thickness != defaultThick:
-            document.body[i - 10 : i - 10] = put_cmd_in_ert("{")
+            document.body[pos : pos] = put_cmd_in_ert("{")
         if thickness != defaultThick:
-            document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness]
+            document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness]
         if separation != defaultSep and thickness == defaultThick:
-            document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation]
+            document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation]
         if separation != defaultSep and thickness != defaultThick:
-            document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
+            document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation]
         if shadowsize != defaultShadow and separation == defaultSep and thickness == defaultThick:
-            document.body[i - 5 : i - 4] = ["{\\backslash shadowsize " + shadowsize]
+            document.body[pos + 5 : pos +6] = ["{\\backslash shadowsize " + shadowsize]
         if shadowsize != defaultShadow and separation != defaultSep and thickness == defaultThick:
-            document.body[i - 5 : i - 4] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
+            document.body[pos + 5 : pos +6] = ["{\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
         if shadowsize != defaultShadow and separation == defaultSep and thickness != defaultThick:
-            document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
+            document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash shadowsize " + shadowsize]
         if shadowsize != defaultShadow and separation != defaultSep and thickness != defaultThick:
-            document.body[i - 5 : i - 4] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
-        i = i + 11
+            document.body[pos + 5 : pos +6] = ["{\\backslash fboxrule " + thickness + "\\backslash fboxsep " + separation + "\\backslash shadowsize " + shadowsize]
 
 
 def convert_origin(document):
@@ -1196,7 +1240,7 @@ def revert_textcolor(document):
                     j = find_token(document.body, "\\color", i + 1)
                     k = find_token(document.body, "\\end_layout", i + 1)
                     if j == -1 and k != -1:
-                        j = k +1 
+                        j = k +1
                     # output TeX code
                     # first output the closing brace
                     if k < j:
@@ -1223,66 +1267,53 @@ def convert_colorbox(document):
 def revert_colorbox(document):
     " outputs color settings for boxes as TeX code "
 
-    binset = 0
+    i = 0
     defaultframecolor = "black"
     defaultbackcolor = "none"
     while True:
-        binset = find_token(document.body, "\\begin_inset Box", binset)
+        i = find_token(document.body, "framecolor", i)
+        if i == -1:
+            return
+        binset = find_token(document.body, "\\begin_inset Box", i - 14)
         if binset == -1:
             return
-
         einset = find_end_of_inset(document.body, binset)
         if einset == -1:
             document.warning("Malformed LyX document: Can't find end of box inset!")
-            binset += 1
             continue
-
-        blay = find_token(document.body, "\\begin_layout", binset, einset)
-        if blay == -1:
-            document.warning("Malformed LyX document: Can't find start of layout!")
-            binset = einset
-            continue
-
-        # doing it this way, we make sure only to find a framecolor option
-        frame = find_token(document.body, "framecolor", binset, blay)
-        if frame == -1:
-            binset = einset
-            continue
-
-        beg = document.body[frame].find('"')
-        end = document.body[frame].rfind('"')
-        framecolor = document.body[frame][beg + 1 : end]
-
-        # this should be on the next line
-        bgcolor = frame + 1
-        beg = document.body[bgcolor].find('"')
-        end = document.body[bgcolor].rfind('"')
-        backcolor = document.body[bgcolor][beg + 1 : end]
-
-        # delete those bits
-        del document.body[frame : frame + 2]
-        # adjust end of inset
-        einset -= 2
-
-        if document.body[binset] == "\\begin_inset Box Boxed" and \
-            framecolor != defaultframecolor:
-          document.body[binset] = "\\begin_inset Box Frameless"
-
-        # output TeX code
+        # read out the values
+        beg = document.body[i].find('"');
+        end = document.body[i].rfind('"');
+        framecolor = document.body[i][beg+1:end];
+        beg = document.body[i + 1].find('"');
+        end = document.body[i + 1].rfind('"');
+        backcolor = document.body[i+1][beg+1:end];
+        # delete the specification
+        del document.body[i:i + 2]
+        # output ERT
         # first output the closing brace
-        if framecolor == defaultframecolor and backcolor == defaultbackcolor:
-            # nothing needed
-            pass
-        else:
-            # we also neeed to load xcolor in the preamble but only once
+        if framecolor != defaultframecolor or backcolor != defaultbackcolor:
             add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}"])
-            document.body[einset + 1 : einset + 1] = put_cmd_in_ert("}")
-            if framecolor != defaultframecolor:
-                document.body[binset:binset] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
-            else:
-              document.body[binset:binset] = put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
-
-        binset = einset
+            document.body[einset : einset] = put_cmd_in_ert("}")
+        # determine the box type
+        isBox = find_token(document.body, "\\begin_inset Box Boxed", binset)
+        # now output the box commands
+        if (framecolor != defaultframecolor and isBox == binset) or (backcolor != defaultbackcolor and isBox == binset):
+            document.body[i - 14 : i - 14] = put_cmd_in_ert("\\fcolorbox{" + framecolor + "}{" + backcolor + "}{")
+            # in the case we must also change the box type because the ERT code adds a frame
+            document.body[i - 4] = "\\begin_inset Box Frameless"
+            # if has_inner_box 0 we must set it and use_makebox to 1
+            ibox = find_token(document.body, "has_inner_box", i - 4)
+            if ibox == -1 or ibox != i - 1:
+                document.warning("Malformed LyX document: Can't find has_inner_box statement!")
+                continue
+            # read out the value
+            innerbox = document.body[ibox][-1:];
+            if innerbox == "0":
+                document.body[ibox] = "has_inner_box 1"
+                document.body[ibox + 3] = "use_makebox 1"
+        if backcolor != defaultbackcolor and isBox != binset:
+            document.body[i - 14 : i - 14] =  put_cmd_in_ert("\\colorbox{" + backcolor + "}{")
 
 
 def revert_mathmulticol(document):
@@ -1697,7 +1728,7 @@ def revert_tcolorbox_1(document):
     i = find_token(document.header, "tcolorbox", i)
     if i == -1:
       break
-    else:    
+    else:
       flex = 0
       flexEnd = -1
       flex = find_token(document.body, "\\begin_inset Flex Subtitle", flex)
@@ -1722,7 +1753,7 @@ def revert_tcolorbox_2(document):
     i = find_token(document.header, "tcolorbox", i)
     if i == -1:
       break
-    else:    
+    else:
       flex = 0
       flexEnd = -1
       flex = find_token(document.body, "\\begin_inset Flex Raster Color Box", flex)
@@ -1743,7 +1774,7 @@ def revert_tcolorbox_3(document):
     i = find_token(document.header, "tcolorbox", i)
     if i == -1:
       break
-    else:    
+    else:
       flex = 0
       flexEnd = -1
       flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 1", flex)
@@ -1765,7 +1796,7 @@ def revert_tcolorbox_4(document):
     i = find_token(document.header, "tcolorbox", i)
     if i == -1:
       break
-    else:    
+    else:
       flex = 0
       flexEnd = -1
       flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 2", flex)
@@ -1787,7 +1818,7 @@ def revert_tcolorbox_5(document):
     i = find_token(document.header, "tcolorbox", i)
     if i == -1:
       break
-    else:    
+    else:
       flex = 0
       flexEnd = -1
       flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 3", flex)
@@ -1809,7 +1840,7 @@ def revert_tcolorbox_6(document):
     i = find_token(document.header, "tcolorbox", i)
     if i == -1:
       break
-    else:    
+    else:
       flex = 0
       flexEnd = -1
       flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 4", flex)
@@ -1831,7 +1862,7 @@ def revert_tcolorbox_7(document):
     i = find_token(document.header, "tcolorbox", i)
     if i == -1:
       break
-    else:    
+    else:
       flex = 0
       flexEnd = -1
       flex = find_token(document.body, "\\begin_inset Flex Custom Color Box 5", flex)
@@ -1879,7 +1910,7 @@ def revert_tcolorbox_8(document):
 
 def revert_moderncv_1(document):
   " Reverts the new inset of moderncv to TeX-code in preamble "
-  
+
   if document.textclass != "moderncv":
     return
   i = 0
@@ -1947,7 +1978,7 @@ def revert_moderncv_1(document):
 
 def revert_moderncv_2(document):
   " Reverts the phone inset of moderncv to the obsoleted mobile or fax "
-  
+
   if document.textclass != "moderncv":
     return
   i = 0
@@ -2089,7 +2120,7 @@ def convert_moderncv_name(document):
 
 def revert_achemso(document):
   " Reverts the flex inset Latin to TeX code "
-  
+
   if document.textclass != "achemso":
     return
   i = 0