/indra/llrender/llfontregistry.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 670 lines · 502 code · 79 blank · 89 comment · 106 complexity · 2cb217345cea814c84c66ee6e21b6dfd MD5 · raw file

  1. /**
  2. * @file llfontregistry.cpp
  3. * @author Brad Payne
  4. * @brief Storage for fonts.
  5. *
  6. * $LicenseInfo:firstyear=2008&license=viewerlgpl$
  7. * Second Life Viewer Source Code
  8. * Copyright (C) 2010, Linden Research, Inc.
  9. *
  10. * This library is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU Lesser General Public
  12. * License as published by the Free Software Foundation;
  13. * version 2.1 of the License only.
  14. *
  15. * This library is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  18. * Lesser General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Lesser General Public
  21. * License along with this library; if not, write to the Free Software
  22. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  23. *
  24. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  25. * $/LicenseInfo$
  26. */
  27. #include "linden_common.h"
  28. #include "llgl.h"
  29. #include "llfontfreetype.h"
  30. #include "llfontgl.h"
  31. #include "llfontregistry.h"
  32. #include <boost/tokenizer.hpp>
  33. #include "llcontrol.h"
  34. #include "lldir.h"
  35. #include "llwindow.h"
  36. extern LLControlGroup gSavedSettings;
  37. using std::string;
  38. using std::map;
  39. bool fontDescInitFromXML(LLXMLNodePtr node, LLFontDescriptor& desc);
  40. LLFontDescriptor::LLFontDescriptor():
  41. mStyle(0)
  42. {
  43. }
  44. LLFontDescriptor::LLFontDescriptor(const std::string& name,
  45. const std::string& size,
  46. const U8 style,
  47. const string_vec_t& file_names):
  48. mName(name),
  49. mSize(size),
  50. mStyle(style),
  51. mFileNames(file_names)
  52. {
  53. }
  54. LLFontDescriptor::LLFontDescriptor(const std::string& name,
  55. const std::string& size,
  56. const U8 style):
  57. mName(name),
  58. mSize(size),
  59. mStyle(style)
  60. {
  61. }
  62. bool LLFontDescriptor::operator<(const LLFontDescriptor& b) const
  63. {
  64. if (mName < b.mName)
  65. return true;
  66. else if (mName > b.mName)
  67. return false;
  68. if (mStyle < b.mStyle)
  69. return true;
  70. else if (mStyle > b.mStyle)
  71. return false;
  72. if (mSize < b.mSize)
  73. return true;
  74. else
  75. return false;
  76. }
  77. static const std::string s_template_string("TEMPLATE");
  78. bool LLFontDescriptor::isTemplate() const
  79. {
  80. return getSize() == s_template_string;
  81. }
  82. // Look for substring match and remove substring if matched.
  83. bool removeSubString(std::string& str, const std::string& substr)
  84. {
  85. size_t pos = str.find(substr);
  86. if (pos != string::npos)
  87. {
  88. str.erase(pos, substr.size());
  89. return true;
  90. }
  91. return false;
  92. }
  93. // Check for substring match without modifying the source string.
  94. bool findSubString(std::string& str, const std::string& substr)
  95. {
  96. size_t pos = str.find(substr);
  97. if (pos != string::npos)
  98. {
  99. return true;
  100. }
  101. return false;
  102. }
  103. // Normal form is
  104. // - raw name
  105. // - bold, italic style info reflected in both style and font name.
  106. // - other style info removed.
  107. // - size info moved to mSize, defaults to Medium
  108. // For example,
  109. // - "SansSerifHuge" would normalize to { "SansSerif", "Huge", 0 }
  110. // - "SansSerifBold" would normalize to { "SansSerifBold", "Medium", BOLD }
  111. LLFontDescriptor LLFontDescriptor::normalize() const
  112. {
  113. std::string new_name(mName);
  114. std::string new_size(mSize);
  115. U8 new_style(mStyle);
  116. // Only care about style to extent it can be picked up by font.
  117. new_style &= (LLFontGL::BOLD | LLFontGL::ITALIC);
  118. // All these transformations are to support old-style font specifications.
  119. if (removeSubString(new_name,"Small"))
  120. new_size = "Small";
  121. if (removeSubString(new_name,"Big"))
  122. new_size = "Large";
  123. if (removeSubString(new_name,"Medium"))
  124. new_size = "Medium";
  125. if (removeSubString(new_name,"Large"))
  126. new_size = "Large";
  127. if (removeSubString(new_name,"Huge"))
  128. new_size = "Huge";
  129. // HACK - Monospace is the only one we don't remove, so
  130. // name "Monospace" doesn't get taken down to ""
  131. // For other fonts, there's no ambiguity between font name and size specifier.
  132. if (new_size != s_template_string && new_size.empty() && findSubString(new_name,"Monospace"))
  133. new_size = "Monospace";
  134. if (new_size.empty())
  135. new_size = "Medium";
  136. if (removeSubString(new_name,"Bold"))
  137. new_style |= LLFontGL::BOLD;
  138. if (removeSubString(new_name,"Italic"))
  139. new_style |= LLFontGL::ITALIC;
  140. return LLFontDescriptor(new_name,new_size,new_style,getFileNames());
  141. }
  142. LLFontRegistry::LLFontRegistry(const string_vec_t& xui_paths,
  143. bool create_gl_textures)
  144. : mCreateGLTextures(create_gl_textures)
  145. {
  146. // Propagate this down from LLUICtrlFactory so LLRender doesn't
  147. // need an upstream dependency on LLUI.
  148. mXUIPaths = xui_paths;
  149. // This is potentially a slow directory traversal, so we want to
  150. // cache the result.
  151. mUltimateFallbackList = LLWindow::getDynamicFallbackFontList();
  152. }
  153. LLFontRegistry::~LLFontRegistry()
  154. {
  155. clear();
  156. }
  157. bool LLFontRegistry::parseFontInfo(const std::string& xml_filename)
  158. {
  159. bool success = false; // Succeed if we find at least one XUI file
  160. const string_vec_t& xml_paths = mXUIPaths;
  161. for (string_vec_t::const_iterator path_it = xml_paths.begin();
  162. path_it != xml_paths.end();
  163. ++path_it)
  164. {
  165. LLXMLNodePtr root;
  166. std::string full_filename = gDirUtilp->findSkinnedFilename(*path_it, xml_filename);
  167. bool parsed_file = LLXMLNode::parseFile(full_filename, root, NULL);
  168. if (!parsed_file)
  169. continue;
  170. if ( root.isNull() || ! root->hasName( "fonts" ) )
  171. {
  172. llwarns << "Bad font info file: "
  173. << full_filename << llendl;
  174. continue;
  175. }
  176. std::string root_name;
  177. root->getAttributeString("name",root_name);
  178. if (root->hasName("fonts"))
  179. {
  180. // Expect a collection of children consisting of "font" or "font_size" entries
  181. bool init_succ = initFromXML(root);
  182. success = success || init_succ;
  183. }
  184. }
  185. //if (success)
  186. // dump();
  187. return success;
  188. }
  189. std::string currentOsName()
  190. {
  191. #if LL_WINDOWS
  192. return "Windows";
  193. #elif LL_DARWIN
  194. return "Mac";
  195. #elif LL_SDL
  196. return "Linux";
  197. #else
  198. return "";
  199. #endif
  200. }
  201. bool fontDescInitFromXML(LLXMLNodePtr node, LLFontDescriptor& desc)
  202. {
  203. if (node->hasName("font"))
  204. {
  205. std::string attr_name;
  206. if (node->getAttributeString("name",attr_name))
  207. {
  208. desc.setName(attr_name);
  209. }
  210. std::string attr_style;
  211. if (node->getAttributeString("font_style",attr_style))
  212. {
  213. desc.setStyle(LLFontGL::getStyleFromString(attr_style));
  214. }
  215. desc.setSize(s_template_string);
  216. }
  217. LLXMLNodePtr child;
  218. for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
  219. {
  220. std::string child_name;
  221. child->getAttributeString("name",child_name);
  222. if (child->hasName("file"))
  223. {
  224. std::string font_file_name = child->getTextContents();
  225. desc.getFileNames().push_back(font_file_name);
  226. }
  227. else if (child->hasName("os"))
  228. {
  229. if (child_name == currentOsName())
  230. {
  231. fontDescInitFromXML(child, desc);
  232. }
  233. }
  234. }
  235. return true;
  236. }
  237. bool LLFontRegistry::initFromXML(LLXMLNodePtr node)
  238. {
  239. LLXMLNodePtr child;
  240. for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
  241. {
  242. std::string child_name;
  243. child->getAttributeString("name",child_name);
  244. if (child->hasName("font"))
  245. {
  246. LLFontDescriptor desc;
  247. bool font_succ = fontDescInitFromXML(child, desc);
  248. LLFontDescriptor norm_desc = desc.normalize();
  249. if (font_succ)
  250. {
  251. // if this is the first time we've seen this font name,
  252. // create a new template map entry for it.
  253. const LLFontDescriptor *match_desc = getMatchingFontDesc(desc);
  254. if (match_desc == NULL)
  255. {
  256. // Create a new entry (with no corresponding font).
  257. mFontMap[norm_desc] = NULL;
  258. }
  259. // otherwise, find the existing entry and combine data.
  260. else
  261. {
  262. // Prepend files from desc.
  263. // A little roundabout because the map key is const,
  264. // so we have to fetch it, make a new map key, and
  265. // replace the old entry.
  266. string_vec_t match_file_names = match_desc->getFileNames();
  267. match_file_names.insert(match_file_names.begin(),
  268. desc.getFileNames().begin(),
  269. desc.getFileNames().end());
  270. LLFontDescriptor new_desc = *match_desc;
  271. new_desc.getFileNames() = match_file_names;
  272. mFontMap.erase(*match_desc);
  273. mFontMap[new_desc] = NULL;
  274. }
  275. }
  276. }
  277. else if (child->hasName("font_size"))
  278. {
  279. std::string size_name;
  280. F32 size_value;
  281. if (child->getAttributeString("name",size_name) &&
  282. child->getAttributeF32("size",size_value))
  283. {
  284. mFontSizes[size_name] = size_value;
  285. }
  286. }
  287. }
  288. return true;
  289. }
  290. bool LLFontRegistry::nameToSize(const std::string& size_name, F32& size)
  291. {
  292. font_size_map_t::iterator it = mFontSizes.find(size_name);
  293. if (it != mFontSizes.end())
  294. {
  295. size = it->second;
  296. return true;
  297. }
  298. return false;
  299. }
  300. LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc)
  301. {
  302. // Name should hold a font name recognized as a setting; the value
  303. // of the setting should be a list of font files.
  304. // Size should be a recognized string value
  305. // Style should be a set of flags including any implied by the font name.
  306. // First decipher the requested size.
  307. LLFontDescriptor norm_desc = desc.normalize();
  308. F32 point_size;
  309. bool found_size = nameToSize(norm_desc.getSize(),point_size);
  310. if (!found_size)
  311. {
  312. llwarns << "createFont unrecognized size " << norm_desc.getSize() << llendl;
  313. return NULL;
  314. }
  315. llinfos << "createFont " << norm_desc.getName() << " size " << norm_desc.getSize() << " style " << ((S32) norm_desc.getStyle()) << llendl;
  316. F32 fallback_scale = 1.0;
  317. // Find corresponding font template (based on same descriptor with no size specified)
  318. LLFontDescriptor template_desc(norm_desc);
  319. template_desc.setSize(s_template_string);
  320. const LLFontDescriptor *match_desc = getClosestFontTemplate(template_desc);
  321. if (!match_desc)
  322. {
  323. llwarns << "createFont failed, no template found for "
  324. << norm_desc.getName() << " style [" << ((S32)norm_desc.getStyle()) << "]" << llendl;
  325. return NULL;
  326. }
  327. // See whether this best-match font has already been instantiated in the requested size.
  328. LLFontDescriptor nearest_exact_desc = *match_desc;
  329. nearest_exact_desc.setSize(norm_desc.getSize());
  330. font_reg_map_t::iterator it = mFontMap.find(nearest_exact_desc);
  331. // If we fail to find a font in the fonts directory, it->second might be NULL.
  332. // We shouldn't construcnt a font with a NULL mFontFreetype.
  333. // This may not be the best solution, but it at least prevents a crash.
  334. if (it != mFontMap.end() && it->second != NULL)
  335. {
  336. llinfos << "-- matching font exists: " << nearest_exact_desc.getName() << " size " << nearest_exact_desc.getSize() << " style " << ((S32) nearest_exact_desc.getStyle()) << llendl;
  337. // copying underlying Freetype font, and storing in LLFontGL with requested font descriptor
  338. LLFontGL *font = new LLFontGL;
  339. font->mFontDescriptor = desc;
  340. font->mFontFreetype = it->second->mFontFreetype;
  341. mFontMap[desc] = font;
  342. return font;
  343. }
  344. // Build list of font names to look for.
  345. // Files specified for this font come first, followed by those from the default descriptor.
  346. string_vec_t file_names = match_desc->getFileNames();
  347. string_vec_t default_file_names;
  348. LLFontDescriptor default_desc("default",s_template_string,0);
  349. const LLFontDescriptor *match_default_desc = getMatchingFontDesc(default_desc);
  350. if (match_default_desc)
  351. {
  352. file_names.insert(file_names.end(),
  353. match_default_desc->getFileNames().begin(),
  354. match_default_desc->getFileNames().end());
  355. }
  356. // Add ultimate fallback list - generated dynamically on linux,
  357. // null elsewhere.
  358. file_names.insert(file_names.end(),
  359. getUltimateFallbackList().begin(),
  360. getUltimateFallbackList().end());
  361. // Load fonts based on names.
  362. if (file_names.empty())
  363. {
  364. llwarns << "createFont failed, no file names specified" << llendl;
  365. return NULL;
  366. }
  367. LLFontFreetype::font_vector_t fontlist;
  368. LLFontGL *result = NULL;
  369. // Snarf all fonts we can into fontlist. First will get pulled
  370. // off the list and become the "head" font, set to non-fallback.
  371. // Rest will consitute the fallback list.
  372. BOOL is_first_found = TRUE;
  373. std::string local_path = LLFontGL::getFontPathLocal();
  374. std::string sys_path = LLFontGL::getFontPathSystem();
  375. // The fontname string may contain multiple font file names separated by semicolons.
  376. // Break it apart and try loading each one, in order.
  377. for(string_vec_t::iterator file_name_it = file_names.begin();
  378. file_name_it != file_names.end();
  379. ++file_name_it)
  380. {
  381. LLFontGL *fontp = new LLFontGL;
  382. std::string font_path = local_path + *file_name_it;
  383. // *HACK: Fallback fonts don't render, so we can use that to suppress
  384. // creation of OpenGL textures for test apps. JC
  385. BOOL is_fallback = !is_first_found || !mCreateGLTextures;
  386. F32 extra_scale = (is_fallback)?fallback_scale:1.0;
  387. if (!fontp->loadFace(font_path, extra_scale * point_size,
  388. LLFontGL::sVertDPI, LLFontGL::sHorizDPI, 2, is_fallback))
  389. {
  390. font_path = sys_path + *file_name_it;
  391. if (!fontp->loadFace(font_path, extra_scale * point_size,
  392. LLFontGL::sVertDPI, LLFontGL::sHorizDPI, 2, is_fallback))
  393. {
  394. LL_INFOS_ONCE("LLFontRegistry") << "Couldn't load font " << *file_name_it << LL_ENDL;
  395. delete fontp;
  396. fontp = NULL;
  397. }
  398. }
  399. if(fontp)
  400. {
  401. if (is_first_found)
  402. {
  403. result = fontp;
  404. is_first_found = false;
  405. }
  406. else
  407. {
  408. fontlist.push_back(fontp->mFontFreetype);
  409. delete fontp;
  410. fontp = NULL;
  411. }
  412. }
  413. }
  414. if (result && !fontlist.empty())
  415. {
  416. result->mFontFreetype->setFallbackFonts(fontlist);
  417. }
  418. if (result)
  419. {
  420. result->mFontDescriptor = desc;
  421. }
  422. else
  423. {
  424. llwarns << "createFont failed in some way" << llendl;
  425. }
  426. mFontMap[desc] = result;
  427. return result;
  428. }
  429. void LLFontRegistry::reset()
  430. {
  431. for (font_reg_map_t::iterator it = mFontMap.begin();
  432. it != mFontMap.end();
  433. ++it)
  434. {
  435. // Reset the corresponding font but preserve the entry.
  436. if (it->second)
  437. it->second->reset();
  438. }
  439. }
  440. void LLFontRegistry::clear()
  441. {
  442. for (font_reg_map_t::iterator it = mFontMap.begin();
  443. it != mFontMap.end();
  444. ++it)
  445. {
  446. LLFontGL *fontp = it->second;
  447. delete fontp;
  448. }
  449. mFontMap.clear();
  450. }
  451. void LLFontRegistry::destroyGL()
  452. {
  453. for (font_reg_map_t::iterator it = mFontMap.begin();
  454. it != mFontMap.end();
  455. ++it)
  456. {
  457. // Reset the corresponding font but preserve the entry.
  458. if (it->second)
  459. it->second->destroyGL();
  460. }
  461. }
  462. LLFontGL *LLFontRegistry::getFont(const LLFontDescriptor& desc)
  463. {
  464. font_reg_map_t::iterator it = mFontMap.find(desc);
  465. if (it != mFontMap.end())
  466. return it->second;
  467. else
  468. {
  469. LLFontGL *fontp = createFont(desc);
  470. if (!fontp)
  471. {
  472. llwarns << "getFont failed, name " << desc.getName()
  473. <<" style=[" << ((S32) desc.getStyle()) << "]"
  474. << " size=[" << desc.getSize() << "]" << llendl;
  475. }
  476. return fontp;
  477. }
  478. }
  479. const LLFontDescriptor *LLFontRegistry::getMatchingFontDesc(const LLFontDescriptor& desc)
  480. {
  481. LLFontDescriptor norm_desc = desc.normalize();
  482. font_reg_map_t::iterator it = mFontMap.find(norm_desc);
  483. if (it != mFontMap.end())
  484. return &(it->first);
  485. else
  486. return NULL;
  487. }
  488. static U32 bitCount(U8 c)
  489. {
  490. U32 count = 0;
  491. if (c & 1)
  492. count++;
  493. if (c & 2)
  494. count++;
  495. if (c & 4)
  496. count++;
  497. if (c & 8)
  498. count++;
  499. if (c & 16)
  500. count++;
  501. if (c & 32)
  502. count++;
  503. if (c & 64)
  504. count++;
  505. if (c & 128)
  506. count++;
  507. return count;
  508. }
  509. // Find nearest match for the requested descriptor.
  510. const LLFontDescriptor *LLFontRegistry::getClosestFontTemplate(const LLFontDescriptor& desc)
  511. {
  512. const LLFontDescriptor *exact_match_desc = getMatchingFontDesc(desc);
  513. if (exact_match_desc)
  514. {
  515. return exact_match_desc;
  516. }
  517. LLFontDescriptor norm_desc = desc.normalize();
  518. const LLFontDescriptor *best_match_desc = NULL;
  519. for (font_reg_map_t::iterator it = mFontMap.begin();
  520. it != mFontMap.end();
  521. ++it)
  522. {
  523. const LLFontDescriptor* curr_desc = &(it->first);
  524. // Ignore if not a template.
  525. if (!curr_desc->isTemplate())
  526. continue;
  527. // Ignore if font name is wrong.
  528. if (curr_desc->getName() != norm_desc.getName())
  529. continue;
  530. // Reject font if it matches any bits we don't want
  531. if (curr_desc->getStyle() & ~norm_desc.getStyle())
  532. {
  533. continue;
  534. }
  535. // Take if it's the first plausible candidate we've found.
  536. if (!best_match_desc)
  537. {
  538. best_match_desc = curr_desc;
  539. continue;
  540. }
  541. // Take if it matches more bits than anything before.
  542. U8 best_style_match_bits =
  543. norm_desc.getStyle() & best_match_desc->getStyle();
  544. U8 curr_style_match_bits =
  545. norm_desc.getStyle() & curr_desc->getStyle();
  546. if (bitCount(curr_style_match_bits) > bitCount(best_style_match_bits))
  547. {
  548. best_match_desc = curr_desc;
  549. continue;
  550. }
  551. // Tie-breaker: take if it matches bold.
  552. if (curr_style_match_bits & LLFontGL::BOLD) // Bold is requested and this descriptor matches it.
  553. {
  554. best_match_desc = curr_desc;
  555. continue;
  556. }
  557. }
  558. // Nothing matched.
  559. return best_match_desc;
  560. }
  561. void LLFontRegistry::dump()
  562. {
  563. llinfos << "LLFontRegistry dump: " << llendl;
  564. for (font_size_map_t::iterator size_it = mFontSizes.begin();
  565. size_it != mFontSizes.end();
  566. ++size_it)
  567. {
  568. llinfos << "Size: " << size_it->first << " => " << size_it->second << llendl;
  569. }
  570. for (font_reg_map_t::iterator font_it = mFontMap.begin();
  571. font_it != mFontMap.end();
  572. ++font_it)
  573. {
  574. const LLFontDescriptor& desc = font_it->first;
  575. llinfos << "Font: name=" << desc.getName()
  576. << " style=[" << ((S32)desc.getStyle()) << "]"
  577. << " size=[" << desc.getSize() << "]"
  578. << " fileNames="
  579. << llendl;
  580. for (string_vec_t::const_iterator file_it=desc.getFileNames().begin();
  581. file_it != desc.getFileNames().end();
  582. ++file_it)
  583. {
  584. llinfos << " file: " << *file_it <<llendl;
  585. }
  586. }
  587. }
  588. const string_vec_t& LLFontRegistry::getUltimateFallbackList() const
  589. {
  590. return mUltimateFallbackList;
  591. }