From 2513930ce5ae699ed568ac2cd3eb2ad71af1d0a2 Mon Sep 17 00:00:00 2001 From: Tom Schoonjans Date: Thu, 2 Nov 2017 16:55:22 +0000 Subject: [PATCH] Use UTF-8 filenames on Windows Until now fopen was used on Windows to open files for reading and writing. This assumed however that the filename would be encoded in the current codepage, which is a major inconvenience and makes it even impossible to use filenames that use characters from more than one codepage. This patch enforces the use of UTF-8 filenames on all platforms. Based on the work of Owen Taylor (https://lists.cairographics.org/archives/cairo/2007-February/009591.html) --- src/cairo-output-stream.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++- src/cairo-png.c | 10 +++++---- src/cairoint.h | 4 ++++ 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c index f43f212ed..123142787 100644 --- a/src/cairo-output-stream.c +++ b/src/cairo-output-stream.c @@ -661,7 +661,7 @@ _cairo_output_stream_create_for_filename (const char *filename) if (filename == NULL) return _cairo_null_stream_create (); - file = fopen (filename, "wb"); + file = _cairo_fopen (filename, "wb"); if (file == NULL) { switch (errno) { case ENOMEM: @@ -804,3 +804,53 @@ _cairo_null_stream_create (void) return stream; } + +/** + * _cairo_fopen: + * @filename: filename to open + * @mode: mode string with which to open the file + * + * Exactly like the C library function, but possibly doing encoding + * conversion on the filename. On Unix platforms, the filename is + * passed directly to the system, but on Windows, the filename is + * interpreted as UTF-8, rather than in a codepage that would depend + * on system settings. + * + * Return value: The newly opened file, or NULL if an error occured. + **/ +FILE * +_cairo_fopen (const char *filename, const char *mode) +{ +#ifdef _WIN32 /* also defined on x86_64 */ + uint16_t *filename_w; + uint16_t *mode_w; + FILE *result; + + if (filename == NULL || mode == NULL) { + errno = EINVAL; + return NULL; + } + + if (_cairo_utf8_to_utf16 (filename, -1, &filename_w, NULL) != CAIRO_STATUS_SUCCESS) { + errno = EINVAL; + return NULL; + } + + if (_cairo_utf8_to_utf16 (mode, -1, &mode_w, NULL) != CAIRO_STATUS_SUCCESS) { + free (filename_w); + errno = EINVAL; + return NULL; + } + + result = _wfopen(filename_w, mode_w); + + free (filename_w); + free (mode_w); + + return result; + +#else /* Use fopen directly */ + return fopen (filename, mode); +#endif +} + diff --git a/src/cairo-png.c b/src/cairo-png.c index eab2537bc..ff0af8829 100644 --- a/src/cairo-png.c +++ b/src/cairo-png.c @@ -345,7 +345,8 @@ stdio_write_func (png_structp png, png_bytep data, png_size_t size) /** * cairo_surface_write_to_png: * @surface: a #cairo_surface_t with pixel contents - * @filename: the name of a file to write to + * @filename: the name of a file to write to; on Windows this filename + * is encoded in UTF-8. * * Writes the contents of @surface to a new file @filename as a PNG * image. @@ -373,7 +374,7 @@ cairo_surface_write_to_png (cairo_surface_t *surface, if (surface->finished) return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); - fp = fopen (filename, "wb"); + fp = _cairo_fopen (filename, "wb"); if (fp == NULL) { switch (errno) { case ENOMEM: @@ -738,7 +739,8 @@ read_png (struct png_read_closure_t *png_closure) /** * cairo_image_surface_create_from_png: - * @filename: name of PNG file to load + * @filename: name of PNG file to load. On Windows this filename + * is encoded in UTF-8. * * Creates a new image surface and initializes the contents to the * given PNG file. @@ -765,7 +767,7 @@ cairo_image_surface_create_from_png (const char *filename) struct png_read_closure_t png_closure; cairo_surface_t *surface; - png_closure.closure = fopen (filename, "rb"); + png_closure.closure = _cairo_fopen (filename, "rb"); if (png_closure.closure == NULL) { cairo_status_t status; switch (errno) { diff --git a/src/cairoint.h b/src/cairoint.h index 6f8303c3c..68ae75f07 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -1919,6 +1919,10 @@ _cairo_matrix_multiply (cairo_matrix_t *r, cairo_private void _cairo_observers_notify (cairo_list_t *observers, void *arg); +/* Open a file with a UTF-8 filename */ +cairo_private FILE * +_cairo_fopen (const char *filename, const char *mode); + /* Avoid unnecessary PLT entries. */ slim_hidden_proto (cairo_clip_preserve); slim_hidden_proto (cairo_close_path); -- 2.14.2 n_proto (cairo_clip_preserve); slim_hidden_proto (cairo_close_path); -- 2.14.2