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.

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