1 /*
2  ******************************************************************************
3  * © 2016 and later: Unicode, Inc. and others.                    *
4  * License & terms of use: http://www.unicode.org/copyright.html      *
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 reuse 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