1 /*
2 ******************************************************************************
3 * Copyright (C) 2016 and later: Unicode, Inc. and others. *
4 * License & terms of use: http://www.unicode.org/copyright.html#License *
5 ******************************************************************************
6 ******************************************************************************
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
FontMap(const char * fileName,le_int16 pointSize,GUISupport * guiSupport,LEErrorCode & status)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;
30 le_bool haveFonts = FALSE;
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);
73 haveFonts = TRUE;
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);
94 haveFonts = TRUE;
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
~FontMap()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
getFontIndex(const char * fontName)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
147 // referenced; find it and resue it's index.
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
strip(char * s)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
getScriptFont(le_int32 scriptCode,LEErrorCode & status)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
getAscent() const233 le_int32 FontMap::getAscent() const
234 {
235 if (fAscent <= 0) {
236 ((FontMap *) this)->getMaxMetrics();
237 }
238
239 return fAscent;
240 }
241
getDescent() const242 le_int32 FontMap::getDescent() const
243 {
244 if (fDescent <= 0) {
245 ((FontMap *) this)->getMaxMetrics();
246 }
247
248 return fDescent;
249 }
250
getLeading() const251 le_int32 FontMap::getLeading() const
252 {
253 if (fLeading <= 0) {
254 ((FontMap *) this)->getMaxMetrics();
255 }
256
257 return fLeading;
258 }
259
getMaxMetrics()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
292