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.

624 lines
20KB

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