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.

544 lines
18KB

  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 "../../../src/juce_core/basics/juce_StandardHeader.h"
  24. #include <ApplicationServices/ApplicationServices.h>
  25. BEGIN_JUCE_NAMESPACE
  26. #include "../../../src/juce_appframework/gui/graphics/fonts/juce_Font.h"
  27. #include "../../../src/juce_appframework/events/juce_Timer.h"
  28. #include "../../../src/juce_appframework/events/juce_MessageManager.h"
  29. #include "../../../src/juce_appframework/application/juce_DeletedAtShutdown.h"
  30. #include "../../../src/juce_core/basics/juce_Singleton.h"
  31. #include "../../../src/juce_core/misc/juce_PlatformUtilities.h"
  32. //==============================================================================
  33. static OSStatus pascal CubicMoveTo (const Float32Point *pt,
  34. void* callBackDataPtr)
  35. {
  36. Path* const p = (Path*) callBackDataPtr;
  37. p->startNewSubPath (pt->x, pt->y);
  38. return noErr;
  39. }
  40. static OSStatus pascal CubicLineTo (const Float32Point *pt,
  41. void* callBackDataPtr)
  42. {
  43. Path* const p = (Path*) callBackDataPtr;
  44. p->lineTo (pt->x, pt->y);
  45. return noErr;
  46. }
  47. static OSStatus pascal CubicCurveTo (const Float32Point *pt1,
  48. const Float32Point *pt2,
  49. const Float32Point *pt3,
  50. void* callBackDataPtr)
  51. {
  52. Path* const p = (Path*) callBackDataPtr;
  53. p->cubicTo (pt1->x, pt1->y,
  54. pt2->x, pt2->y,
  55. pt3->x, pt3->y);
  56. return noErr;
  57. }
  58. static OSStatus pascal CubicClosePath (void* callBackDataPtr)
  59. {
  60. Path* const p = (Path*) callBackDataPtr;
  61. p->closeSubPath();
  62. return noErr;
  63. }
  64. //==============================================================================
  65. class ATSFontHelper
  66. {
  67. ATSUFontID fontId;
  68. ATSUStyle style;
  69. ATSCubicMoveToUPP moveToProc;
  70. ATSCubicLineToUPP lineToProc;
  71. ATSCubicCurveToUPP curveToProc;
  72. ATSCubicClosePathUPP closePathProc;
  73. float totalSize, ascent;
  74. TextToUnicodeInfo encodingInfo;
  75. public:
  76. String name;
  77. bool isBold, isItalic;
  78. float fontSize;
  79. int refCount;
  80. ATSFontHelper (const String& name_,
  81. const bool bold_,
  82. const bool italic_,
  83. const float size_)
  84. : fontId (0),
  85. name (name_),
  86. isBold (bold_),
  87. isItalic (italic_),
  88. fontSize (size_),
  89. refCount (1)
  90. {
  91. const char* const nameUtf8 = name_.toUTF8();
  92. ATSUFindFontFromName (const_cast <char*> (nameUtf8),
  93. strlen (nameUtf8),
  94. kFontFullName,
  95. kFontNoPlatformCode,
  96. kFontNoScriptCode,
  97. kFontNoLanguageCode,
  98. &fontId);
  99. ATSUCreateStyle (&style);
  100. ATSUAttributeTag attTypes[] = { kATSUFontTag,
  101. kATSUQDBoldfaceTag,
  102. kATSUQDItalicTag,
  103. kATSUSizeTag };
  104. ByteCount attSizes[] = { sizeof (ATSUFontID),
  105. sizeof (Boolean),
  106. sizeof (Boolean),
  107. sizeof (Fixed) };
  108. Boolean bold = bold_, italic = italic_;
  109. Fixed size = X2Fix (size_);
  110. ATSUAttributeValuePtr attValues[] = { &fontId,
  111. &bold,
  112. &italic,
  113. &size };
  114. ATSUSetAttributes (style, 4, attTypes, attSizes, attValues);
  115. moveToProc = NewATSCubicMoveToUPP (CubicMoveTo);
  116. lineToProc = NewATSCubicLineToUPP (CubicLineTo);
  117. curveToProc = NewATSCubicCurveToUPP (CubicCurveTo);
  118. closePathProc = NewATSCubicClosePathUPP (CubicClosePath);
  119. ascent = 0.0f;
  120. float kern, descent = 0.0f;
  121. getPathAndKerning (T('N'), T('O'), 0, kern, &ascent, &descent);
  122. totalSize = ascent + descent;
  123. }
  124. ~ATSFontHelper()
  125. {
  126. ATSUDisposeStyle (style);
  127. DisposeATSCubicMoveToUPP (moveToProc);
  128. DisposeATSCubicLineToUPP (lineToProc);
  129. DisposeATSCubicCurveToUPP (curveToProc);
  130. DisposeATSCubicClosePathUPP (closePathProc);
  131. }
  132. bool getPathAndKerning (const juce_wchar char1,
  133. const juce_wchar char2,
  134. Path* path,
  135. float& kerning,
  136. float* ascent,
  137. float* descent)
  138. {
  139. bool ok = false;
  140. UniChar buffer[4];
  141. buffer[0] = T(' ');
  142. buffer[1] = char1;
  143. buffer[2] = char2;
  144. buffer[3] = 0;
  145. UniCharCount count = kATSUToTextEnd;
  146. ATSUTextLayout layout;
  147. OSStatus err = ATSUCreateTextLayoutWithTextPtr (buffer,
  148. 0,
  149. 2,
  150. 2,
  151. 1,
  152. &count,
  153. &style,
  154. &layout);
  155. if (err == noErr)
  156. {
  157. ATSUSetTransientFontMatching (layout, true);
  158. ATSLayoutRecord* layoutRecords;
  159. ItemCount numRecords;
  160. Fixed* deltaYs;
  161. ItemCount numDeltaYs;
  162. ATSUDirectGetLayoutDataArrayPtrFromTextLayout (layout,
  163. 0,
  164. kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
  165. (void**) &layoutRecords,
  166. &numRecords);
  167. ATSUDirectGetLayoutDataArrayPtrFromTextLayout (layout,
  168. 0,
  169. kATSUDirectDataBaselineDeltaFixedArray,
  170. (void**) &deltaYs,
  171. &numDeltaYs);
  172. if (numRecords > 2)
  173. {
  174. kerning = (float) (Fix2X (layoutRecords[2].realPos)
  175. - Fix2X (layoutRecords[1].realPos));
  176. if (ascent != 0)
  177. {
  178. ATSUTextMeasurement asc;
  179. ByteCount actualSize;
  180. ATSUGetLineControl (layout,
  181. 0,
  182. kATSULineAscentTag,
  183. sizeof (ATSUTextMeasurement),
  184. &asc,
  185. &actualSize);
  186. *ascent = (float) Fix2X (asc);
  187. }
  188. if (descent != 0)
  189. {
  190. ATSUTextMeasurement desc;
  191. ByteCount actualSize;
  192. ATSUGetLineControl (layout,
  193. 0,
  194. kATSULineDescentTag,
  195. sizeof (ATSUTextMeasurement),
  196. &desc,
  197. &actualSize);
  198. *descent = (float) Fix2X (desc);
  199. }
  200. if (path != 0)
  201. {
  202. OSStatus callbackResult;
  203. ok = (ATSUGlyphGetCubicPaths (style,
  204. layoutRecords[1].glyphID,
  205. moveToProc,
  206. lineToProc,
  207. curveToProc,
  208. closePathProc,
  209. (void*) path,
  210. &callbackResult) == noErr);
  211. if (numDeltaYs > 0 && ok)
  212. {
  213. const float dy = (float) Fix2X (deltaYs[1]);
  214. path->applyTransform (AffineTransform::translation (0.0f, dy));
  215. }
  216. }
  217. else
  218. {
  219. ok = true;
  220. }
  221. }
  222. if (deltaYs != 0)
  223. ATSUDirectReleaseLayoutDataArrayPtr (0, kATSUDirectDataBaselineDeltaFixedArray,
  224. (void**) &deltaYs);
  225. if (layoutRecords != 0)
  226. ATSUDirectReleaseLayoutDataArrayPtr (0, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
  227. (void**) &layoutRecords);
  228. ATSUDisposeTextLayout (layout);
  229. }
  230. return kerning;
  231. }
  232. float getAscent()
  233. {
  234. return ascent;
  235. }
  236. float getTotalHeight()
  237. {
  238. return totalSize;
  239. }
  240. juce_wchar getDefaultChar()
  241. {
  242. return 0;
  243. }
  244. };
  245. //==============================================================================
  246. class ATSFontHelperCache : public Timer,
  247. public DeletedAtShutdown
  248. {
  249. VoidArray cache;
  250. public:
  251. ATSFontHelperCache()
  252. {
  253. }
  254. ~ATSFontHelperCache()
  255. {
  256. for (int i = cache.size(); --i >= 0;)
  257. {
  258. ATSFontHelper* const f = (ATSFontHelper*) cache.getUnchecked(i);
  259. delete f;
  260. }
  261. clearSingletonInstance();
  262. }
  263. ATSFontHelper* getFont (const String& name,
  264. const bool bold,
  265. const bool italic,
  266. const float size = 1024)
  267. {
  268. for (int i = cache.size(); --i >= 0;)
  269. {
  270. ATSFontHelper* const f = (ATSFontHelper*) cache.getUnchecked(i);
  271. if (f->name == name
  272. && f->isBold == bold
  273. && f->isItalic == italic
  274. && f->fontSize == size)
  275. {
  276. f->refCount++;
  277. return f;
  278. }
  279. }
  280. ATSFontHelper* const f = new ATSFontHelper (name, bold, italic, size);
  281. cache.add (f);
  282. return f;
  283. }
  284. void releaseFont (ATSFontHelper* f)
  285. {
  286. for (int i = cache.size(); --i >= 0;)
  287. {
  288. ATSFontHelper* const f2 = (ATSFontHelper*) cache.getUnchecked(i);
  289. if (f == f2)
  290. {
  291. f->refCount--;
  292. if (f->refCount == 0)
  293. startTimer (5000);
  294. break;
  295. }
  296. }
  297. }
  298. void timerCallback()
  299. {
  300. stopTimer();
  301. for (int i = cache.size(); --i >= 0;)
  302. {
  303. ATSFontHelper* const f = (ATSFontHelper*) cache.getUnchecked(i);
  304. if (f->refCount == 0)
  305. {
  306. cache.remove (i);
  307. delete f;
  308. }
  309. }
  310. if (cache.size() == 0)
  311. delete this;
  312. }
  313. juce_DeclareSingleton_SingleThreaded_Minimal (ATSFontHelperCache)
  314. };
  315. juce_ImplementSingleton_SingleThreaded (ATSFontHelperCache)
  316. //==============================================================================
  317. void Typeface::initialiseTypefaceCharacteristics (const String& fontName,
  318. bool bold,
  319. bool italic,
  320. bool addAllGlyphsToFont) throw()
  321. {
  322. // This method is only safe to be called from the normal UI thread..
  323. jassert (MessageManager::getInstance()->isThisTheMessageThread());
  324. ATSFontHelper* const helper = ATSFontHelperCache::getInstance()
  325. ->getFont (fontName, bold, italic);
  326. clear();
  327. setAscent (helper->getAscent() / helper->getTotalHeight());
  328. setName (fontName);
  329. setDefaultCharacter (helper->getDefaultChar());
  330. setBold (bold);
  331. setItalic (italic);
  332. if (addAllGlyphsToFont)
  333. {
  334. //xxx
  335. jassertfalse
  336. }
  337. ATSFontHelperCache::getInstance()->releaseFont (helper);
  338. }
  339. bool Typeface::findAndAddSystemGlyph (juce_wchar character) throw()
  340. {
  341. // This method is only safe to be called from the normal UI thread..
  342. jassert (MessageManager::getInstance()->isThisTheMessageThread());
  343. ATSFontHelper* const helper = ATSFontHelperCache::getInstance()
  344. ->getFont (getName(), isBold(), isItalic());
  345. Path path;
  346. float width;
  347. bool foundOne = false;
  348. if (helper->getPathAndKerning (character, T('I'), &path, width, 0, 0))
  349. {
  350. path.applyTransform (AffineTransform::scale (1.0f / helper->getTotalHeight(),
  351. 1.0f / helper->getTotalHeight()));
  352. addGlyph (character, path, width / helper->getTotalHeight());
  353. for (int i = 0; i < glyphs.size(); ++i)
  354. {
  355. const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i);
  356. float kerning;
  357. if (helper->getPathAndKerning (character, g->getCharacter(), 0, kerning, 0, 0))
  358. {
  359. kerning = (kerning - width) / helper->getTotalHeight();
  360. if (kerning != 0)
  361. addKerningPair (character, g->getCharacter(), kerning);
  362. }
  363. if (helper->getPathAndKerning (g->getCharacter(), character, 0, kerning, 0, 0))
  364. {
  365. kerning = kerning / helper->getTotalHeight() - g->width;
  366. if (kerning != 0)
  367. addKerningPair (g->getCharacter(), character, kerning);
  368. }
  369. }
  370. foundOne = true;
  371. }
  372. ATSFontHelperCache::getInstance()->releaseFont (helper);
  373. return foundOne;
  374. }
  375. const StringArray Font::findAllTypefaceNames() throw()
  376. {
  377. StringArray names;
  378. ATSFontIterator iter;
  379. if (ATSFontIteratorCreate (kATSFontContextGlobal,
  380. 0,
  381. 0,
  382. kATSOptionFlagsRestrictedScope,
  383. &iter) == noErr)
  384. {
  385. ATSFontRef font;
  386. while (ATSFontIteratorNext (iter, &font) == noErr)
  387. {
  388. CFStringRef name;
  389. if (ATSFontGetName (font,
  390. kATSOptionFlagsDefault,
  391. &name) == noErr)
  392. {
  393. const String nm (PlatformUtilities::cfStringToJuceString (name));
  394. if (nm.isNotEmpty())
  395. names.add (nm);
  396. CFRelease (name);
  397. }
  398. }
  399. ATSFontIteratorRelease (&iter);
  400. }
  401. // Use some totuous logic to eliminate bold/italic versions of fonts that we've already got
  402. // a plain version of. This is only necessary because of Carbon's total lack of support
  403. // for dealing with font families...
  404. for (int j = names.size(); --j >= 0;)
  405. {
  406. const char* const endings[] = { " bold", " italic", " bold italic", " bolditalic",
  407. " oblque", " bold oblique", " boldoblique" };
  408. for (int i = 0; i < numElementsInArray (endings); ++i)
  409. {
  410. const String ending (endings[i]);
  411. if (names[j].endsWithIgnoreCase (ending))
  412. {
  413. const String root (names[j].dropLastCharacters (ending.length()).trimEnd());
  414. if (names.contains (root)
  415. || names.contains (root + T(" plain"), true))
  416. {
  417. names.remove (j);
  418. break;
  419. }
  420. }
  421. }
  422. }
  423. names.sort (true);
  424. return names;
  425. }
  426. void Font::getDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw()
  427. {
  428. defaultSans = "Lucida Grande";
  429. defaultSerif = "Times New Roman";
  430. defaultFixed = "Monaco";
  431. }
  432. END_JUCE_NAMESPACE