jshin@chromium.org | 6f31ac3 | 2014-03-26 22:15:14 +0000 | [diff] [blame] | 1 | /* |
| 2 | ****************************************************************************** |
Jungshik Shin | 87232d8 | 2017-05-13 21:10:13 -0700 | [diff] [blame] | 3 | * © 2016 and later: Unicode, Inc. and others. * |
Frank Tang | f90543d | 2020-10-30 19:02:04 -0700 | [diff] [blame] | 4 | * License & terms of use: http://www.unicode.org/copyright.html * |
Jungshik Shin | 5feb9ad | 2016-10-21 12:52:48 -0700 | [diff] [blame] | 5 | ****************************************************************************** |
| 6 | ****************************************************************************** |
jshin@chromium.org | 6f31ac3 | 2014-03-26 22:15:14 +0000 | [diff] [blame] | 7 | * Copyright (C) 1998-2006, International Business Machines Corporation and * |
| 8 | * others. All Rights Reserved. * |
| 9 | ****************************************************************************** |
| 10 | */ |
| 11 | |
| 12 | #include <stdio.h> |
| 13 | #include <string.h> |
| 14 | #include <ctype.h> |
| 15 | |
| 16 | #include "unicode/utypes.h" |
| 17 | #include "unicode/uscript.h" |
| 18 | |
| 19 | #include "layout/LETypes.h" |
| 20 | #include "layout/LEScripts.h" |
| 21 | #include "layout/LEFontInstance.h" |
| 22 | |
| 23 | #include "GUISupport.h" |
| 24 | #include "FontMap.h" |
| 25 | |
| 26 | FontMap::FontMap(const char *fileName, le_int16 pointSize, GUISupport *guiSupport, LEErrorCode &status) |
| 27 | : fPointSize(pointSize), fFontCount(0), fAscent(0), fDescent(0), fLeading(0), fGUISupport(guiSupport) |
| 28 | { |
| 29 | le_int32 defaultFont = -1, i, script; |
Frank Tang | 1f164ee | 2022-11-08 12:31:27 -0800 | [diff] [blame^] | 30 | le_bool haveFonts = false; |
jshin@chromium.org | 6f31ac3 | 2014-03-26 22:15:14 +0000 | [diff] [blame] | 31 | |
| 32 | /**/ |
| 33 | for (i = 0; i < scriptCodeCount; i += 1) { |
| 34 | fFontIndices[i] = -1; |
| 35 | fFontNames[i] = NULL; |
| 36 | fFontInstances[i] = NULL; |
| 37 | } |
| 38 | /**/ |
| 39 | |
| 40 | if (LE_FAILURE(status)) { |
| 41 | return; |
| 42 | } |
| 43 | |
| 44 | char *c, *scriptName, *fontName, *line, buffer[BUFFER_SIZE]; |
| 45 | FILE *file; |
| 46 | |
| 47 | file = fopen(fileName, "r"); |
| 48 | |
| 49 | if (file == NULL) { |
| 50 | sprintf(errorMessage, "Could not open the font map file: %s.", fileName); |
| 51 | fGUISupport->postErrorMessage(errorMessage, "Font Map Error"); |
| 52 | status = LE_FONT_FILE_NOT_FOUND_ERROR; |
| 53 | return; |
| 54 | } |
| 55 | |
| 56 | while (fgets(buffer, BUFFER_SIZE, file) != NULL) { |
| 57 | UScriptCode scriptCode; |
| 58 | UErrorCode scriptStatus = U_ZERO_ERROR; |
| 59 | |
| 60 | line = strip(buffer); |
| 61 | if (line[0] == '#' || line[0] == 0) { |
| 62 | continue; |
| 63 | } |
| 64 | |
| 65 | c = strchr(line, ':'); |
| 66 | c[0] = 0; |
| 67 | |
| 68 | fontName = strip(&c[1]); |
| 69 | scriptName = strip(line); |
| 70 | |
| 71 | if (strcmp(scriptName, "DEFAULT") == 0) { |
| 72 | defaultFont = getFontIndex(fontName); |
Frank Tang | 1f164ee | 2022-11-08 12:31:27 -0800 | [diff] [blame^] | 73 | haveFonts = true; |
jshin@chromium.org | 6f31ac3 | 2014-03-26 22:15:14 +0000 | [diff] [blame] | 74 | continue; |
| 75 | } |
| 76 | |
| 77 | le_int32 fillCount = uscript_getCode(scriptName, &scriptCode, 1, &scriptStatus); |
| 78 | |
| 79 | if (U_FAILURE(scriptStatus) || fillCount <= 0 || |
| 80 | scriptStatus == U_USING_FALLBACK_WARNING || scriptStatus == U_USING_DEFAULT_WARNING) { |
| 81 | sprintf(errorMessage, "The script name %s is invalid.", line); |
| 82 | fGUISupport->postErrorMessage(errorMessage, "Font Map Error"); |
| 83 | continue; |
| 84 | } |
| 85 | |
| 86 | script = (le_int32) scriptCode; |
| 87 | |
| 88 | if (fFontIndices[script] >= 0) { |
| 89 | // FIXME: complain that this is a duplicate entry and bail (?) |
| 90 | fFontIndices[script] = -1; |
| 91 | } |
| 92 | |
| 93 | fFontIndices[script] = getFontIndex(fontName); |
Frank Tang | 1f164ee | 2022-11-08 12:31:27 -0800 | [diff] [blame^] | 94 | haveFonts = true; |
jshin@chromium.org | 6f31ac3 | 2014-03-26 22:15:14 +0000 | [diff] [blame] | 95 | } |
| 96 | |
| 97 | if (defaultFont >= 0) { |
| 98 | for (script = 0; script < scriptCodeCount; script += 1) { |
| 99 | if (fFontIndices[script] < 0) { |
| 100 | fFontIndices[script] = defaultFont; |
| 101 | } |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | if (! haveFonts) { |
| 106 | sprintf(errorMessage, "The font map file %s does not contain any valid scripts.", fileName); |
| 107 | fGUISupport->postErrorMessage(errorMessage, "Font Map Error"); |
| 108 | status = LE_ILLEGAL_ARGUMENT_ERROR; |
| 109 | } |
| 110 | |
| 111 | fclose(file); |
| 112 | } |
| 113 | |
| 114 | FontMap::~FontMap() |
| 115 | { |
| 116 | le_int32 font; |
| 117 | |
| 118 | for (font = 0; font < fFontCount; font += 1) { |
| 119 | if (fFontNames[font] != NULL) { |
| 120 | delete[] (char *) fFontNames[font]; |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | for (font = 0; font < fFontCount; font += 1) { |
| 125 | if (fFontInstances[font] != NULL) { |
| 126 | delete fFontInstances[font]; |
| 127 | } |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | le_int32 FontMap::getFontIndex(const char *fontName) |
| 132 | { |
| 133 | le_int32 index; |
| 134 | |
| 135 | for (index = 0; index < fFontCount; index += 1) { |
| 136 | if (strcmp(fontName, fFontNames[index]) == 0) { |
| 137 | return index; |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | if (fFontCount < (le_int32) scriptCodeCount) { |
| 142 | index = fFontCount++; |
| 143 | } else { |
| 144 | // The font name table is full. Since there can |
| 145 | // only be scriptCodeCount fonts in use at once, |
| 146 | // there should be at least one that's not being |
Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 147 | // referenced; find it and reuse it's index. |
jshin@chromium.org | 6f31ac3 | 2014-03-26 22:15:14 +0000 | [diff] [blame] | 148 | |
| 149 | for (index = 0; index < fFontCount; index += 1) { |
| 150 | le_int32 script; |
| 151 | |
| 152 | for (script = 0; script < scriptCodeCount; script += 1) { |
| 153 | if (fFontIndices[script] == index) { |
| 154 | break; |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | if (script >= scriptCodeCount) { |
| 159 | break; |
| 160 | } |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | if (index >= scriptCodeCount) { |
| 165 | return -1; |
| 166 | } |
| 167 | |
| 168 | le_int32 len = strlen(fontName); |
| 169 | char *s = new char[len + 1]; |
| 170 | |
| 171 | fFontNames[index] = strcpy(s, fontName); |
| 172 | return index; |
| 173 | } |
| 174 | |
| 175 | char *FontMap::strip(char *s) |
| 176 | { |
| 177 | le_int32 start, end, len; |
| 178 | |
| 179 | start = 0; |
| 180 | len = strlen(s); |
| 181 | |
| 182 | while (start < len && isspace(s[start])) { |
| 183 | start += 1; |
| 184 | } |
| 185 | |
| 186 | end = len - 1; |
| 187 | |
| 188 | while (end > start && isspace(s[end])) { |
| 189 | end -= 1; |
| 190 | } |
| 191 | |
| 192 | if (end < len) { |
| 193 | s[end + 1] = '\0'; |
| 194 | } |
| 195 | |
| 196 | return &s[start]; |
| 197 | } |
| 198 | |
| 199 | const LEFontInstance *FontMap::getScriptFont(le_int32 scriptCode, LEErrorCode &status) |
| 200 | { |
| 201 | if (LE_FAILURE(status)) { |
| 202 | return NULL; |
| 203 | } |
| 204 | |
| 205 | if (scriptCode <= -1 || scriptCode >= scriptCodeCount) { |
| 206 | status = LE_ILLEGAL_ARGUMENT_ERROR; |
| 207 | return NULL; |
| 208 | } |
| 209 | |
| 210 | |
| 211 | le_int32 fontIndex = fFontIndices[scriptCode]; |
| 212 | |
| 213 | if (fontIndex < 0) { |
| 214 | sprintf(errorMessage, "No font was set for script %s", uscript_getName((UScriptCode) scriptCode)); |
| 215 | fGUISupport->postErrorMessage(errorMessage, "Font Map Error"); |
| 216 | status = LE_FONT_FILE_NOT_FOUND_ERROR; |
| 217 | return NULL; |
| 218 | } |
| 219 | |
| 220 | if (fFontInstances[fontIndex] == NULL) { |
| 221 | fFontInstances[fontIndex] = openFont(fFontNames[fontIndex], fPointSize, status); |
| 222 | |
| 223 | if (LE_FAILURE(status)) { |
| 224 | sprintf(errorMessage, "Could not open font file %s", fFontNames[fontIndex]); |
| 225 | fGUISupport->postErrorMessage(errorMessage, "Font Map Error"); |
| 226 | return NULL; |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | return fFontInstances[fontIndex]; |
| 231 | } |
| 232 | |
| 233 | le_int32 FontMap::getAscent() const |
| 234 | { |
| 235 | if (fAscent <= 0) { |
| 236 | ((FontMap *) this)->getMaxMetrics(); |
| 237 | } |
| 238 | |
| 239 | return fAscent; |
| 240 | } |
| 241 | |
| 242 | le_int32 FontMap::getDescent() const |
| 243 | { |
| 244 | if (fDescent <= 0) { |
| 245 | ((FontMap *) this)->getMaxMetrics(); |
| 246 | } |
| 247 | |
| 248 | return fDescent; |
| 249 | } |
| 250 | |
| 251 | le_int32 FontMap::getLeading() const |
| 252 | { |
| 253 | if (fLeading <= 0) { |
| 254 | ((FontMap *) this)->getMaxMetrics(); |
| 255 | } |
| 256 | |
| 257 | return fLeading; |
| 258 | } |
| 259 | |
| 260 | void FontMap::getMaxMetrics() |
| 261 | { |
| 262 | for (le_int32 i = 0; i < fFontCount; i += 1) { |
| 263 | LEErrorCode status = LE_NO_ERROR; |
| 264 | le_int32 ascent, descent, leading; |
| 265 | |
| 266 | if (fFontInstances[i] == NULL) { |
| 267 | fFontInstances[i] = openFont(fFontNames[i], fPointSize, status); |
| 268 | |
| 269 | if (LE_FAILURE(status)) { |
| 270 | continue; |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | ascent = fFontInstances[i]->getAscent(); |
| 275 | descent = fFontInstances[i]->getDescent(); |
| 276 | leading = fFontInstances[i]->getLeading(); |
| 277 | |
| 278 | if (ascent > fAscent) { |
| 279 | fAscent = ascent; |
| 280 | } |
| 281 | |
| 282 | if (descent > fDescent) { |
| 283 | fDescent = descent; |
| 284 | } |
| 285 | |
| 286 | if (leading > fLeading) { |
| 287 | fLeading = leading; |
| 288 | } |
| 289 | } |
| 290 | } |
| 291 | |