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.

530 lines
17KB

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