Japanese Windows win32 API / _winreg / font_manager.py bug workaround

Hi,

A few months back I gave a little report to matplotlib-users on using matplotlib on Japanese Windows. There were several issues that I managed to find workarounds for.

I wanted to give an update on the font_manager.py bug. This bug is still in matplotlib (as of CVS earlier today) and causes a matplotlib import to throw a WindowsError exception (ErrNo 234). This has happened to me on every Windows 2000 Japanese PC I've tried it on, including PCs which had a pretty fresh installs of Win2K. (It may also happen on Japanese WinXP, I haven't tried it.)

In other words, it's a showstopper. And it happens even without py2exe.

A bit of google searching turned up a couple of *.jp blogs mentioning the error.

The workaround I suggested last Novemeber works (see my message quoted below), but it's a bit of a kludge. I dug a bit into the bug today and discovered it's actually a side-effect of what appears to be a Microsoft Win32 API registry bug.

_winreg uses RegQueryInfoKey to ask for the maximum length of all the subkeys in the fonts registry key. Then _winreg creates a buffer of that length (plus 1 for NULL termination) and uses RegEnumValue to grab the subkey, but for some fonts Windows returns ERROR_MORE_DATA (234) indicating that the buffer size is too small. There are at least 3 fonts that trigger it, which seem to be standard on Japanese Win2k.

To make a long story short, _winreg is failing to deal with some Japanese font registry keys, and throwing the WindowsError (ErrNo 234). A good workaround is to add a "try / except WindowsError: pass" around the _winreg.EnumValue() stanza inside the for loop. From my testing, for a system with 93 fonts, this should skip the 3 troublesome fonts and catalog the rest.

My apologies that I cannot provide a patch or a simple test case at this time, but I hope this report is useful.

Cheers,
Matthew.

p.s. It may be there are ways to work around this bug in _winreg, which I think would be the better place to do it. E.g. by using RegEnumValue to query the subkey size instead of RegQueryInfoKey. But I haven't isolated a simple test case yet, apart from "import matplotlib" on Japanese Win2k.

Here is the code fragment:

MSFontDirectories = [
     r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts',
     r'SOFTWARE\Microsoft\Windows\CurrentVersion\Fonts']

...

def win32InstalledFonts(directory=None, fontext='ttf'):

     """Search for fonts in the specified font directory, or use the
system directories if none given. A list of TrueType fonts are
returned by default with AFM fonts as an option.
"""

     import _winreg
     if directory is None:
         directory = win32FontDirectory()

     key, items = None, {}
     for fontdir in MSFontDirectories:
         try:
             local = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, fontdir)
         except OSError:
             continue

         if not local:
             return glob.glob(os.path.join(directory, '*.'+fontext))
         try:
             for j in range(_winreg.QueryInfoKey(local)[1]):
                 key, direc, any = _winreg.EnumValue( local, j)
                 if not os.path.dirname(direc):
                     direc = os.path.join(directory, direc)
                 direc = os.path.abspath(direc).lower()
                 if direc[-4:] == '.'+fontext:
                     items[direc] = 1
             return items.keys()
         finally:
             _winreg.CloseKey(local)
     return None

···

On 1/11/2004 3:32 PM, matthew arnison wrote:

I thought I'd mention some small isuues I found with using py2exe to deploy matplotlib 0.63.2 on Japanese Windows 2000.

> [...snip...]
>

* font_manager.py throws an exception, I suspect to do with Japanese font names or font directory names:

File "c:\Python23\lib\site-packages\matplotlib\font_manager.py", line 111, in
win32InstalledFonts
    key, direc, any = _winreg.EnumValue( local, j)
WindowsError: [Errno 234]

from this site:

http://www.google.com.au/search?q=cache:B8BlgKwVYxcJ:www.cubelab.com/ymasuda/python/index_jp.html+matplotlib+local+None+windowserror&hl=en

I found a workaround to add

local = None

at line 107 of font_manager.py to give this:

    for fontdir in MSFontDirectories:
        try:
            local = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, fontdir)
        except OSError:
            continue

        local = None

        if not local:
            return glob.glob(os.path.join(directory, '*.'+fontext))