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.

627 lines
21KB

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