The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

604 lines
20KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. class FreeTypeFontFace
  19. {
  20. public:
  21. //==============================================================================
  22. enum FontStyle
  23. {
  24. Plain = 0,
  25. Bold = 1,
  26. Italic = 2
  27. };
  28. //==============================================================================
  29. FreeTypeFontFace (const String& familyName)
  30. : hasSerif (false),
  31. monospaced (false)
  32. {
  33. family = familyName;
  34. }
  35. void setFileName (const String& name, const int faceIndex, FontStyle style)
  36. {
  37. if (names [(int) style].fileName.isEmpty())
  38. {
  39. names [(int) style].fileName = name;
  40. names [(int) style].faceIndex = faceIndex;
  41. }
  42. }
  43. const String& getFamilyName() const noexcept { return family; }
  44. const String& getFileName (const int style, int& faceIndex) const noexcept
  45. {
  46. faceIndex = names[style].faceIndex;
  47. return names[style].fileName;
  48. }
  49. void setMonospaced (bool mono) noexcept { monospaced = mono; }
  50. bool getMonospaced() const noexcept { return monospaced; }
  51. void setSerif (const bool serif) noexcept { hasSerif = serif; }
  52. bool getSerif() const noexcept { return hasSerif; }
  53. private:
  54. //==============================================================================
  55. String family;
  56. struct FontNameIndex
  57. {
  58. String fileName;
  59. int faceIndex;
  60. };
  61. FontNameIndex names[4];
  62. bool hasSerif, monospaced;
  63. };
  64. //==============================================================================
  65. class LinuxFontFileIterator
  66. {
  67. public:
  68. LinuxFontFileIterator()
  69. : index (0)
  70. {
  71. fontDirs.addTokens (CharPointer_UTF8 (getenv ("JUCE_FONT_PATH")), ";,", String::empty);
  72. fontDirs.removeEmptyStrings (true);
  73. if (fontDirs.size() == 0)
  74. {
  75. const ScopedPointer<XmlElement> fontsInfo (XmlDocument::parse (File ("/etc/fonts/fonts.conf")));
  76. if (fontsInfo != nullptr)
  77. {
  78. forEachXmlChildElementWithTagName (*fontsInfo, e, "dir")
  79. {
  80. fontDirs.add (e->getAllSubText().trim());
  81. }
  82. }
  83. }
  84. if (fontDirs.size() == 0)
  85. fontDirs.add ("/usr/X11R6/lib/X11/fonts");
  86. fontDirs.removeEmptyStrings (true);
  87. }
  88. bool next()
  89. {
  90. if (iter != nullptr)
  91. {
  92. while (iter->next())
  93. if (getFile().hasFileExtension ("ttf;pfb;pcf"))
  94. return true;
  95. }
  96. if (index >= fontDirs.size())
  97. return false;
  98. iter = new DirectoryIterator (fontDirs [index++], true);
  99. return next();
  100. }
  101. File getFile() const { jassert (iter != nullptr); return iter->getFile(); }
  102. private:
  103. StringArray fontDirs;
  104. int index;
  105. ScopedPointer<DirectoryIterator> iter;
  106. };
  107. //==============================================================================
  108. class FreeTypeInterface : public DeletedAtShutdown
  109. {
  110. public:
  111. //==============================================================================
  112. FreeTypeInterface()
  113. : ftLib (0),
  114. lastFace (0),
  115. lastBold (false),
  116. lastItalic (false)
  117. {
  118. if (FT_Init_FreeType (&ftLib) != 0)
  119. {
  120. ftLib = 0;
  121. DBG ("Failed to initialize FreeType");
  122. }
  123. LinuxFontFileIterator fontFileIterator;
  124. while (fontFileIterator.next())
  125. {
  126. FT_Face face;
  127. int faceIndex = 0;
  128. int numFaces = 0;
  129. do
  130. {
  131. if (FT_New_Face (ftLib, fontFileIterator.getFile().getFullPathName().toUTF8(),
  132. faceIndex, &face) == 0)
  133. {
  134. if (faceIndex == 0)
  135. numFaces = face->num_faces;
  136. if ((face->face_flags & FT_FACE_FLAG_SCALABLE) != 0)
  137. {
  138. FreeTypeFontFace* const newFace = findOrCreate (face->family_name, true);
  139. int style = (int) FreeTypeFontFace::Plain;
  140. if ((face->style_flags & FT_STYLE_FLAG_BOLD) != 0)
  141. style |= (int) FreeTypeFontFace::Bold;
  142. if ((face->style_flags & FT_STYLE_FLAG_ITALIC) != 0)
  143. style |= (int) FreeTypeFontFace::Italic;
  144. newFace->setFileName (fontFileIterator.getFile().getFullPathName(),
  145. faceIndex, (FreeTypeFontFace::FontStyle) style);
  146. newFace->setMonospaced ((face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0);
  147. // Surely there must be a better way to do this?
  148. const String name (face->family_name);
  149. newFace->setSerif (! (name.containsIgnoreCase ("Sans")
  150. || name.containsIgnoreCase ("Verdana")
  151. || name.containsIgnoreCase ("Arial")));
  152. //DBG (fontFileIterator.getFile().getFullPathName() << " - " << name);
  153. }
  154. FT_Done_Face (face);
  155. }
  156. ++faceIndex;
  157. }
  158. while (faceIndex < numFaces);
  159. }
  160. }
  161. ~FreeTypeInterface()
  162. {
  163. if (lastFace != 0)
  164. FT_Done_Face (lastFace);
  165. if (ftLib != 0)
  166. FT_Done_FreeType (ftLib);
  167. clearSingletonInstance();
  168. }
  169. //==============================================================================
  170. FreeTypeFontFace* findOrCreate (const String& familyName, const bool create = false)
  171. {
  172. for (int i = 0; i < faces.size(); i++)
  173. if (faces[i]->getFamilyName() == familyName)
  174. return faces[i];
  175. if (! create)
  176. return nullptr;
  177. FreeTypeFontFace* newFace = new FreeTypeFontFace (familyName);
  178. faces.add (newFace);
  179. return newFace;
  180. }
  181. // Create a FreeType face object for a given font
  182. FT_Face createFT_Face (const String& fontName, const bool bold, const bool italic)
  183. {
  184. FT_Face face = 0;
  185. if (fontName == lastFontName && bold == lastBold && italic == lastItalic)
  186. {
  187. face = lastFace;
  188. }
  189. else
  190. {
  191. if (lastFace != 0)
  192. {
  193. FT_Done_Face (lastFace);
  194. lastFace = 0;
  195. }
  196. lastFontName = fontName;
  197. lastBold = bold;
  198. lastItalic = italic;
  199. FreeTypeFontFace* const ftFace = findOrCreate (fontName);
  200. if (ftFace != 0)
  201. {
  202. int style = (int) FreeTypeFontFace::Plain;
  203. if (bold)
  204. style |= (int) FreeTypeFontFace::Bold;
  205. if (italic)
  206. style |= (int) FreeTypeFontFace::Italic;
  207. int faceIndex;
  208. String fileName (ftFace->getFileName (style, faceIndex));
  209. if (fileName.isEmpty())
  210. {
  211. style ^= (int) FreeTypeFontFace::Bold;
  212. fileName = ftFace->getFileName (style, faceIndex);
  213. if (fileName.isEmpty())
  214. {
  215. style ^= (int) FreeTypeFontFace::Bold;
  216. style ^= (int) FreeTypeFontFace::Italic;
  217. fileName = ftFace->getFileName (style, faceIndex);
  218. if (! fileName.length())
  219. {
  220. style ^= (int) FreeTypeFontFace::Bold;
  221. fileName = ftFace->getFileName (style, faceIndex);
  222. }
  223. }
  224. }
  225. if (! FT_New_Face (ftLib, fileName.toUTF8(), faceIndex, &lastFace))
  226. {
  227. face = lastFace;
  228. // If there isn't a unicode charmap then select the first one.
  229. if (FT_Select_Charmap (face, ft_encoding_unicode))
  230. FT_Set_Charmap (face, face->charmaps[0]);
  231. }
  232. }
  233. }
  234. return face;
  235. }
  236. bool addGlyph (FT_Face face, CustomTypeface& dest, uint32 character)
  237. {
  238. const unsigned int glyphIndex = FT_Get_Char_Index (face, character);
  239. const float height = (float) (face->ascender - face->descender);
  240. const float scaleX = 1.0f / height;
  241. const float scaleY = -1.0f / height;
  242. Path destShape;
  243. if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM) != 0
  244. || face->glyph->format != ft_glyph_format_outline)
  245. {
  246. return false;
  247. }
  248. const FT_Outline* const outline = &face->glyph->outline;
  249. const short* const contours = outline->contours;
  250. const char* const tags = outline->tags;
  251. FT_Vector* const points = outline->points;
  252. for (int c = 0; c < outline->n_contours; c++)
  253. {
  254. const int startPoint = (c == 0) ? 0 : contours [c - 1] + 1;
  255. const int endPoint = contours[c];
  256. for (int p = startPoint; p <= endPoint; p++)
  257. {
  258. const float x = scaleX * points[p].x;
  259. const float y = scaleY * points[p].y;
  260. if (p == startPoint)
  261. {
  262. if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic)
  263. {
  264. float x2 = scaleX * points [endPoint].x;
  265. float y2 = scaleY * points [endPoint].y;
  266. if (FT_CURVE_TAG (tags[endPoint]) != FT_Curve_Tag_On)
  267. {
  268. x2 = (x + x2) * 0.5f;
  269. y2 = (y + y2) * 0.5f;
  270. }
  271. destShape.startNewSubPath (x2, y2);
  272. }
  273. else
  274. {
  275. destShape.startNewSubPath (x, y);
  276. }
  277. }
  278. if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_On)
  279. {
  280. if (p != startPoint)
  281. destShape.lineTo (x, y);
  282. }
  283. else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic)
  284. {
  285. const int nextIndex = (p == endPoint) ? startPoint : p + 1;
  286. float x2 = scaleX * points [nextIndex].x;
  287. float y2 = scaleY * points [nextIndex].y;
  288. if (FT_CURVE_TAG (tags [nextIndex]) == FT_Curve_Tag_Conic)
  289. {
  290. x2 = (x + x2) * 0.5f;
  291. y2 = (y + y2) * 0.5f;
  292. }
  293. else
  294. {
  295. ++p;
  296. }
  297. destShape.quadraticTo (x, y, x2, y2);
  298. }
  299. else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Cubic)
  300. {
  301. if (p >= endPoint)
  302. return false;
  303. const int next1 = p + 1;
  304. const int next2 = (p == (endPoint - 1)) ? startPoint : p + 2;
  305. const float x2 = scaleX * points [next1].x;
  306. const float y2 = scaleY * points [next1].y;
  307. const float x3 = scaleX * points [next2].x;
  308. const float y3 = scaleY * points [next2].y;
  309. if (FT_CURVE_TAG (tags[next1]) != FT_Curve_Tag_Cubic
  310. || FT_CURVE_TAG (tags[next2]) != FT_Curve_Tag_On)
  311. return false;
  312. destShape.cubicTo (x, y, x2, y2, x3, y3);
  313. p += 2;
  314. }
  315. }
  316. destShape.closeSubPath();
  317. }
  318. dest.addGlyph (character, destShape, face->glyph->metrics.horiAdvance / height);
  319. if ((face->face_flags & FT_FACE_FLAG_KERNING) != 0)
  320. addKerning (face, dest, character, glyphIndex);
  321. return true;
  322. }
  323. void addKerning (FT_Face face, CustomTypeface& dest, const uint32 character, const uint32 glyphIndex)
  324. {
  325. const float height = (float) (face->ascender - face->descender);
  326. uint32 rightGlyphIndex;
  327. uint32 rightCharCode = FT_Get_First_Char (face, &rightGlyphIndex);
  328. while (rightGlyphIndex != 0)
  329. {
  330. FT_Vector kerning;
  331. if (FT_Get_Kerning (face, glyphIndex, rightGlyphIndex, ft_kerning_unscaled, &kerning) == 0)
  332. {
  333. if (kerning.x != 0)
  334. dest.addKerningPair (character, rightCharCode, kerning.x / height);
  335. }
  336. rightCharCode = FT_Get_Next_Char (face, rightCharCode, &rightGlyphIndex);
  337. }
  338. }
  339. // Add a glyph to a font
  340. bool addGlyphToFont (const uint32 character, const String& fontName,
  341. bool bold, bool italic, CustomTypeface& dest)
  342. {
  343. FT_Face face = createFT_Face (fontName, bold, italic);
  344. return face != 0 && addGlyph (face, dest, character);
  345. }
  346. //==============================================================================
  347. void getFamilyNames (StringArray& familyNames) const
  348. {
  349. for (int i = 0; i < faces.size(); i++)
  350. familyNames.add (faces[i]->getFamilyName());
  351. }
  352. void getMonospacedNames (StringArray& monoSpaced) const
  353. {
  354. for (int i = 0; i < faces.size(); i++)
  355. if (faces[i]->getMonospaced())
  356. monoSpaced.add (faces[i]->getFamilyName());
  357. }
  358. void getSerifNames (StringArray& serif) const
  359. {
  360. for (int i = 0; i < faces.size(); i++)
  361. if (faces[i]->getSerif())
  362. serif.add (faces[i]->getFamilyName());
  363. }
  364. void getSansSerifNames (StringArray& sansSerif) const
  365. {
  366. for (int i = 0; i < faces.size(); i++)
  367. if (! faces[i]->getSerif())
  368. sansSerif.add (faces[i]->getFamilyName());
  369. }
  370. juce_DeclareSingleton_SingleThreaded_Minimal (FreeTypeInterface);
  371. private:
  372. //==============================================================================
  373. FT_Library ftLib;
  374. FT_Face lastFace;
  375. String lastFontName;
  376. bool lastBold, lastItalic;
  377. OwnedArray<FreeTypeFontFace> faces;
  378. };
  379. juce_ImplementSingleton_SingleThreaded (FreeTypeInterface)
  380. //==============================================================================
  381. class FreetypeTypeface : public CustomTypeface
  382. {
  383. public:
  384. FreetypeTypeface (const Font& font)
  385. {
  386. FT_Face face = FreeTypeInterface::getInstance()
  387. ->createFT_Face (font.getTypefaceName(), font.isBold(), font.isItalic());
  388. if (face == 0)
  389. {
  390. #if JUCE_DEBUG
  391. String msg ("Failed to create typeface: ");
  392. msg << font.getTypefaceName() << " " << (font.isBold() ? 'B' : ' ') << (font.isItalic() ? 'I' : ' ');
  393. DBG (msg);
  394. #endif
  395. }
  396. else
  397. {
  398. setCharacteristics (font.getTypefaceName(),
  399. face->ascender / (float) (face->ascender - face->descender),
  400. font.isBold(), font.isItalic(),
  401. L' ');
  402. }
  403. }
  404. bool loadGlyphIfPossible (juce_wchar character)
  405. {
  406. return FreeTypeInterface::getInstance()
  407. ->addGlyphToFont (character, name, isBold, isItalic, *this);
  408. }
  409. };
  410. Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
  411. {
  412. return new FreetypeTypeface (font);
  413. }
  414. //==============================================================================
  415. StringArray Font::findAllTypefaceNames()
  416. {
  417. StringArray s;
  418. FreeTypeInterface::getInstance()->getFamilyNames (s);
  419. s.sort (true);
  420. return s;
  421. }
  422. namespace LinuxFontHelpers
  423. {
  424. String pickBestFont (const StringArray& names,
  425. const char* const* choicesString)
  426. {
  427. const StringArray choices (choicesString);
  428. int i, j;
  429. for (j = 0; j < choices.size(); ++j)
  430. if (names.contains (choices[j], true))
  431. return choices[j];
  432. for (j = 0; j < choices.size(); ++j)
  433. for (i = 0; i < names.size(); i++)
  434. if (names[i].startsWithIgnoreCase (choices[j]))
  435. return names[i];
  436. for (j = 0; j < choices.size(); ++j)
  437. for (i = 0; i < names.size(); i++)
  438. if (names[i].containsIgnoreCase (choices[j]))
  439. return names[i];
  440. return names[0];
  441. }
  442. String getDefaultSansSerifFontName()
  443. {
  444. StringArray allFonts;
  445. FreeTypeInterface::getInstance()->getSansSerifNames (allFonts);
  446. const char* targets[] = { "Verdana", "Bitstream Vera Sans", "Luxi Sans", "Sans", 0 };
  447. return pickBestFont (allFonts, targets);
  448. }
  449. String getDefaultSerifFontName()
  450. {
  451. StringArray allFonts;
  452. FreeTypeInterface::getInstance()->getSerifNames (allFonts);
  453. const char* targets[] = { "Bitstream Vera Serif", "Times", "Nimbus Roman", "Serif", 0 };
  454. return pickBestFont (allFonts, targets);
  455. }
  456. String getDefaultMonospacedFontName()
  457. {
  458. StringArray allFonts;
  459. FreeTypeInterface::getInstance()->getMonospacedNames (allFonts);
  460. const char* targets[] = { "Bitstream Vera Sans Mono", "Courier", "Sans Mono", "Mono", 0 };
  461. return pickBestFont (allFonts, targets);
  462. }
  463. }
  464. struct DefaultFontNames
  465. {
  466. DefaultFontNames()
  467. : defaultSans (LinuxFontHelpers::getDefaultSansSerifFontName()),
  468. defaultSerif (LinuxFontHelpers::getDefaultSerifFontName()),
  469. defaultFixed (LinuxFontHelpers::getDefaultMonospacedFontName())
  470. {
  471. }
  472. String defaultSans, defaultSerif, defaultFixed;
  473. };
  474. Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
  475. {
  476. static DefaultFontNames defaultNames;
  477. String faceName (font.getTypefaceName());
  478. if (faceName == Font::getDefaultSansSerifFontName()) faceName = defaultNames.defaultSans;
  479. else if (faceName == Font::getDefaultSerifFontName()) faceName = defaultNames.defaultSerif;
  480. else if (faceName == Font::getDefaultMonospacedFontName()) faceName = defaultNames.defaultFixed;
  481. Font f (font);
  482. f.setTypefaceName (faceName);
  483. return Typeface::createSystemTypefaceFor (f);
  484. }