freetype-py icon indicating copy to clipboard operation
freetype-py copied to clipboard

FT_Exception: (cannot open resource) - non-english file path

Open moi15moi opened this issue 2 years ago • 21 comments

freetype.Face() seems to not take non-ascii path. It will raise an exception.

With a string path

import freetype
font = freetype.Face(r"C:\Windows\Fonts\حُرّ عادي.ttf") # it return: freetype.ft_errors.FT_Exception: FT_Exception:  (cannot open resource)

With an BufferedReader

from pathlib import Path
import freetype
font = freetype.Face(Path(r"C:\Windows\Fonts\حُرّ عادي.ttf").open("rb")) # Works

moi15moi avatar Jul 12 '22 23:07 moi15moi

Try C:\\Windows\\Fonts\\حُرّ عادي.ttf (double backslash) or "C:/Windows/Fonts/حُرّ عادي.ttf". This is likely a Windows IO anomaly.

HinTak avatar Jul 13 '22 03:07 HinTak

I try the two method you have suggest. Both of them give me the same exception.

freetype.Face("C:\\Windows\\Fonts\\حُرّ عادي.ttf")
freetype.Face("C:/Windows/Fonts/حُرّ عادي.ttf")

moi15moi avatar Jul 13 '22 23:07 moi15moi

Can you print here what is Path(r"C:\Windows\Fonts\حُرّ عادي.ttf")?

rougier avatar Jul 14 '22 05:07 rougier

It print me this (so the exact same thing that I provide): C:\Windows\Fonts\حُرّ عادي.ttf

moi15moi avatar Jul 14 '22 21:07 moi15moi

Is is a PosixPath ?

rougier avatar Jul 15 '22 04:07 rougier

No, simply a path.

It will instanciate a windows path since I am on windows: https://docs.python.org/3/library/pathlib.html

moi15moi avatar Jul 15 '22 11:07 moi15moi

AFAIK it needs to be something acceptable to windows' c runtime's fopen().

Python 3 is unicode internally and needs some encoding on all platforms. Unix's takes utf8, while some windows internal bits are utf16. You might be able to experiment by playing with python ctypes on Windows directly. I am afraid this is one area where playing with wine does not help, and wine differs slightly in behavior compared to windows. Ie running windows python on wine is no help.

HinTak avatar Jul 15 '22 11:07 HinTak

@HinTak We could use pathlib internally and we would be on the safe side, no?

rougier avatar Jul 15 '22 16:07 rougier

@rougier yes, apparently it is included in python standard library since 3.4 (and we had a separate frozen-ish 2.x branch). If anybody cares enough about older 3.x, they should do their own thing and/or submit a pull.

That said, we really want to know why it doesn't work on Windows with non-ascii paths. The "/" vs "" (and needing to double "\" or switch to "/") is relatively well known. Win32 IO have ascii vs unicode IO routines, but I don't think freetype uses those.

Thinking about it, @moi15moi , where did you get your libfreetype.dll? I am wondering if msvc compiled ones uses the win32 routines. I am quite sure mingw built ones don't.

HinTak avatar Jul 15 '22 17:07 HinTak

Thinking about it, @moi15moi , where did you get your libfreetype.dll? I am wondering if msvc compiled ones uses the win32 routines. I am quite sure mingw built ones don't.

I don't know what you are talking about. I just install freetype-py like this: pip install freetype-py

moi15moi avatar Jul 15 '22 22:07 moi15moi

@moi15moi argh, sorry somebody else among the team did the bundled binaries. According to https://pypi.org/project/freetype-py/#description , it is probably msvc (=visual studio) + cmake -built.

HinTak avatar Jul 15 '22 23:07 HinTak

I agree it would be good to know why it fails. See also https://jod.al/2019/12/10/pathlib-and-paths-with-arbitrary-bytes/

rougier avatar Jul 16 '22 05:07 rougier

I was just reminded of a somewhat similar issue in a different project I work on: https://github.com/Robmaister/SharpFont/issues/89 File names go through two translation processes, one as it crosses the non-C / C boundary (in both cases, the non-C sides are widows-based and uses UTF16). Then between freetype and the file-system. There workaround there was to read the file in full in memory on the non-C side, and passes the whole block of memory to freetype. (We never "solved" it, just found a workaround and moved on...)

The two questions are mainly what bytes as a file name FreeType receives, and can one open a file with those bytes via fopen.

HinTak avatar Jul 20 '22 13:07 HinTak

Can't we use the pathlib library to solve the translation problem for us then?

rougier avatar Jul 25 '22 07:07 rougier

I encountered the same problem, and it was solved by changing the encoding of line 162 of freetype/init.py to ansi

def _encode_filename(filename):
    # encoded = filename.encode(sys.getfilesystemencoding())
    encoded = filename.encode('ansi')
    if "?" not in filename and b"?" in encoded:
        # A bug, decoding mbcs always ignore exception, still isn't fixed in Python 2,
        # view http://bugs.python.org/issue850997 for detail
        raise UnicodeError()
    return encoded

cd-86 avatar Sep 13 '22 02:09 cd-86

This is not a good idea.

moi15moi avatar Sep 13 '22 10:09 moi15moi

@rougier Have you found any alternative?

moi15moi avatar Aug 15 '23 20:08 moi15moi

I just found this issue: https://gitlab.freedesktop.org/freetype/freetype/-/issues/1098 This may help you

moi15moi avatar Aug 17 '23 14:08 moi15moi

No, you did not notice the "won't fix" label on the issue you referenced. :-).

HinTak avatar Aug 17 '23 18:08 HinTak

Exactly. Then, when the font filename contain an non ascii char and the platform is windows, we could simply create a memory font.

moi15moi avatar Aug 18 '23 10:08 moi15moi

Not sure what you are asking for. It is the same as what you did, freetype.Face(Path(r"C:\Windows\Fonts\حُرّ عادي.ttf").open("rb")), creating a memory font. This is pretty much how the sharpfont solution works too, just read it via the non-C way, then passes the block of memory to the c-side to freetype.

HinTak avatar Aug 18 '23 10:08 HinTak