|
33 | 33 | #include <jni.h>
|
34 | 34 | #include <com_sun_javafx_font_PrismFontFactory.h>
|
35 | 35 |
|
36 |
| -#define BSIZE (max(512, MAX_PATH+1)) |
37 |
| - |
38 | 36 | /* Typically all local references held by a JNI function are automatically
|
39 | 37 | * released by JVM when the function returns. However, there is a limit to the
|
40 | 38 | * number of local references that can remain active. If the local references
|
@@ -64,56 +62,141 @@ JNIEXPORT jint JNICALL JNI_OnLoad_javafx_font(JavaVM *vm, void *reserved) {
|
64 | 62 | }
|
65 | 63 | #endif // STATIC_BUILD
|
66 | 64 |
|
67 |
| -JNIEXPORT jbyteArray JNICALL |
| 65 | +JNIEXPORT jstring JNICALL |
68 | 66 | Java_com_sun_javafx_font_PrismFontFactory_getFontPath(JNIEnv *env, jobject thiz)
|
69 | 67 | {
|
70 |
| - char windir[BSIZE]; |
71 |
| - char sysdir[BSIZE]; |
72 |
| - char fontpath[BSIZE*2]; |
73 |
| - char *end; |
74 |
| - jbyteArray byteArrObj; |
75 |
| - int pathLen; |
76 |
| - unsigned char *data; |
| 68 | + const wchar_t* const FONTS_DIR = L"\\Fonts"; |
| 69 | + const UINT FONTS_DIR_STRLEN = (UINT)wcslen(FONTS_DIR); |
| 70 | + errno_t err = 0; |
| 71 | + wchar_t* windir = NULL; |
| 72 | + UINT windirBufSize = 0; // total buffer size in wchar_t elements (including NULL terminator) |
| 73 | + UINT windirFilledLen = 0; // amount of wchar_t elements filled (excluding NULL terminator) |
| 74 | + wchar_t* sysdir = NULL; |
| 75 | + UINT sysdirBufSize = 0; |
| 76 | + UINT sysdirFilledLen = 0; |
| 77 | + wchar_t* fontpath = NULL; |
| 78 | + UINT fontpathBufSize = 0; |
| 79 | + UINT fontpathFilledLen = 0; |
| 80 | + wchar_t *end = NULL; |
| 81 | + jsize pathLen = 0; |
| 82 | + jstring stringObj = NULL; |
| 83 | + |
| 84 | + /* Preallocate dummy 1-char buffers in case WinAPI wants to write something despite count being 0. |
| 85 | + * |
| 86 | + * Windows API doesn't explicitly say that calling GetSystem/GetWindowsDirectory(NULL, 0) |
| 87 | + * will NOT attempt writing to NULL, so we want to provide something to prevent access |
| 88 | + * violation if they do |
| 89 | + */ |
| 90 | + sysdir = calloc(1, sizeof(wchar_t)); |
| 91 | + windir = calloc(1, sizeof(wchar_t)); |
| 92 | + if (!sysdir || !windir) goto finish; |
77 | 93 |
|
78 | 94 | /* Locate fonts directories relative to the Windows System directory.
|
79 | 95 | * If Windows System location is different than the user's window
|
80 | 96 | * directory location, as in a shared Windows installation,
|
81 | 97 | * return both locations as potential font directories
|
82 | 98 | */
|
83 |
| - GetSystemDirectory(sysdir, BSIZE); |
84 |
| - end = strrchr(sysdir,'\\'); |
85 |
| - if (end && (stricmp(end,"\\System") || stricmp(end,"\\System32"))) { |
86 |
| - *end = 0; |
87 |
| - strcat(sysdir, "\\Fonts"); |
| 99 | + UINT ret = GetSystemDirectoryW(sysdir, 0); |
| 100 | + if (ret == 0) { |
| 101 | + goto finish; |
| 102 | + } else { |
| 103 | + /* if buffer is too small, GetSystemDirectory will return the length in wchar_t-s |
| 104 | + * that's needed to allocate the buffer, including terminating null character. |
| 105 | + * `*Len` variables keep amount of elements without the NULL-terminator, so appropriate |
| 106 | + * corrections have to be applied here. |
| 107 | + */ |
| 108 | + free(sysdir); |
| 109 | + sysdirBufSize = ret + FONTS_DIR_STRLEN; |
| 110 | + sysdir = calloc(sysdirBufSize, sizeof(wchar_t)); |
| 111 | + if (!sysdir) goto finish; |
| 112 | + ret = GetSystemDirectoryW(sysdir, sysdirBufSize - FONTS_DIR_STRLEN); |
| 113 | + if (ret == 0 || ret >= (sysdirBufSize - FONTS_DIR_STRLEN)) { |
| 114 | + goto finish; |
| 115 | + } |
| 116 | + // write was successful so now ret contains only characters written (without NULL-terminator) |
| 117 | + sysdirFilledLen = ret; |
| 118 | + } |
| 119 | + |
| 120 | + /* Figure out fonts location based on acquired System directory - commonly it is one level up |
| 121 | + * from sysdir and then in "Fonts" dir |
| 122 | + */ |
| 123 | + end = wcsrchr(sysdir, '\\'); |
| 124 | + if (end) { |
| 125 | + const wchar_t* const SYSTEM_DIR = L"\\System"; |
| 126 | + const wchar_t* const SYSTEM32_DIR = L"\\System32"; |
| 127 | + |
| 128 | + UINT sysdirToEnd = (UINT)(end - sysdir); |
| 129 | + UINT endLen = sysdirFilledLen - sysdirToEnd; |
| 130 | + |
| 131 | + // If the last directory in sysdir is "System" or "System32", strip that and replace it with "\Fonts" |
| 132 | + if ((_wcsnicmp(end, SYSTEM_DIR, endLen) == 0) || (_wcsnicmp(end, SYSTEM32_DIR, endLen) == 0)) { |
| 133 | + *end = 0; |
| 134 | + // no length re-check here, both \System and \System32 are longer than \Fonts string |
| 135 | + sysdirFilledLen -= endLen; |
| 136 | + err = wcsncat_s(sysdir, sysdirBufSize, FONTS_DIR, FONTS_DIR_STRLEN); |
| 137 | + if (err != 0) goto finish; |
| 138 | + sysdirFilledLen += FONTS_DIR_STRLEN; |
| 139 | + } |
88 | 140 | }
|
89 | 141 |
|
90 |
| - GetWindowsDirectory(windir, BSIZE); |
91 |
| - if (strlen(windir) > BSIZE-7) { |
92 |
| - *windir = 0; |
| 142 | + // Acquire Windows' directory - follows same assumptions as sysdir above |
| 143 | + ret = GetWindowsDirectoryW(windir, 0); |
| 144 | + if (ret == 0) { |
| 145 | + goto finish; |
93 | 146 | } else {
|
94 |
| - strcat(windir, "\\Fonts"); |
| 147 | + free(windir); |
| 148 | + windirBufSize = ret + FONTS_DIR_STRLEN; |
| 149 | + windir = calloc(windirBufSize, sizeof(wchar_t)); |
| 150 | + if (!windir) goto finish; |
| 151 | + ret = GetWindowsDirectoryW(windir, windirBufSize - FONTS_DIR_STRLEN); |
| 152 | + if (ret == 0 || ret >= (windirBufSize - FONTS_DIR_STRLEN)) { |
| 153 | + goto finish; |
| 154 | + } |
| 155 | + windirFilledLen = ret; |
95 | 156 | }
|
96 | 157 |
|
97 |
| - strcpy(fontpath,sysdir); |
98 |
| - if (stricmp(sysdir,windir)) { |
99 |
| - strcat(fontpath,";"); |
100 |
| - strcat(fontpath,windir); |
| 158 | + // "Fonts" directory should be placed right inside Windows directory, so just append it to the path |
| 159 | + err = wcsncat_s(windir, windirBufSize, FONTS_DIR, FONTS_DIR_STRLEN); |
| 160 | + if (err != 0) goto finish; |
| 161 | + windirFilledLen += FONTS_DIR_STRLEN; |
| 162 | + |
| 163 | + // Copy sysdir to final string that we'll return to JVM |
| 164 | + fontpathBufSize = windirFilledLen + sysdirFilledLen + 2; // add semicolon and zero-terminator |
| 165 | + fontpath = calloc(fontpathBufSize, sizeof(wchar_t)); |
| 166 | + if (!fontpath) goto finish; |
| 167 | + err = wcsncpy_s(fontpath, fontpathBufSize, sysdir, sysdirFilledLen); |
| 168 | + if (err != 0) goto finish; |
| 169 | + fontpathFilledLen = sysdirFilledLen; |
| 170 | + |
| 171 | + /* JFX expects either one path, or two separated by a semicolon. |
| 172 | + * If sysdir and windir are different, form a complete path "list" by |
| 173 | + * joining them in "<sysdir>;<windir>" pattern. JVM side will unpack it. |
| 174 | + */ |
| 175 | + if (_wcsnicmp(sysdir, windir, min(sysdirFilledLen, windirFilledLen))) { |
| 176 | + err = wcsncat_s(fontpath, fontpathBufSize, L";", 1); |
| 177 | + if (err != 0) goto finish; |
| 178 | + err = wcsncat_s(fontpath, fontpathBufSize, windir, windirFilledLen); |
| 179 | + if (err != 0) goto finish; |
| 180 | + fontpathFilledLen += windirFilledLen + 1; |
101 | 181 | }
|
102 | 182 |
|
103 |
| - pathLen = strlen(fontpath); |
| 183 | + pathLen = (jsize)wcsnlen_s(fontpath, fontpathBufSize); |
| 184 | + |
| 185 | + // Lastly, return what we just created to JVM as a String |
| 186 | + stringObj = (*env)->NewString(env, fontpath, fontpathFilledLen); |
104 | 187 |
|
105 |
| - byteArrObj = (*env)->NewByteArray(env, pathLen); |
106 |
| - if (byteArrObj == NULL) { |
107 |
| - return (jbyteArray)NULL; |
| 188 | +finish: |
| 189 | + if (sysdir != NULL) { |
| 190 | + free(sysdir); |
| 191 | + } |
| 192 | + if (windir != NULL) { |
| 193 | + free(windir); |
108 | 194 | }
|
109 |
| - data = (*env)->GetByteArrayElements(env, byteArrObj, NULL); |
110 |
| - if (data == NULL) { |
111 |
| - return byteArrObj; |
| 195 | + if (fontpath != NULL) { |
| 196 | + free(fontpath); |
112 | 197 | }
|
113 |
| - memcpy(data, fontpath, pathLen); |
114 |
| - (*env)->ReleaseByteArrayElements(env, byteArrObj, (jbyte*) data, (jint)0); |
115 | 198 |
|
116 |
| - return byteArrObj; |
| 199 | + return stringObj; |
117 | 200 | }
|
118 | 201 |
|
119 | 202 | /* The code below is used to obtain information from the windows font APIS
|
|
0 commit comments