Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

juce_Font.cpp 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. namespace FontValues
  22. {
  23. static float limitFontHeight (const float height) noexcept
  24. {
  25. return jlimit (0.1f, 10000.0f, height);
  26. }
  27. const float defaultFontHeight = 14.0f;
  28. float minimumHorizontalScale = 0.7f;
  29. String fallbackFont;
  30. String fallbackFontStyle;
  31. }
  32. typedef Typeface::Ptr (*GetTypefaceForFont) (const Font&);
  33. GetTypefaceForFont juce_getTypefaceForFont = nullptr;
  34. float Font::getDefaultMinimumHorizontalScaleFactor() noexcept { return FontValues::minimumHorizontalScale; }
  35. void Font::setDefaultMinimumHorizontalScaleFactor (float newValue) noexcept { FontValues::minimumHorizontalScale = newValue; }
  36. //==============================================================================
  37. class TypefaceCache : private DeletedAtShutdown
  38. {
  39. public:
  40. TypefaceCache()
  41. {
  42. setSize (10);
  43. }
  44. ~TypefaceCache()
  45. {
  46. clearSingletonInstance();
  47. }
  48. juce_DeclareSingleton (TypefaceCache, false)
  49. void setSize (const int numToCache)
  50. {
  51. const ScopedWriteLock sl (lock);
  52. faces.clear();
  53. faces.insertMultiple (-1, CachedFace(), numToCache);
  54. }
  55. void clear()
  56. {
  57. const ScopedWriteLock sl (lock);
  58. setSize (faces.size());
  59. defaultFace = nullptr;
  60. }
  61. Typeface::Ptr findTypefaceFor (const Font& font)
  62. {
  63. const ScopedReadLock slr (lock);
  64. auto faceName = font.getTypefaceName();
  65. auto faceStyle = font.getTypefaceStyle();
  66. jassert (faceName.isNotEmpty());
  67. for (int i = faces.size(); --i >= 0;)
  68. {
  69. CachedFace& face = faces.getReference(i);
  70. if (face.typefaceName == faceName
  71. && face.typefaceStyle == faceStyle
  72. && face.typeface != nullptr
  73. && face.typeface->isSuitableForFont (font))
  74. {
  75. face.lastUsageCount = ++counter;
  76. return face.typeface;
  77. }
  78. }
  79. const ScopedWriteLock slw (lock);
  80. int replaceIndex = 0;
  81. auto bestLastUsageCount = std::numeric_limits<size_t>::max();
  82. for (int i = faces.size(); --i >= 0;)
  83. {
  84. auto lu = faces.getReference(i).lastUsageCount;
  85. if (bestLastUsageCount > lu)
  86. {
  87. bestLastUsageCount = lu;
  88. replaceIndex = i;
  89. }
  90. }
  91. auto& face = faces.getReference (replaceIndex);
  92. face.typefaceName = faceName;
  93. face.typefaceStyle = faceStyle;
  94. face.lastUsageCount = ++counter;
  95. if (juce_getTypefaceForFont == nullptr)
  96. face.typeface = Font::getDefaultTypefaceForFont (font);
  97. else
  98. face.typeface = juce_getTypefaceForFont (font);
  99. jassert (face.typeface != nullptr); // the look and feel must return a typeface!
  100. if (defaultFace == nullptr && font == Font())
  101. defaultFace = face.typeface;
  102. return face.typeface;
  103. }
  104. Typeface::Ptr defaultFace;
  105. private:
  106. struct CachedFace
  107. {
  108. CachedFace() noexcept : lastUsageCount (0) {}
  109. // Although it seems a bit wacky to store the name here, it's because it may be a
  110. // placeholder rather than a real one, e.g. "<Sans-Serif>" vs the actual typeface name.
  111. // Since the typeface itself doesn't know that it may have this alias, the name under
  112. // which it was fetched needs to be stored separately.
  113. String typefaceName, typefaceStyle;
  114. size_t lastUsageCount;
  115. Typeface::Ptr typeface;
  116. };
  117. ReadWriteLock lock;
  118. Array<CachedFace> faces;
  119. size_t counter = 0;
  120. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TypefaceCache)
  121. };
  122. juce_ImplementSingleton (TypefaceCache)
  123. void Typeface::setTypefaceCacheSize (int numFontsToCache)
  124. {
  125. TypefaceCache::getInstance()->setSize (numFontsToCache);
  126. }
  127. void (*clearOpenGLGlyphCache)() = nullptr;
  128. void Typeface::clearTypefaceCache()
  129. {
  130. TypefaceCache::getInstance()->clear();
  131. RenderingHelpers::SoftwareRendererSavedState::clearGlyphCache();
  132. if (clearOpenGLGlyphCache != nullptr)
  133. clearOpenGLGlyphCache();
  134. }
  135. //==============================================================================
  136. class Font::SharedFontInternal : public ReferenceCountedObject
  137. {
  138. public:
  139. SharedFontInternal() noexcept
  140. : typeface (TypefaceCache::getInstance()->defaultFace),
  141. typefaceName (Font::getDefaultSansSerifFontName()),
  142. typefaceStyle (Font::getDefaultStyle()),
  143. height (FontValues::defaultFontHeight)
  144. {
  145. }
  146. SharedFontInternal (int styleFlags, float fontHeight) noexcept
  147. : typefaceName (Font::getDefaultSansSerifFontName()),
  148. typefaceStyle (FontStyleHelpers::getStyleName (styleFlags)),
  149. height (fontHeight),
  150. underline ((styleFlags & underlined) != 0)
  151. {
  152. if (styleFlags == plain)
  153. typeface = TypefaceCache::getInstance()->defaultFace;
  154. }
  155. SharedFontInternal (const String& name, int styleFlags, float fontHeight) noexcept
  156. : typefaceName (name),
  157. typefaceStyle (FontStyleHelpers::getStyleName (styleFlags)),
  158. height (fontHeight),
  159. underline ((styleFlags & underlined) != 0)
  160. {
  161. if (styleFlags == plain && typefaceName.isEmpty())
  162. typeface = TypefaceCache::getInstance()->defaultFace;
  163. }
  164. SharedFontInternal (const String& name, const String& style, float fontHeight) noexcept
  165. : typefaceName (name), typefaceStyle (style), height (fontHeight)
  166. {
  167. if (typefaceName.isEmpty())
  168. typefaceName = Font::getDefaultSansSerifFontName();
  169. }
  170. explicit SharedFontInternal (const Typeface::Ptr& face) noexcept
  171. : typeface (face),
  172. typefaceName (face->getName()),
  173. typefaceStyle (face->getStyle()),
  174. height (FontValues::defaultFontHeight)
  175. {
  176. jassert (typefaceName.isNotEmpty());
  177. }
  178. SharedFontInternal (const SharedFontInternal& other) noexcept
  179. : ReferenceCountedObject(),
  180. typeface (other.typeface),
  181. typefaceName (other.typefaceName),
  182. typefaceStyle (other.typefaceStyle),
  183. height (other.height),
  184. horizontalScale (other.horizontalScale),
  185. kerning (other.kerning),
  186. ascent (other.ascent),
  187. underline (other.underline)
  188. {
  189. }
  190. bool operator== (const SharedFontInternal& other) const noexcept
  191. {
  192. return height == other.height
  193. && underline == other.underline
  194. && horizontalScale == other.horizontalScale
  195. && kerning == other.kerning
  196. && typefaceName == other.typefaceName
  197. && typefaceStyle == other.typefaceStyle;
  198. }
  199. Typeface::Ptr typeface;
  200. String typefaceName, typefaceStyle;
  201. float height, horizontalScale = 1.0f, kerning = 0, ascent = 0;
  202. bool underline = false;
  203. };
  204. //==============================================================================
  205. Font::Font() : font (new SharedFontInternal()) {}
  206. Font::Font (const Typeface::Ptr& typeface) : font (new SharedFontInternal (typeface)) {}
  207. Font::Font (const Font& other) noexcept : font (other.font) {}
  208. Font::Font (float fontHeight, int styleFlags)
  209. : font (new SharedFontInternal (styleFlags, FontValues::limitFontHeight (fontHeight)))
  210. {
  211. }
  212. Font::Font (const String& typefaceName, float fontHeight, int styleFlags)
  213. : font (new SharedFontInternal (typefaceName, styleFlags, FontValues::limitFontHeight (fontHeight)))
  214. {
  215. }
  216. Font::Font (const String& typefaceName, const String& typefaceStyle, float fontHeight)
  217. : font (new SharedFontInternal (typefaceName, typefaceStyle, FontValues::limitFontHeight (fontHeight)))
  218. {
  219. }
  220. Font& Font::operator= (const Font& other) noexcept
  221. {
  222. font = other.font;
  223. return *this;
  224. }
  225. Font::Font (Font&& other) noexcept
  226. : font (static_cast<ReferenceCountedObjectPtr<SharedFontInternal>&&> (other.font))
  227. {
  228. }
  229. Font& Font::operator= (Font&& other) noexcept
  230. {
  231. font = static_cast<ReferenceCountedObjectPtr<SharedFontInternal>&&> (other.font);
  232. return *this;
  233. }
  234. Font::~Font() noexcept
  235. {
  236. }
  237. bool Font::operator== (const Font& other) const noexcept
  238. {
  239. return font == other.font
  240. || *font == *other.font;
  241. }
  242. bool Font::operator!= (const Font& other) const noexcept
  243. {
  244. return ! operator== (other);
  245. }
  246. void Font::dupeInternalIfShared()
  247. {
  248. if (font->getReferenceCount() > 1)
  249. font = new SharedFontInternal (*font);
  250. }
  251. void Font::checkTypefaceSuitability()
  252. {
  253. if (font->typeface != nullptr && ! font->typeface->isSuitableForFont (*this))
  254. font->typeface = nullptr;
  255. }
  256. //==============================================================================
  257. struct FontPlaceholderNames
  258. {
  259. String sans { "<Sans-Serif>" },
  260. serif { "<Serif>" },
  261. mono { "<Monospaced>" },
  262. regular { "<Regular>" };
  263. };
  264. const FontPlaceholderNames& getFontPlaceholderNames()
  265. {
  266. static FontPlaceholderNames names;
  267. return names;
  268. }
  269. #if JUCE_MSVC
  270. // This is a workaround for the lack of thread-safety in MSVC's handling of function-local
  271. // statics - if multiple threads all try to create the first Font object at the same time,
  272. // it can cause a race-condition in creating these placeholder strings.
  273. struct FontNamePreloader { FontNamePreloader() { getFontPlaceholderNames(); } };
  274. static FontNamePreloader fnp;
  275. #endif
  276. const String& Font::getDefaultSansSerifFontName() { return getFontPlaceholderNames().sans; }
  277. const String& Font::getDefaultSerifFontName() { return getFontPlaceholderNames().serif; }
  278. const String& Font::getDefaultMonospacedFontName() { return getFontPlaceholderNames().mono; }
  279. const String& Font::getDefaultStyle() { return getFontPlaceholderNames().regular; }
  280. const String& Font::getTypefaceName() const noexcept { return font->typefaceName; }
  281. const String& Font::getTypefaceStyle() const noexcept { return font->typefaceStyle; }
  282. void Font::setTypefaceName (const String& faceName)
  283. {
  284. if (faceName != font->typefaceName)
  285. {
  286. jassert (faceName.isNotEmpty());
  287. dupeInternalIfShared();
  288. font->typefaceName = faceName;
  289. font->typeface = nullptr;
  290. font->ascent = 0;
  291. }
  292. }
  293. void Font::setTypefaceStyle (const String& typefaceStyle)
  294. {
  295. if (typefaceStyle != font->typefaceStyle)
  296. {
  297. dupeInternalIfShared();
  298. font->typefaceStyle = typefaceStyle;
  299. font->typeface = nullptr;
  300. font->ascent = 0;
  301. }
  302. }
  303. Font Font::withTypefaceStyle (const String& newStyle) const
  304. {
  305. Font f (*this);
  306. f.setTypefaceStyle (newStyle);
  307. return f;
  308. }
  309. StringArray Font::getAvailableStyles() const
  310. {
  311. return findAllTypefaceStyles (getTypeface()->getName());
  312. }
  313. Typeface* Font::getTypeface() const
  314. {
  315. if (font->typeface == nullptr)
  316. {
  317. font->typeface = TypefaceCache::getInstance()->findTypefaceFor (*this);
  318. jassert (font->typeface != nullptr);
  319. }
  320. return font->typeface;
  321. }
  322. //==============================================================================
  323. const String& Font::getFallbackFontName()
  324. {
  325. return FontValues::fallbackFont;
  326. }
  327. void Font::setFallbackFontName (const String& name)
  328. {
  329. FontValues::fallbackFont = name;
  330. #if JUCE_MAC || JUCE_IOS
  331. jassertfalse; // Note that use of a fallback font isn't currently implemented in OSX..
  332. #endif
  333. }
  334. const String& Font::getFallbackFontStyle()
  335. {
  336. return FontValues::fallbackFontStyle;
  337. }
  338. void Font::setFallbackFontStyle (const String& style)
  339. {
  340. FontValues::fallbackFontStyle = style;
  341. #if JUCE_MAC || JUCE_IOS
  342. jassertfalse; // Note that use of a fallback font isn't currently implemented in OSX..
  343. #endif
  344. }
  345. //==============================================================================
  346. Font Font::withHeight (const float newHeight) const
  347. {
  348. Font f (*this);
  349. f.setHeight (newHeight);
  350. return f;
  351. }
  352. float Font::getHeightToPointsFactor() const
  353. {
  354. return getTypeface()->getHeightToPointsFactor();
  355. }
  356. Font Font::withPointHeight (float heightInPoints) const
  357. {
  358. Font f (*this);
  359. f.setHeight (heightInPoints / getHeightToPointsFactor());
  360. return f;
  361. }
  362. void Font::setHeight (float newHeight)
  363. {
  364. newHeight = FontValues::limitFontHeight (newHeight);
  365. if (font->height != newHeight)
  366. {
  367. dupeInternalIfShared();
  368. font->height = newHeight;
  369. checkTypefaceSuitability();
  370. }
  371. }
  372. void Font::setHeightWithoutChangingWidth (float newHeight)
  373. {
  374. newHeight = FontValues::limitFontHeight (newHeight);
  375. if (font->height != newHeight)
  376. {
  377. dupeInternalIfShared();
  378. font->horizontalScale *= (font->height / newHeight);
  379. font->height = newHeight;
  380. checkTypefaceSuitability();
  381. }
  382. }
  383. int Font::getStyleFlags() const noexcept
  384. {
  385. int styleFlags = font->underline ? underlined : plain;
  386. if (isBold()) styleFlags |= bold;
  387. if (isItalic()) styleFlags |= italic;
  388. return styleFlags;
  389. }
  390. Font Font::withStyle (const int newFlags) const
  391. {
  392. Font f (*this);
  393. f.setStyleFlags (newFlags);
  394. return f;
  395. }
  396. void Font::setStyleFlags (const int newFlags)
  397. {
  398. if (getStyleFlags() != newFlags)
  399. {
  400. dupeInternalIfShared();
  401. font->typeface = nullptr;
  402. font->typefaceStyle = FontStyleHelpers::getStyleName (newFlags);
  403. font->underline = (newFlags & underlined) != 0;
  404. font->ascent = 0;
  405. }
  406. }
  407. void Font::setSizeAndStyle (float newHeight,
  408. const int newStyleFlags,
  409. const float newHorizontalScale,
  410. const float newKerningAmount)
  411. {
  412. newHeight = FontValues::limitFontHeight (newHeight);
  413. if (font->height != newHeight
  414. || font->horizontalScale != newHorizontalScale
  415. || font->kerning != newKerningAmount)
  416. {
  417. dupeInternalIfShared();
  418. font->height = newHeight;
  419. font->horizontalScale = newHorizontalScale;
  420. font->kerning = newKerningAmount;
  421. checkTypefaceSuitability();
  422. }
  423. setStyleFlags (newStyleFlags);
  424. }
  425. void Font::setSizeAndStyle (float newHeight,
  426. const String& newStyle,
  427. const float newHorizontalScale,
  428. const float newKerningAmount)
  429. {
  430. newHeight = FontValues::limitFontHeight (newHeight);
  431. if (font->height != newHeight
  432. || font->horizontalScale != newHorizontalScale
  433. || font->kerning != newKerningAmount)
  434. {
  435. dupeInternalIfShared();
  436. font->height = newHeight;
  437. font->horizontalScale = newHorizontalScale;
  438. font->kerning = newKerningAmount;
  439. checkTypefaceSuitability();
  440. }
  441. setTypefaceStyle (newStyle);
  442. }
  443. Font Font::withHorizontalScale (const float newHorizontalScale) const
  444. {
  445. Font f (*this);
  446. f.setHorizontalScale (newHorizontalScale);
  447. return f;
  448. }
  449. void Font::setHorizontalScale (const float scaleFactor)
  450. {
  451. dupeInternalIfShared();
  452. font->horizontalScale = scaleFactor;
  453. checkTypefaceSuitability();
  454. }
  455. float Font::getHorizontalScale() const noexcept
  456. {
  457. return font->horizontalScale;
  458. }
  459. float Font::getExtraKerningFactor() const noexcept
  460. {
  461. return font->kerning;
  462. }
  463. Font Font::withExtraKerningFactor (const float extraKerning) const
  464. {
  465. Font f (*this);
  466. f.setExtraKerningFactor (extraKerning);
  467. return f;
  468. }
  469. void Font::setExtraKerningFactor (const float extraKerning)
  470. {
  471. dupeInternalIfShared();
  472. font->kerning = extraKerning;
  473. checkTypefaceSuitability();
  474. }
  475. Font Font::boldened() const { return withStyle (getStyleFlags() | bold); }
  476. Font Font::italicised() const { return withStyle (getStyleFlags() | italic); }
  477. bool Font::isBold() const noexcept { return FontStyleHelpers::isBold (font->typefaceStyle); }
  478. bool Font::isItalic() const noexcept { return FontStyleHelpers::isItalic (font->typefaceStyle); }
  479. bool Font::isUnderlined() const noexcept { return font->underline; }
  480. void Font::setBold (const bool shouldBeBold)
  481. {
  482. auto flags = getStyleFlags();
  483. setStyleFlags (shouldBeBold ? (flags | bold)
  484. : (flags & ~bold));
  485. }
  486. void Font::setItalic (const bool shouldBeItalic)
  487. {
  488. auto flags = getStyleFlags();
  489. setStyleFlags (shouldBeItalic ? (flags | italic)
  490. : (flags & ~italic));
  491. }
  492. void Font::setUnderline (const bool shouldBeUnderlined)
  493. {
  494. dupeInternalIfShared();
  495. font->underline = shouldBeUnderlined;
  496. checkTypefaceSuitability();
  497. }
  498. float Font::getAscent() const
  499. {
  500. if (font->ascent == 0.0f)
  501. font->ascent = getTypeface()->getAscent();
  502. return font->height * font->ascent;
  503. }
  504. float Font::getHeight() const noexcept { return font->height; }
  505. float Font::getDescent() const { return font->height - getAscent(); }
  506. float Font::getHeightInPoints() const { return getHeight() * getHeightToPointsFactor(); }
  507. float Font::getAscentInPoints() const { return getAscent() * getHeightToPointsFactor(); }
  508. float Font::getDescentInPoints() const { return getDescent() * getHeightToPointsFactor(); }
  509. int Font::getStringWidth (const String& text) const
  510. {
  511. return (int) std::ceil (getStringWidthFloat (text));
  512. }
  513. float Font::getStringWidthFloat (const String& text) const
  514. {
  515. // This call isn't thread-safe when there's a message thread running
  516. jassert (MessageManager::getInstanceWithoutCreating() == nullptr
  517. || MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager());
  518. auto w = getTypeface()->getStringWidth (text);
  519. if (font->kerning != 0.0f)
  520. w += font->kerning * text.length();
  521. return w * font->height * font->horizontalScale;
  522. }
  523. void Font::getGlyphPositions (const String& text, Array<int>& glyphs, Array<float>& xOffsets) const
  524. {
  525. // This call isn't thread-safe when there's a message thread running
  526. jassert (MessageManager::getInstanceWithoutCreating() == nullptr
  527. || MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager());
  528. getTypeface()->getGlyphPositions (text, glyphs, xOffsets);
  529. if (auto num = xOffsets.size())
  530. {
  531. auto scale = font->height * font->horizontalScale;
  532. auto* x = xOffsets.getRawDataPointer();
  533. if (font->kerning != 0.0f)
  534. {
  535. for (int i = 0; i < num; ++i)
  536. x[i] = (x[i] + i * font->kerning) * scale;
  537. }
  538. else
  539. {
  540. for (int i = 0; i < num; ++i)
  541. x[i] *= scale;
  542. }
  543. }
  544. }
  545. void Font::findFonts (Array<Font>& destArray)
  546. {
  547. for (auto& name : findAllTypefaceNames())
  548. {
  549. auto styles = findAllTypefaceStyles (name);
  550. String style ("Regular");
  551. if (! styles.contains (style, true))
  552. style = styles[0];
  553. destArray.add (Font (name, style, FontValues::defaultFontHeight));
  554. }
  555. }
  556. //==============================================================================
  557. String Font::toString() const
  558. {
  559. String s;
  560. if (getTypefaceName() != getDefaultSansSerifFontName())
  561. s << getTypefaceName() << "; ";
  562. s << String (getHeight(), 1);
  563. if (getTypefaceStyle() != getDefaultStyle())
  564. s << ' ' << getTypefaceStyle();
  565. return s;
  566. }
  567. Font Font::fromString (const String& fontDescription)
  568. {
  569. const int separator = fontDescription.indexOfChar (';');
  570. String name;
  571. if (separator > 0)
  572. name = fontDescription.substring (0, separator).trim();
  573. if (name.isEmpty())
  574. name = getDefaultSansSerifFontName();
  575. String sizeAndStyle (fontDescription.substring (separator + 1).trimStart());
  576. float height = sizeAndStyle.getFloatValue();
  577. if (height <= 0)
  578. height = 10.0f;
  579. const String style (sizeAndStyle.fromFirstOccurrenceOf (" ", false, false));
  580. return Font (name, style, height);
  581. }
  582. } // namespace juce