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.

618 lines
20KB

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