OSDN Git Service

*colors module: move findColorName to a 'name' property on the RGB class.
authorMichael Curran <mick@kulgan.net>
Thu, 16 Sep 2010 04:26:51 +0000 (14:26 +1000)
committerMichael Curran <mick@kulgan.net>
Thu, 16 Sep 2010 04:26:51 +0000 (14:26 +1000)
*colors.RGB: add a 'fromString' factory method which creates an RGB instance from a CSS color string (such as rgb(x,y,z) or RGBA(x,y,z,w) or #XYZ or #XXYYZZ etc).
*colors.RGB: add a 'fromCOLORREF" factory method which creates an RGB instance from a COLORREF ctypes instance.
*IAccessible2 and soffice code: convert to use the new methods on the colors.RGB class.
*speech.getFormatFieldSpeech: allow color and background-color to be strings, as well as RGB instances.

source/NVDAObjects/IAccessible/__init__.py
source/appModules/soffice.py
source/colors.py
source/speech.py

index c55479f..c14b0d5 100644 (file)
@@ -10,12 +10,11 @@ import os
 from comInterfaces.tom import ITextDocument\r
 import tones\r
 import textInfos.offsets\r
+import colors\r
 import time\r
 import displayModel\r
 import IAccessibleHandler\r
 import oleacc\r
-import re\r
-import colors\r
 import JABHandler\r
 import winUser\r
 import globalVars\r
@@ -138,8 +137,6 @@ class IA2TextTextInfo(textInfos.offsets.OffsetsTextInfo):
                except:\r
                        return ""\r
 \r
-       re_IA2RGB=re.compile(r'rgb\((\d+),\s(\d+),\s(\d+)\)')\r
-\r
        def _getFormatFieldAndOffsets(self,offset,formatConfig,calculateOffsets=True):\r
                try:\r
                        startOffset,endOffset,attribsString=self.obj.IAccessibleTextObject.attributes(offset)\r
@@ -190,14 +187,16 @@ class IA2TextTextInfo(textInfos.offsets.OffsetsTextInfo):
                        formatField["invalid-spelling"]=True\r
                color=formatField.get('color')\r
                if color:\r
-                       m=self.re_IA2RGB.match(color)\r
-                       if m:\r
-                               formatField['color']=colors.RGB(int(m.group(1)),int(m.group(2)),int(m.group(3)))\r
+                       try:\r
+                               formatField['color']=colors.RGB.fromString(color)\r
+                       except ValueError:\r
+                               pass\r
                backgroundColor=formatField.get('background-color')\r
                if backgroundColor:\r
-                       m=self.re_IA2RGB.match(backgroundColor)\r
-                       if m:\r
-                               formatField['background-color']=colors.RGB(int(m.group(1)),int(m.group(2)),int(m.group(3)))\r
+                       try:\r
+                               formatField['background-color']=colors.RGB.fromString(backgroundColor)\r
+                       except ValueError:\r
+                               pass\r
                return formatField,(startOffset,endOffset)\r
 \r
        def _getCharacterOffsets(self,offset):\r
index a5498ac..edbe33d 100755 (executable)
@@ -31,10 +31,6 @@ def gridCoordStringToNumbers(coordString):
                colNum+=((ord(ch.upper())-ord('A')+1)*(26**index))\r
        return rowNum,colNum\r
 \r
-def symphonyColorToRGB(symphonyColor):\r
-       h=symphonyColor[-6:]\r
-       return colors.RGB(int(h[0:2],16),int(h[2:4],16),int(h[4:6],16))\r
-\r
 class JAB_OOTable(JAB):\r
 \r
        def _get_rowCount(self):\r
@@ -139,13 +135,13 @@ class SymphonyTextInfo(IA2TextTextInfo):
                except KeyError:\r
                        color=None\r
                if color:\r
-                       formatField['color']=symphonyColorToRGB(color)\r
+                       formatField['color']=colors.RGB.fromString(color) \r
                try:\r
                        backgroundColor=formatField.pop('CharBackColor')\r
                except KeyError:\r
                        backgroundColor=None\r
                if backgroundColor:\r
-                       formatField['background-color']=symphonyColorToRGB(backgroundColor)\r
+                       formatField['background-color']=colors.RGB.fromString(backgroundColor)\r
 \r
                # optimisation: Assume a hyperlink occupies a full attribute run.\r
                try:\r
index 242af37..b9f7c0f 100644 (file)
@@ -6,8 +6,70 @@
 \r
 from collections import namedtuple\r
 import math\r
+from ctypes.wintypes import COLORREF\r
+import re\r
 \r
-RGB=namedtuple('RGB',('red','green','blue'))\r
+class RGB(namedtuple('RGB',('red','green','blue'))):\r
+       """Represents a color as an RGB (red green blue) value"""\r
+\r
+       @classmethod\r
+       def fromCOLORREF(cls,c):\r
+               """factory method to create an RGB from a COLORREF ctypes instance"""\r
+               if not isinstance(c,COLORREF): raise ValueError("%s is not a COLORREF instance"%c)\r
+               return cls(c.value&0xff,(c.value<<8)&0xff,(c.value<<16)&0xff)\r
+\r
+       _re_RGBFunctionString=re.compile(r'rgb\(\s*(\d+%?)\s*,\s*(\d+%?)\s*,\s*(\d+%?)\s*\)',re.I)\r
+       _re_RGBAFunctionString=re.compile(r'rgba\(\s*(\d+%?)\s*,\s*(\d+%?)\s*,\s*(\d+%?)\s*,\s*\d+(\.\d+)?\s*\)',re.I)\r
+\r
+       @staticmethod\r
+       def _RGBStringValToInt(s):\r
+               val=int(round(int(s[:-1])*2.55)) if s.endswith('%') else int(s)\r
+               if val<0 or val>255:\r
+                       raise ValueError("%s out of range"%val)\r
+               return val\r
+\r
+       @classmethod\r
+       def fromString(cls,s):\r
+               """\r
+               Factory method to create an RGB instance from a css RGB string representation.\r
+               """\r
+               s=s.strip()\r
+               #Try to match on the form RGB(x,y,z)\r
+               m=cls._re_RGBFunctionString.match(s) or cls._re_RGBAFunctionString.match(s)\r
+               if m:\r
+                       r=cls._RGBStringValToInt(m.group(1))\r
+                       g=cls._RGBStringValToInt(m.group(2))\r
+                       b=cls._RGBStringValToInt(m.group(3))\r
+                       return RGB(r,g,b)\r
+               if s.startswith('#'):\r
+                       sLen=len(s)\r
+                       try:\r
+                               val=int(s[1:],16)\r
+                       except ValueError:\r
+                               val=None\r
+                       if val is not None:\r
+                               #Perhaps its a #aarrggbb or #rrggbb hex string\r
+                               if sLen==7 or sLen==9:\r
+                                       r=(val>>16)&0xff\r
+                                       g=(val>>8)&0xff\r
+                                       b=val&0xff\r
+                                       return RGB(r,g,b)\r
+                               #Perhaps its a #argb or #rgb hex string\r
+                               if sLen==4 or sLen==5:\r
+                                       r=((val>>8)&0xf)+(((val>>8)&0xf)<<4)\r
+                                       g=((val>>4)&0xf)+(((val>>4)&0xf)<<4)\r
+                                       b=(val&0xf)+((val&0xf)<<4)\r
+                                       return RGB(r,g,b)\r
+               raise ValueError("invalid RGB string: %s"%s)\r
+\r
+       @property\r
+       def name(self):\r
+               foundName=RGBToNames.get(self,None)\r
+               if not foundName:\r
+                       closestRGB=sorted(RGBToNames.iterkeys(),key=lambda x: math.sqrt((abs(self.red-x.red)*0.3)**2+(abs(self.green-x.green)*0.59)**2+(abs(self.blue-x.blue)*0.11)**2))[0]\r
+                       foundName=RGBToNames[closestRGB]\r
+                       RGBToNames[self]=foundName\r
+               return foundName\r
 \r
 RGBToNames={\r
        #Standard 16 HTML 4 colors\r
@@ -31,12 +93,3 @@ RGBToNames={
        RGB(0xff,0xa5,0x00):_('orange'),\r
 }\r
 \r
-def findColorName(rgb):\r
-       foundName=RGBToNames.get(rgb,None)\r
-       if not foundName:\r
-               closestRGB=sorted(RGBToNames.iterkeys(),key=lambda x: math.sqrt((abs(rgb.red-x.red)*0.3)**2+(abs(rgb.green-x.green)*0.59)**2+(abs(rgb.blue-x.blue)*0.11)**2))[0]\r
-               foundName=RGBToNames[closestRGB]\r
-               RGBToNames[rgb]=foundName\r
-       return foundName\r
-\r
-\r
index 85a5702..e386145 100755 (executable)
@@ -894,11 +894,11 @@ def getFormatFieldSpeech(attrs,attrsCache=None,formatConfig=None,unit=None,extra
                color=attrs.get("color")\r
                oldColor=attrsCache.get("color") if attrsCache is not None else None\r
                if color and color!=oldColor:\r
-                       textList.append(colors.findColorName(color))\r
+                       textList.append(color.name if isinstance(color,colors.RGB) else unicode(color))\r
                backgroundColor=attrs.get("background-color")\r
                oldBackgroundColor=attrsCache.get("background-color") if attrsCache is not None else None\r
                if backgroundColor and backgroundColor!=oldBackgroundColor:\r
-                       textList.append(_("on {backgroundColor}").format(backgroundColor=colors.findColorName(backgroundColor)))\r
+                       textList.append(_("on {backgroundColor}").format(backgroundColor=backgroundColor.name if isinstance(backgroundColor,colors.RGB) else unicode(backgroundColor)))\r
        if  formatConfig["reportLineNumber"]:\r
                lineNumber=attrs.get("line-number")\r
                oldLineNumber=attrsCache.get("line-number") if attrsCache is not None else None\r