#998 Add MZ_ICU option for string encoding conversion

Add ICU as an alternative backend to libiconv using ucnv_convert.
ICU is detected via CMake's FindICU module on non-Windows platforms.
Add CI build configuration for ICU in GitHub Actions.
This commit is contained in:
tbeu 2026-07-04 10:30:29 +02:00 committed by Nathan Moin Vaziri
parent cde8b2f81b
commit b59f684429
4 changed files with 83 additions and 0 deletions

View file

@ -176,6 +176,15 @@ jobs:
packages: clang-14 llvm-14
gcov-exec: llvm-cov-14 gcov
- name: Ubuntu Clang ICU
os: ubuntu-latest
compiler: clang-14
cxx-compiler: clang++-14
cmake-args: -D MZ_CODE_COVERAGE=ON -D MZ_ICU=ON -D MZ_ICONV=OFF
codecov: ubuntu_clang_icu
packages: clang-14 llvm-14 libicu-dev
gcov-exec: llvm-cov-14 gcov
# No code coverage supported
- name: Windows MSVC
os: windows-latest

View file

@ -60,6 +60,7 @@ option(MZ_OPENSSL "Enables OpenSSL for encryption" ${UNIX})
cmake_dependent_option(MZ_LIBBSD "Builds with libbsd crypto random" ON "UNIX" OFF)
# Character conversion options
option(MZ_ICONV "Enables iconv for string encoding conversion" ON)
option(MZ_ICU "Enables ICU for string encoding conversion" OFF)
# Code generation options
option(MZ_COMPRESS_ONLY "Only support compression" OFF)
option(MZ_DECOMPRESS_ONLY "Only support decompression" OFF)
@ -586,6 +587,12 @@ else()
list(APPEND MINIZIP_DEF -DMZ_ZIP_NO_CRYPTO)
endif()
# Iconv and ICU are mutually exclusive; ICU takes precedence
if (MZ_ICU AND MZ_ICONV)
message(STATUS "ICU enabled, disabling Iconv")
set(MZ_ICONV OFF)
endif()
# Iconv is only necessary when it is not already built-in
# FindIconv requires cmake 3.11 or higher
if (MZ_ICONV)
@ -606,6 +613,24 @@ else()
set(MZ_ICONV OFF)
endif()
if (MZ_ICU)
find_package(ICU COMPONENTS uc QUIET)
endif()
if(ICU_FOUND)
message(STATUS "Using ICU ${ICU_VERSION}")
list(APPEND MINIZIP_DEF -DHAVE_ICU)
list(APPEND MINIZIP_DEP_PKG ICU)
list(APPEND MINIZIP_LIB ICU::uc)
set(PC_PRIVATE_LIBS "${PC_PRIVATE_LIBS} -licuuc")
else()
if(MZ_ICU)
message(STATUS "ICU library not found")
set(MZ_ICU OFF)
endif()
endif()
endif()
# Setup predefined macros
@ -1056,6 +1081,7 @@ add_feature_info(MZ_OPENSSL MZ_OPENSSL "Enables OpenSSL for encryption")
add_feature_info(MZ_LIBBSD MZ_LIBBSD "Builds with libbsd crypto random")
# Character conversion options
add_feature_info(MZ_ICONV MZ_ICONV "Enables iconv string encoding conversion library")
add_feature_info(MZ_ICU MZ_ICU "Enables ICU string encoding conversion library")
# Code generation options
add_feature_info(MZ_COMPRESS_ONLY MZ_COMPRESS_ONLY "Only support compression")
add_feature_info(MZ_DECOMPRESS_ONLY MZ_DECOMPRESS_ONLY "Only support decompression")

View file

@ -82,6 +82,7 @@ cmake --build build
| MZ_OPENSSL | Enables OpenSSL encryption | UNIX |
| MZ_LIBBSD | Builds with libbsd crypto random | UNIX |
| MZ_ICONV | Enables iconv encoding conversion | ON |
| MZ_ICU | Enables ICU encoding conversion | OFF |
| MZ_COMPRESS_ONLY | Only support compression | OFF |
| MZ_DECOMPRESS_ONLY | Only support decompression | OFF |
| MZ_FILE32_API | Builds using posix 32-bit file api | OFF |

View file

@ -18,6 +18,9 @@
#if defined(HAVE_ICONV)
# include <iconv.h>
#endif
#if defined(HAVE_ICU)
# include <unicode/ucnv.h>
#endif
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
@ -88,6 +91,50 @@ char *mz_os_utf8_string_create(const char *string, int32_t encoding) {
return string_utf8;
}
#elif defined(HAVE_ICU)
char *mz_os_utf8_string_create(const char *string, int32_t encoding) {
char string_encoding[16];
const char *from_encoding = NULL;
int32_t string_length = 0;
int32_t string_utf8_size = 0;
char *string_utf8 = NULL;
int32_t result = 0;
UErrorCode status = U_ZERO_ERROR;
if (!string || encoding <= 0)
return NULL;
if (encoding == MZ_ENCODING_UTF8)
from_encoding = "UTF-8";
else if (encoding == MZ_ENCODING_CODEPAGE_437)
from_encoding = "ibm-437";
else if (encoding == MZ_ENCODING_CODEPAGE_932)
from_encoding = "windows-932-2000";
else if (encoding == MZ_ENCODING_CODEPAGE_936)
from_encoding = "windows-936-2000";
else if (encoding == MZ_ENCODING_CODEPAGE_950)
from_encoding = "windows-950-2000";
else {
snprintf(string_encoding, sizeof(string_encoding), "windows-%" PRId32 "-2000", encoding);
from_encoding = string_encoding;
}
string_length = (int32_t)strlen(string);
string_utf8_size = string_length * 4 + 1;
string_utf8 = (char *)calloc(string_utf8_size, sizeof(char));
if (!string_utf8)
return NULL;
result = ucnv_convert("UTF-8", from_encoding, string_utf8, string_utf8_size, string, string_length, &status);
if (U_FAILURE(status) || result < 0) {
free(string_utf8);
string_utf8 = NULL;
}
return string_utf8;
}
#else
char *mz_os_utf8_string_create(const char *string, int32_t encoding) {
return strdup(string);