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