1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 #include "common/archive.h"
23 #include "common/stream.h"
24 #include "common/unzip.h"
25 #include "common/macresman.h"
26 #include "graphics/fonts/bdf.h"
27 #include "graphics/fonts/macfont.h"
28 #include "graphics/fonts/ttf.h"
29 
30 #include "graphics/macgui/macwindowmanager.h"
31 #include "graphics/macgui/macfontmanager.h"
32 
33 namespace Graphics {
34 
35 // Source: Apple IIGS Technical Note #41, "Font Family Numbers"
36 // http://apple2.boldt.ca/?page=til/tn.iigs.041
37 static struct FontProto {
38 	int id;
39 	Common::Language lang;
40 	Common::CodePage encoding;
41 	const char *name;
42 } defaultFonts[] = {
43 	{ 2,		Common::UNK_LANG,	Common::kMacRoman,	"New York" },
44 	{ 3,		Common::UNK_LANG,	Common::kMacRoman,	"Geneva" },
45 	{ 4,		Common::UNK_LANG,	Common::kMacRoman,	"Monaco" },
46 	{ 5,		Common::UNK_LANG,	Common::kMacRoman,	"Venice" },
47 	{ 6,		Common::UNK_LANG,	Common::kMacRoman,	"London" },
48 	{ 7,		Common::UNK_LANG,	Common::kMacRoman,	"Athens" },
49 	{ 8,		Common::UNK_LANG,	Common::kMacRoman,	"San Francisco" },
50 	{ 9,		Common::UNK_LANG,	Common::kMacRoman,	"Toronto" },
51 	{ 11,		Common::UNK_LANG,	Common::kMacRoman,	"Cairo" },
52 	{ 12,		Common::UNK_LANG,	Common::kMacRoman,	"Los Angeles" },
53 	{ 13,		Common::UNK_LANG,	Common::kMacRoman,	"Zapf Dingbats" },
54 	{ 14,		Common::UNK_LANG,	Common::kMacRoman,	"Bookman" },
55 	{ 15,		Common::UNK_LANG,	Common::kMacRoman,	"Helvetica Narrow" },
56 	{ 16,		Common::UNK_LANG,	Common::kMacRoman,	"Palatino" },
57 	{ 18,		Common::UNK_LANG,	Common::kMacRoman,	"Zapf Chancery" },
58 	{ 20,		Common::UNK_LANG,	Common::kMacRoman,	"Times" },
59 	{ 21,		Common::UNK_LANG,	Common::kMacRoman,	"Helvetica" },
60 	{ 22,		Common::UNK_LANG,	Common::kMacRoman,	"Courier" },
61 	{ 23,		Common::UNK_LANG,	Common::kMacRoman,	"Symbol" },
62 	{ 24,		Common::UNK_LANG,	Common::kMacRoman,	"Taliesin" }, // mobile?
63 	{ 33,		Common::UNK_LANG,	Common::kMacRoman,	"Avant Garde" },
64 	{ 34,		Common::UNK_LANG,	Common::kMacRoman,	"New Century Schoolbook" },
65 	{ 16383,	Common::UNK_LANG,	Common::kMacRoman,	"Chicago" },
66 
67 	// Japanese (names are Shift JIS encoded)
68 	{ 16384,	Common::JA_JPN,		Common::kUtf8,		"Osaka" },
69 	{ 16436,	Common::JA_JPN,		Common::kUtf8,		"Osaka\x81\x7C\x93\x99\x95\x9D" }, // Osaka Mono
70 
71 	{ -1,		Common::UNK_LANG,	Common::kCodePageInvalid,	NULL }
72 };
73 
74 struct AliasProto {
75 	int id;
76 	int aliasForId;
77 	const char *name;
78 };
79 
80 static AliasProto defaultAliases[] = {
81 	// English names for Japanese fonts
82 	{ 16436,	16436,	"OsakaMono" },
83 
84 	// Missing Japanese fonts
85 	// These technically should be separate fonts, not just aliases for Osaka.
86 	// However, we don't have a free source for these right now.
87 	{ 16396,	16384,	"\x96\x7B\x96\xBE\x92\xA9\x81\x7C\x82\x6C" }, // Book Mincho - M
88 	{ 16433,	16436,	"\x93\x99\x95\x9D\x83\x53\x83\x56\x83\x62\x83\x4E" }, // Mono Gothic
89 	{ 16435,	16436,	"\x93\x99\x95\x9D\x96\xBE\x92\xA9" }, // Mono Ming
90 	{ 16640,	16384,	"\x92\x86\x83\x53\x83\x56\x83\x62\x83\x4E\x91\xCC" }, // Medium Gothic
91 	{ 16641,	16384,	"\x8D\xD7\x96\xBE\x92\xA9\x91\xCC" }, // Ming
92 	{ 16700,	16384,	"\x95\xBD\x90\xAC\x96\xBE\x92\xA9" }, // Heisei Mincho
93 	{ 16701,	16384,	"\x95\xBD\x90\xAC\x8A\x70\x83\x53\x83\x56\x83\x62\x83\x4E" }, // Heisei Kaku Gothic
94 
95 	{ -1,		-1,		NULL }
96 };
97 
98 static AliasProto latinModeAliases[] = {
99 	{ 0,		16383,	"System" }, // Chicago
100 	{ 1,		3,		"Application" }, // Geneva
101 	{ -1,		-1,		NULL }
102 };
103 
104 static AliasProto japaneseModeAliases[] = {
105 	{ 0,		16384,	"System" }, // Osaka
106 	{ 1,		16384,	"Application" }, // Osaka
107 	{ -1,		-1,		NULL }
108 };
109 
110 static const char *const fontStyleSuffixes[] = {
111 	"",
112 	"Bold",
113 	"Italic",
114 	"Underline",
115 	"Outline",
116 	"Shadow",
117 	"Condense",
118 	"Extend"
119 };
120 
parseSlant(const Common::String fontname)121 int parseSlant(const Common::String fontname) {
122 	int res = 0;
123 
124 	for (int i = 1; i < 7; i++)
125 		if (fontname.contains(fontStyleSuffixes[i]))
126 			res |= (1 << (i - 1));
127 
128 	return res;
129 }
130 
cleanFontName(const Common::String fontname)131 Common::String cleanFontName(const Common::String fontname) {
132 	const char *pos;
133 	Common::String f = fontname;
134 	for (int i = 1; i < 7; i++) {
135 		if ((pos = strstr(f.c_str(), fontStyleSuffixes[i])))
136 			f = Common::String(f.c_str(), pos);
137 	}
138 	f.trim();
139 
140 	return f;
141 }
142 
MacFontManager(uint32 mode,Common::Language language)143 MacFontManager::MacFontManager(uint32 mode, Common::Language language) : _mode(mode), _language(language) {
144 	for (FontProto *font = defaultFonts; font->name; font++) {
145 		if (!_fontInfo.contains(font->id)) {
146 			FontInfo *info = new FontInfo;
147 			info->lang = font->lang;
148 			info->encoding = font->encoding;
149 			info->name = font->name;
150 			_fontInfo[font->id] = info;
151 		}
152 		if (!_fontIds.contains(font->name)) {
153 			_fontIds[font->name] = font->id;
154 		}
155 	}
156 	for (AliasProto *alias = defaultAliases; alias->name; alias++) {
157 		if (!_fontInfo.contains(alias->id)) {
158 			FontInfo *info = new FontInfo;
159 			info->aliasForId = alias->aliasForId;
160 			info->name = alias->name;
161 			_fontInfo[alias->id] = info;
162 		}
163 		if (!_fontIds.contains(alias->name)) {
164 			_fontIds[alias->name] = alias->id;
165 		}
166 	}
167 	setLocalizedFonts();
168 
169 	if (_mode & MacGUIConstants::kWMModeForceBuiltinFonts) {
170 		_builtInFonts = true;
171 	} else {
172 		loadFonts();
173 	}
174 	_japaneseFontsLoaded = false;
175 }
176 
~MacFontManager()177 MacFontManager::~MacFontManager() {
178 	for (Common::HashMap<int, FontInfo *>::iterator it = _fontInfo.begin(); it != _fontInfo.end(); it++)
179 		delete it->_value;
180 	for (Common::HashMap<int, const Graphics::Font *>::iterator it = _uniFonts.begin(); it != _uniFonts.end(); it++)
181 		delete it->_value;
182 	for (Common::HashMap<Common::String, Common::SeekableReadStream *>::iterator it = _ttfData.begin(); it != _ttfData.end(); it++)
183 		delete it->_value;
184 	for (Common::HashMap<Common::String, MacFont *>::iterator it = _fontRegistry.begin(); it != _fontRegistry.end(); it++)
185 		delete it->_value;
186 	for (Common::HashMap<Common::String, MacFontFamily *>::iterator it = _fontFamilies.begin(); it != _fontFamilies.end(); it++)
187 		delete it->_value;
188 }
189 
setLocalizedFonts()190 void MacFontManager::setLocalizedFonts() {
191 	AliasProto *aliases = latinModeAliases;
192 	if (_language == Common::JA_JPN) {
193 		aliases = japaneseModeAliases;
194 		loadJapaneseFonts();
195 	}
196 	for (AliasProto *alias = aliases; alias->name; alias++) {
197 		if (_fontInfo.contains(alias->id)) {
198 			// Overwrite the font info that's already registered in case
199 			// we're switching languages or something.
200 			delete _fontInfo[alias->id];
201 		}
202 		FontInfo *info = new FontInfo;
203 		info->aliasForId = alias->aliasForId;
204 		info->name = alias->name;
205 		_fontInfo[alias->id] = info;
206 		_fontIds[alias->name] = alias->id;
207 	}
208 }
209 
loadFontsBDF()210 void MacFontManager::loadFontsBDF() {
211 	Common::Archive *dat;
212 
213 	dat = Common::makeZipArchive("classicmacfonts.dat");
214 
215 	if (!dat) {
216 		warning("Could not find classicmacfonts.dat. Falling back to built-in fonts");
217 		_builtInFonts = true;
218 
219 		return;
220 	}
221 
222 	Common::ArchiveMemberList list;
223 	dat->listMembers(list);
224 
225 	for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it) {
226 		Common::SeekableReadStream *stream = dat->createReadStreamForMember((*it)->getName());
227 
228 		Graphics::BdfFont *font = Graphics::BdfFont::loadFont(*stream);
229 
230 		delete stream;
231 
232 		Common::String fontName;
233 		MacFont *macfont;
234 
235 		if (font->getFamilyName() && *font->getFamilyName()) {
236 			fontName = Common::String::format("%s-%s-%d", font->getFamilyName(), font->getFontSlant(), font->getFontSize());
237 
238 			macfont = new MacFont(_fontIds.getValOrDefault(font->getFamilyName(), kMacFontNonStandard), font->getFontSize(), parseFontSlant(font->getFontSlant()));
239 		} else { // Get it from the file name
240 			fontName = (*it)->getName();
241 
242 			// Trim the .bdf extension
243 			for (int i = fontName.size() - 1; i >= 0; --i) {
244 				if (fontName[i] == '.') {
245 					while ((uint)i < fontName.size()) {
246 						fontName.deleteLastChar();
247 					}
248 					break;
249 				}
250 			}
251 
252 			macfont = new MacFont(kMacFontNonStandard);
253 			macfont->setName(fontName);
254 		}
255 
256 		FontMan.assignFontToName(fontName, font);
257 		//macfont->setFont(font);
258 		_fontRegistry.setVal(fontName, macfont);
259 
260 		debug(2, " %s", fontName.c_str());
261 	}
262 
263 	_builtInFonts = false;
264 
265 	delete dat;
266 }
267 
loadFonts()268 void MacFontManager::loadFonts() {
269 	Common::Archive *dat;
270 
271 	dat = Common::makeZipArchive("classicmacfonts.dat");
272 
273 	if (!dat) {
274 		warning("Could not find classicmacfonts.dat. Falling back to built-in fonts");
275 		_builtInFonts = true;
276 
277 		return;
278 	}
279 
280 	Common::ArchiveMemberList list;
281 	dat->listMembers(list);
282 
283 	for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it) {
284 		Common::SeekableReadStream *stream = dat->createReadStreamForMember((*it)->getName());
285 
286 		loadFonts(stream);
287 	}
288 
289 	_builtInFonts = false;
290 
291 	delete dat;
292 }
293 
loadJapaneseFonts()294 void MacFontManager::loadJapaneseFonts() {
295 	if (_japaneseFontsLoaded)
296 		return;
297 
298 #ifdef USE_FREETYPE2
299 	Common::Archive *dat;
300 
301 	dat = Common::makeZipArchive("japanesemacfonts.dat");
302 
303 	if (!dat) {
304 		warning("Could not find japanesemacfonts.dat");
305 		return;
306 	}
307 
308 	Common::ArchiveMemberList list;
309 	dat->listMembers(list);
310 
311 	for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it) {
312 		Common::SeekableReadStream *stream = dat->createReadStreamForMember((*it)->getName());
313 		Common::String fontName = (*it)->getName();
314 
315 		// Trim the .ttf extension
316 		for (int i = fontName.size() - 1; i >= 0; --i) {
317 			if (fontName[i] == '.') {
318 				while ((uint)i < fontName.size()) {
319 					fontName.deleteLastChar();
320 				}
321 				break;
322 			}
323 		}
324 
325 		_ttfData[fontName + "-0-0"] = stream;
326 	}
327 
328 	delete dat;
329 #else
330 	warning("Japanese fonts require FreeType");
331 #endif
332 
333 	// Set this to true even if we don't have FreeType so we don't spam warnings.
334 	_japaneseFontsLoaded = true;
335 }
336 
loadFonts(Common::SeekableReadStream * stream)337 void MacFontManager::loadFonts(Common::SeekableReadStream *stream) {
338 	Common::MacResManager fontFile;
339 
340 	if (!fontFile.loadFromMacBinary(*stream))
341 		return;
342 
343 	loadFonts(&fontFile);
344 }
345 
loadFonts(const Common::String & fileName)346 void MacFontManager::loadFonts(const Common::String &fileName) {
347 	Common::MacResManager fontFile;
348 
349 	if (!fontFile.open(fileName))
350 		return;
351 
352 	loadFonts(&fontFile);
353 }
354 
loadFonts(Common::MacResManager * fontFile)355 void MacFontManager::loadFonts(Common::MacResManager *fontFile) {
356 	Common::MacResIDArray fonds = fontFile->getResIDArray(MKTAG('F','O','N','D'));
357 	if (fonds.size() > 0) {
358 		for (Common::Array<uint16>::iterator iterator = fonds.begin(); iterator != fonds.end(); ++iterator) {
359 			Common::SeekableReadStream *fond = fontFile->getResource(MKTAG('F', 'O', 'N', 'D'), *iterator);
360 
361 			Common::String familyName = fontFile->getResName(MKTAG('F', 'O', 'N', 'D'), *iterator);
362 			int familySlant = parseSlant(familyName);
363 
364 			if (familySlant) {
365 				familyName = cleanFontName(familyName);
366 			}
367 
368 			Graphics::MacFontFamily *fontFamily = new MacFontFamily();
369 			fontFamily->load(*fond);
370 
371 			Common::Array<Graphics::MacFontFamily::AsscEntry> *assoc = fontFamily->getAssocTable();
372 
373 			bool fontFamilyUsed = false;
374 
375 			for (uint i = 0; i < assoc->size(); i++) {
376 				debug(8, "size: %d style: %d id: %d", (*assoc)[i]._fontSize, (*assoc)[i]._fontStyle | familySlant,
377 										(*assoc)[i]._fontID);
378 
379 				Common::SeekableReadStream *fontstream;
380 				MacFont *macfont;
381 				Graphics::MacFONTFont *font;
382 
383 				fontstream = fontFile->getResource(MKTAG('N', 'F', 'N', 'T'), (*assoc)[i]._fontID);
384 
385 				if (!fontstream)
386 					fontstream = fontFile->getResource(MKTAG('F', 'O', 'N', 'T'), (*assoc)[i]._fontID);
387 
388 #ifdef USE_FREETYPE2
389 				if (!fontstream) {
390 					// The sfnt resource should be just a copy of a TTF
391 					fontstream = fontFile->getResource(MKTAG('s', 'f', 'n', 't'), (*assoc)[i]._fontID);
392 					Common::String fontName = Common::String::format("%s-%d-0", familyName.c_str(), (*assoc)[i]._fontStyle | familySlant);
393 					_ttfData[fontName] = fontstream;
394 					continue;
395 				}
396 #endif
397 
398 				if (!fontstream) {
399 					if ((*assoc)[i]._fontSize == 0) {
400 						warning("MacFontManager: Detected possible TrueType FontID %d, but no TrueType support detected", (*assoc)[i]._fontID);
401 					} else {
402 						warning("MacFontManager: Unknown FontId: %d", (*assoc)[i]._fontID);
403 					}
404 					continue;
405 				}
406 
407 				fontFamilyUsed = true;
408 
409 				font = new Graphics::MacFONTFont;
410 				font->loadFont(*fontstream, fontFamily, (*assoc)[i]._fontSize, (*assoc)[i]._fontStyle | familySlant);
411 
412 				delete fontstream;
413 
414 				Common::String fontName = Common::String::format("%s-%d-%d", familyName.c_str(), (*assoc)[i]._fontStyle | familySlant, (*assoc)[i]._fontSize);
415 
416 				macfont = new MacFont(_fontIds.getValOrDefault(familyName, kMacFontNonStandard), (*assoc)[i]._fontSize, (*assoc)[i]._fontStyle | familySlant);
417 
418 				FontMan.assignFontToName(fontName, font);
419 				macfont->setFont(font, false);
420 				_fontRegistry.setVal(fontName, macfont);
421 
422 				debug(2, " %s", fontName.c_str());
423 			}
424 
425 			delete fond;
426 
427 			if (fontFamilyUsed)
428 				_fontFamilies[familyName] = fontFamily;
429 			else
430 				delete fontFamily;
431 		}
432 	}
433 }
434 
getFont(MacFont macFont)435 const Font *MacFontManager::getFont(MacFont macFont) {
436 	Common::String name;
437 	const Font *font = 0;
438 
439 	int aliasForId = getFontAliasForId(macFont.getId());
440 	if (aliasForId > -1) {
441 		macFont.setId(aliasForId);
442 	}
443 
444 	if (!_builtInFonts) {
445 		Common::Language lang = getFontLanguage(macFont.getId());
446 		if (lang == Common::JA_JPN && !_japaneseFontsLoaded) {
447 			loadJapaneseFonts();
448 		}
449 
450 		if (macFont.getName().empty()) {
451 			name = getFontName(macFont.getId(), macFont.getSize(), macFont.getSlant());
452 			macFont.setName(name);
453 		}
454 
455 		if (!_fontRegistry.contains(macFont.getName())) {
456 			// Let's try to generate name
457 			if (macFont.getSlant() != kMacFontRegular) {
458 				name = getFontName(macFont.getId(), macFont.getSize(), macFont.getSlant(), true);
459 				macFont.setName(name);
460 			}
461 
462 			if (!_fontRegistry.contains(macFont.getName()))
463 				generateFontSubstitute(macFont);
464 		}
465 
466 		font = FontMan.getFontByName(macFont.getName());
467 
468 		if (!font) {
469 			debug(1, "Cannot load font '%s'", macFont.getName().c_str());
470 
471 			font = FontMan.getFontByName(MacFont(kMacFontChicago, 12).getName());
472 		}
473 	}
474 
475 #ifdef USE_FREETYPE2
476 	if (!font) {
477 		if (_mode & kWMModeUnicode) {
478 			if (macFont.getSize() <= 0) {
479 				debug(1, "MacFontManager::getFont() - Font size <= 0!");
480 			}
481 			Common::HashMap<int, const Graphics::Font *>::iterator pFont = _uniFonts.find(macFont.getSize());
482 
483 			if (pFont != _uniFonts.end()) {
484 				font = pFont->_value;
485 			} else {
486 				font = Graphics::loadTTFFontFromArchive("FreeSans.ttf", macFont.getSize(), Graphics::kTTFSizeModeCharacter, 0, Graphics::kTTFRenderModeMonochrome);
487 				_uniFonts[macFont.getSize()] = font;
488 			}
489 		}
490 	}
491 #endif
492 
493 	if (!font)
494 		font = FontMan.getFontByUsage(macFont.getFallback());
495 
496 	return font;
497 }
498 
parseFontSlant(Common::String slant)499 int MacFontManager::parseFontSlant(Common::String slant) {
500 	slant.toUppercase();
501 	int slantVal = 0;
502 
503 	if (slant == "I")
504 		slantVal |= kMacFontItalic;
505 	if (slant == "B")
506 		slantVal |= kMacFontBold;
507 	if (slant == "R")
508 		slantVal |= kMacFontRegular;
509 
510 	return slantVal;
511 }
512 
parseSlantFromName(const Common::String & name)513 int MacFontManager::parseSlantFromName(const Common::String &name) {
514 	int slantVal = 0;
515 
516 	if (name.contains(" Bold"))
517 		slantVal |= kMacFontBold;
518 	if (name.contains(" Italic"))
519 		slantVal |= kMacFontItalic;
520 	if (name.contains(" Regular"))
521 		slantVal |= kMacFontRegular;
522 	if (name.contains(" Underline"))
523 		slantVal |= kMacFontUnderline;
524 	if (name.contains(" Shadow"))
525 		slantVal |= kMacFontShadow;
526 	if (name.contains(" Outline"))
527 		slantVal |= kMacFontOutline;
528 	if (name.contains(" Condense"))
529 		slantVal |= kMacFontCondense;
530 	if (name.contains(" Extend"))
531 		slantVal |= kMacFontExtend;
532 
533 	return slantVal;
534 }
535 
registerFontName(Common::String name,int preferredId)536 int MacFontManager::registerFontName(Common::String name, int preferredId) {
537 	// Don't register an empty font name, just return Geneva's ID.
538 	if (name.empty())
539 		return 1;
540 
541 	if (_fontIds.contains(name))
542 		return _fontIds[name];
543 
544 	int id;
545 	if (preferredId > -1 && !_fontInfo.contains(preferredId)) {
546 		id = preferredId;
547 	} else {
548 		// Preferred ID is already registered, find an unused one.
549 		id = 100;
550 		while (_fontInfo.contains(id))
551 			id++;
552 	}
553 
554 	FontInfo *info = new FontInfo;
555 	info->name = name;
556 	if (preferredId >= 0x4000) {
557 		info->lang = Common::JA_JPN;
558 		info->encoding = Common::kWindows932; // default to Shift JIS
559 	} else {
560 		info->encoding = Common::kMacRoman;
561 	}
562 	_fontInfo[id] = info;
563 	_fontIds[name] = id;
564 	return id;
565 }
566 
setName(const char * name)567 void MacFont::setName(const char *name) {
568 	_name = name;
569 }
570 
getFontName(uint16 id,int size,int slant,bool tryGen)571 const Common::String MacFontManager::getFontName(uint16 id, int size, int slant, bool tryGen) {
572 	Common::String rawName = getFontName(id);
573 	Common::String n = cleanFontName(rawName);
574 	int extraSlant = parseFontSlant(rawName);
575 	// let's try parse slant from name
576 	if (!extraSlant)
577 		extraSlant = parseSlantFromName(rawName);
578 
579 	if (n.empty()) {
580 		warning("MacFontManager: Requested font ID %d not found. Falling back to Geneva", id);
581 		n = "Geneva";
582 	}
583 
584 	return Common::String::format("%s-%d-%d", n.c_str(), slant | extraSlant, size);
585 }
586 
getFontName(MacFont & font)587 const Common::String MacFontManager::getFontName(MacFont &font) {
588 	return getFontName(font.getId(), font.getSize(), font.getSlant());
589 }
590 
getFontIdByName(Common::String name)591 int MacFontManager::getFontIdByName(Common::String name) {
592 	if (_fontIds.contains(name))
593 		return _fontIds[name];
594 
595 	return 1;
596 }
597 
getFontLanguage(uint16 id)598 Common::Language MacFontManager::getFontLanguage(uint16 id) {
599 	if (!_fontInfo.contains(id)) {
600 		warning("MacFontManager::getFontLanguage: No _fontInfo entry for font %d", id);
601 		return Common::UNK_LANG;
602 	}
603 	if (_fontInfo[id]->aliasForId > -1) {
604 		return getFontLanguage(_fontInfo[id]->aliasForId);
605 	}
606 	return _fontInfo[id]->lang;
607 }
608 
getFontEncoding(uint16 id)609 Common::CodePage MacFontManager::getFontEncoding(uint16 id) {
610 	if (!_fontInfo.contains(id)) {
611 		warning("MacFontManager::getFontEncoding: No _fontInfo entry for font %d", id);
612 		return Common::kCodePageInvalid;
613 	}
614 	if (_fontInfo[id]->aliasForId > -1) {
615 		return getFontEncoding(_fontInfo[id]->aliasForId);
616 	}
617 	return _fontInfo[id]->encoding;
618 }
619 
getFontAliasForId(uint16 id)620 int MacFontManager::getFontAliasForId(uint16 id) {
621 	if (!_fontInfo.contains(id)) {
622 		warning("MacFontManager::getFontAliasForId: No _fontInfo entry for font %d", id);
623 		return -1;
624 	}
625 	return _fontInfo[id]->aliasForId;
626 }
627 
getFontName(uint16 id)628 Common::String MacFontManager::getFontName(uint16 id) {
629 	if (!_fontInfo.contains(id)) {
630 		warning("MacFontManager::getFontAliasForId: No _fontInfo entry for font %d", id);
631 		return "";
632 	}
633 	if (_fontInfo[id]->aliasForId > -1) {
634 		return getFontName(_fontInfo[id]->aliasForId);
635 	}
636 	return _fontInfo[id]->name;
637 }
638 
generateFontSubstitute(MacFont & macFont)639 void MacFontManager::generateFontSubstitute(MacFont &macFont) {
640 	Common::String name;
641 
642 #ifdef USE_FREETYPE2
643 	// Check if we have TTF data for this font.
644 	name = getFontName(macFont.getId(), 0, macFont.getSlant());
645 	if (_ttfData.contains(name)) {
646 		generateTTFFont(macFont, _ttfData[name]);
647 		return;
648 	}
649 #endif
650 
651 	// Try to see if we have regular font
652 	if (macFont.getSlant() != kMacFontRegular) {
653 		name = getFontName(macFont.getId(), macFont.getSize(), kMacFontRegular);
654 
655 		if (_fontRegistry.contains(name) && !_fontRegistry[name]->isGenerated()) {
656 			generateFONTFont(macFont, *_fontRegistry[name]);
657 
658 			return;
659 		}
660 	}
661 
662 	// Now try twice size
663 	name = getFontName(macFont.getId(), macFont.getSize() * 2, macFont.getSlant());
664 	if (_fontRegistry.contains(name) && !_fontRegistry[name]->isGenerated()) {
665 		generateFONTFont(macFont, *_fontRegistry[name]);
666 
667 		return;
668 	}
669 
670 	// Now half size
671 	name = getFontName(macFont.getId(), macFont.getSize() / 2, macFont.getSlant());
672 	if (_fontRegistry.contains(name) && !_fontRegistry[name]->isGenerated()) {
673 		generateFONTFont(macFont, *_fontRegistry[name]);
674 
675 		return;
676 	}
677 
678 	// No simple substitute was found. Looking for neighborhood fonts
679 
680 	// First we gather all font sizes for this font
681 	Common::Array<MacFont *> sizes;
682 	for (Common::HashMap<Common::String, MacFont *>::iterator i = _fontRegistry.begin(); i != _fontRegistry.end(); ++i) {
683 		if (i->_value->getId() == macFont.getId() && i->_value->getSlant() == macFont.getSlant() && !i->_value->isGenerated())
684 			sizes.push_back(i->_value);
685 	}
686 
687 	if (sizes.empty()) {
688 		if (macFont.getSlant() == kMacFontRegular) {
689 			debug(1, "No viable substitute found (1) for font %s", getFontName(macFont).c_str());
690 			return;
691 		}
692 
693 		// Now let's try to find a regular font
694 		for (Common::HashMap<Common::String, MacFont *>::iterator i = _fontRegistry.begin(); i != _fontRegistry.end(); ++i) {
695 			if (i->_value->getId() == macFont.getId() && i->_value->getSlant() == kMacFontRegular && !i->_value->isGenerated())
696 				sizes.push_back(i->_value);
697 		}
698 
699 		if (sizes.empty()) {
700 			debug(1, "No viable substitute found (2) for font %s", getFontName(macFont).c_str());
701 			return;
702 		}
703 	}
704 
705 	// Now looking for the next larger font, and store the largest one for next check
706 	MacFont *candidate = nullptr;
707 	MacFont *maxSize = sizes[0];
708 	for (uint i = 0; i < sizes.size(); i++) {
709 		if (sizes[i]->getSize() == macFont.getSize()) { // Same size but regular slant
710 			candidate = sizes[i];
711 			break;
712 		}
713 
714 		if ((!candidate && sizes[i]->getSize() > macFont.getSize())
715 				|| (candidate && sizes[i]->getSize() < candidate->getSize()))
716 			candidate = sizes[i];
717 
718 		if (sizes[i]->getSize() > maxSize->getSize())
719 			maxSize = sizes[i];
720 	}
721 
722 	if (candidate) {
723 		generateFONTFont(macFont, *candidate);
724 		return;
725 	}
726 
727 	// Now next smaller font, which is the biggest we have
728 	generateFONTFont(macFont, *maxSize);
729 }
730 
731 #ifdef USE_FREETYPE2
generateTTFFont(MacFont & toFont,Common::SeekableReadStream * stream)732 void MacFontManager::generateTTFFont(MacFont &toFont, Common::SeekableReadStream *stream) {
733 	debug("Generating TTF font '%s'", getFontName(toFont).c_str());
734 
735 	// TODO: Handle getSlant() flags
736 
737 	stream->seek(0);
738 	Font *font = Graphics::loadTTFFont(*stream, toFont.getSize());
739 
740 	if (!font) {
741 		warning("Failed to generate font '%s'", getFontName(toFont).c_str());
742 	}
743 
744 	toFont.setGenerated(true);
745 	toFont.setFont(font, true);
746 
747 	FontMan.assignFontToName(getFontName(toFont), font);
748 	_fontRegistry.setVal(getFontName(toFont), new MacFont(toFont));
749 
750 	debug("Generated font '%s'", getFontName(toFont).c_str());
751 }
752 #endif
753 
generateFONTFont(MacFont & toFont,MacFont & fromFont)754 void MacFontManager::generateFONTFont(MacFont &toFont, MacFont &fromFont) {
755 	if (fromFont.isTrueType()) {
756 		warning("Cannot generate FONT font '%s' from TTF font '%s'", getFontName(toFont).c_str(), getFontName(fromFont).c_str());
757 		return;
758 	}
759 
760 	debugN("Found font substitute for font '%s' ", getFontName(toFont).c_str());
761 	debug("as '%s'", getFontName(fromFont).c_str());
762 
763 	int slant = kMacFontRegular;
764 	if (fromFont.getSlant() == kMacFontRegular)
765 		slant = toFont.getSlant();
766 
767 	MacFONTFont *fromFONTFont = static_cast<MacFONTFont *>(fromFont.getFont());
768 	MacFONTFont *font = Graphics::MacFONTFont::scaleFont(fromFONTFont, toFont.getSize(), slant);
769 
770 	if (!font) {
771 		warning("Failed to generate font '%s'", getFontName(toFont).c_str());
772 	}
773 
774 	toFont.setGenerated(true);
775 	toFont.setFont(font, false);
776 
777 	FontMan.assignFontToName(getFontName(toFont), font);
778 	_fontRegistry.setVal(getFontName(toFont), new MacFont(toFont));
779 
780 	debug("Generated font '%s'", getFontName(toFont).c_str());
781 }
782 
783 } // End of namespace Graphics
784