Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fopen() fails on Windows for non-ANSI characters #645

Closed
shenjia opened this issue Nov 27, 2018 · 5 comments

Comments

Projects
None yet
3 participants
@shenjia
Copy link

commented Nov 27, 2018

I use windows 10 with a Chinese username '运城国际学校', the desktop dir will display as "C:\Users\运城国~1\Desktop" in command line or use os.getcwd().

While I try to load a font file which store in this path by:

root = os.getcwd() # C:\Users\运城国~1\Desktop\Test
fontfile = os.path.join(root, 'test.ttf')
pygame.font.Font(fontfile, 32)

I got this error [OSError: unable to read font file 'C:\Users\运城国~1\Desktop\Test\test.ttf']

I can use os.listdir() to location this font file, and it works well if I move this file to c:\test. So the font file have no problem at all. It must be something wrong to location this file in pygame.font.Font.

Since I can load images, audios from the same UTF-8 path, except fonts. Maybe there are different ways to location file between load image / audios and load fonts?

I have to use this UTF-8 path, because I want to package my game with Pyinstaller. It will automaticly extract everything to Temp directory which is in user's home directory.

@dlon

This comment has been minimized.

Copy link
Member

commented Nov 28, 2018

I think fopen() doesn't work with UTF-8? At least on Windows. And I don't know if this is true any longer, but Windows apparently only accepts UTF-16 paths.

pygame/src_c/font.c

Lines 666 to 686 in e987de7

if (Bytes_Check(obj)) {
const char *filename = Bytes_AS_STRING(obj);
FILE *test;
/*check if it is a valid file, else SDL_ttf segfaults*/
test = fopen(filename, "rb");
if (test == NULL) {
PyObject *tmp = NULL;
if (strcmp(filename, font_defaultname) == 0) {
/* filename is the default font; get it's resource
*/
tmp = font_resource(font_defaultname);
}
if (tmp == NULL) {
if (PyErr_Occurred() == NULL) {
PyErr_Format(PyExc_IOError,
"unable to read font file '%.1024s'",
filename);
}
goto error;

I think we want to use Unicode_AsEncodedPath()?

@dlon

This comment has been minimized.

Copy link
Member

commented Nov 28, 2018

Actually, this is already being used, but it returns an incorrect result?

pygame/src_c/rwobject.c

Lines 170 to 259 in e987de7

static PyObject *
pgRWopsEncodeString(PyObject *obj, const char *encoding, const char *errors,
PyObject *eclass)
{
PyObject *oencoded;
PyObject *exc_type;
PyObject *exc_value;
PyObject *exc_trace;
PyObject *str;
if (obj == NULL) {
/* Assume an error was raise; forward it */
return NULL;
}
if (encoding == NULL) {
encoding = pg_default_encoding;
}
if (errors == NULL) {
errors = pg_default_errors;
}
if (PyUnicode_Check(obj)) {
oencoded = PyUnicode_AsEncodedString(obj, encoding, errors);
if (oencoded != NULL) {
return oencoded;
}
else if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
/* Forward memory errors */
return NULL;
}
else if (eclass != NULL) {
/* Foward as eclass error */
PyErr_Fetch(&exc_type, &exc_value, &exc_trace);
Py_DECREF(exc_type);
Py_XDECREF(exc_trace);
if (exc_value == NULL) {
PyErr_SetString(eclass, "Unicode encoding error");
}
else {
str = PyObject_Str(exc_value);
Py_DECREF(exc_value);
if (str != NULL) {
PyErr_SetObject(eclass, str);
Py_DECREF(str);
}
}
return NULL;
}
else if (encoding == pg_default_encoding &&
errors == pg_default_errors) {
/* The default encoding and error handling should not fail */
return RAISE(PyExc_SystemError,
"Pygame bug (in pgRWopsEncodeString):"
" unexpected encoding error");
}
PyErr_Clear();
}
else if (Bytes_Check(obj)) {
Py_INCREF(obj);
return obj;
}
Py_RETURN_NONE;
}
static PyObject *
pgRWopsEncodeFilePath(PyObject *obj, PyObject *eclass)
{
PyObject *result = pgRWopsEncodeString(obj, UNICODE_DEF_FS_CODEC,
UNICODE_DEF_FS_ERROR, eclass);
if (result == NULL || result == Py_None) {
return result;
}
if ((size_t)Bytes_GET_SIZE(result) != strlen(Bytes_AS_STRING(result))) {
if (eclass != NULL) {
Py_DECREF(result);
result = pgRWopsEncodeString(obj, NULL, NULL, NULL);
if (result == NULL) {
return NULL;
}
PyErr_Format(eclass,
"File path '%.1024s' contains null characters",
Bytes_AS_STRING(result));
Py_DECREF(result);
return NULL;
}
Py_DECREF(result);
Py_RETURN_NONE;
}
return result;
}

@dlon

This comment has been minimized.

Copy link
Member

commented Nov 28, 2018

Okay the problem really is fopen() on Windows.
Solution: I think we must use _wfopen() on Windows.

@dlon dlon added the Windows label Nov 28, 2018

@dlon dlon changed the title Failed to load font from file while use a long Chinese username in Windows fopen() fails on Windows for non-ANSI characters Nov 28, 2018

@dlon dlon self-assigned this Nov 29, 2018

@dlon dlon marked this as a duplicate of #196 Nov 29, 2018

@dlon dlon closed this in #656 Dec 6, 2018

@shenjia

This comment has been minimized.

Copy link
Author

commented Dec 10, 2018

Thx to you guys! But so how could I fix this problem on my PC? Must I waiting for the next version of pygame?

@illume

This comment has been minimized.

Copy link
Member

commented Dec 10, 2018

Hi,

if you're on windows you can download from the appveyor page: https://ci.appveyor.com/project/pygame/pygame

  • click on the python version you use, either 32bit or 64bit
  • then click on "Artifacts" tab
  • download the .whl file.
  • install the .whl file with pip install pygame-1.9.5.dev0-cp36-cp36m-win32.whl

Compiling it yourself is very easy on other platforms.
https://www.pygame.org/wiki/Compilation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.