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.

507 lines
17KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 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. namespace FontEnumerators
  19. {
  20. static int CALLBACK fontEnum2 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam)
  21. {
  22. if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0)
  23. {
  24. const String fontName (lpelfe->elfLogFont.lfFaceName);
  25. ((StringArray*) lParam)->addIfNotAlreadyThere (fontName.removeCharacters ("@"));
  26. }
  27. return 1;
  28. }
  29. static int CALLBACK fontEnum1 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam)
  30. {
  31. if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0)
  32. {
  33. LOGFONTW lf = { 0 };
  34. lf.lfWeight = FW_DONTCARE;
  35. lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
  36. lf.lfQuality = DEFAULT_QUALITY;
  37. lf.lfCharSet = DEFAULT_CHARSET;
  38. lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  39. lf.lfPitchAndFamily = FF_DONTCARE;
  40. const String fontName (lpelfe->elfLogFont.lfFaceName);
  41. fontName.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
  42. HDC dc = CreateCompatibleDC (0);
  43. EnumFontFamiliesEx (dc, &lf,
  44. (FONTENUMPROCW) &fontEnum2,
  45. lParam, 0);
  46. DeleteDC (dc);
  47. }
  48. return 1;
  49. }
  50. }
  51. StringArray Font::findAllTypefaceNames()
  52. {
  53. StringArray results;
  54. #if JUCE_USE_DIRECTWRITE
  55. const Direct2DFactories& factories = Direct2DFactories::getInstance();
  56. if (factories.systemFonts != nullptr)
  57. {
  58. ComSmartPtr<IDWriteFontFamily> fontFamily;
  59. uint32 fontFamilyCount = 0;
  60. fontFamilyCount = factories.systemFonts->GetFontFamilyCount();
  61. for (uint32 i = 0; i < fontFamilyCount; ++i)
  62. {
  63. HRESULT hr = factories.systemFonts->GetFontFamily (i, fontFamily.resetAndGetPointerAddress());
  64. if (SUCCEEDED (hr))
  65. results.addIfNotAlreadyThere (getFontFamilyName (fontFamily));
  66. }
  67. }
  68. else
  69. #endif
  70. {
  71. HDC dc = CreateCompatibleDC (0);
  72. {
  73. LOGFONTW lf = { 0 };
  74. lf.lfWeight = FW_DONTCARE;
  75. lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
  76. lf.lfQuality = DEFAULT_QUALITY;
  77. lf.lfCharSet = DEFAULT_CHARSET;
  78. lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  79. lf.lfPitchAndFamily = FF_DONTCARE;
  80. EnumFontFamiliesEx (dc, &lf,
  81. (FONTENUMPROCW) &FontEnumerators::fontEnum1,
  82. (LPARAM) &results, 0);
  83. }
  84. DeleteDC (dc);
  85. }
  86. results.sort (true);
  87. return results;
  88. }
  89. StringArray Font::findAllTypefaceStyles (const String& family)
  90. {
  91. if (FontStyleHelpers::isPlaceholderFamilyName (family))
  92. return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family));
  93. StringArray results;
  94. #if JUCE_USE_DIRECTWRITE
  95. const Direct2DFactories& factories = Direct2DFactories::getInstance();
  96. if (factories.systemFonts != nullptr)
  97. {
  98. BOOL fontFound = false;
  99. uint32 fontIndex = 0;
  100. HRESULT hr = factories.systemFonts->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound);
  101. if (! fontFound)
  102. fontIndex = 0;
  103. // Get the font family using the search results
  104. // Fonts like: Times New Roman, Times New Roman Bold, Times New Roman Italic are all in the same font family
  105. ComSmartPtr<IDWriteFontFamily> fontFamily;
  106. hr = factories.systemFonts->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress());
  107. // Get the font faces
  108. ComSmartPtr<IDWriteFont> dwFont;
  109. uint32 fontFacesCount = 0;
  110. fontFacesCount = fontFamily->GetFontCount();
  111. for (uint32 i = 0; i < fontFacesCount; ++i)
  112. {
  113. hr = fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress());
  114. // Ignore any algorithmically generated bold and oblique styles..
  115. if (dwFont->GetSimulations() == DWRITE_FONT_SIMULATIONS_NONE)
  116. results.addIfNotAlreadyThere (getFontFaceName (dwFont));
  117. }
  118. }
  119. else
  120. #endif
  121. {
  122. results.add ("Regular");
  123. results.add ("Italic");
  124. results.add ("Bold");
  125. results.add ("Bold Italic");
  126. }
  127. return results;
  128. }
  129. extern bool juce_isRunningInWine();
  130. struct DefaultFontNames
  131. {
  132. DefaultFontNames()
  133. {
  134. if (juce_isRunningInWine())
  135. {
  136. // If we're running in Wine, then use fonts that might be available on Linux..
  137. defaultSans = "Bitstream Vera Sans";
  138. defaultSerif = "Bitstream Vera Serif";
  139. defaultFixed = "Bitstream Vera Sans Mono";
  140. }
  141. else
  142. {
  143. defaultSans = "Verdana";
  144. defaultSerif = "Times";
  145. defaultFixed = "Lucida Console";
  146. defaultFallback = "Tahoma"; // (contains plenty of unicode characters)
  147. }
  148. }
  149. String defaultSans, defaultSerif, defaultFixed, defaultFallback;
  150. };
  151. Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
  152. {
  153. static DefaultFontNames defaultNames;
  154. Font newFont (font);
  155. const String& faceName = font.getTypefaceName();
  156. if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans);
  157. else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif);
  158. else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed);
  159. if (font.getTypefaceStyle() == getDefaultStyle())
  160. newFont.setTypefaceStyle ("Regular");
  161. return Typeface::createSystemTypefaceFor (newFont);
  162. }
  163. //==============================================================================
  164. class WindowsTypeface : public Typeface
  165. {
  166. public:
  167. WindowsTypeface (const Font& font)
  168. : Typeface (font.getTypefaceName(),
  169. font.getTypefaceStyle()),
  170. fontH (0),
  171. previousFontH (0),
  172. dc (CreateCompatibleDC (0)),
  173. ascent (1.0f), heightToPointsFactor (1.0f),
  174. defaultGlyph (-1)
  175. {
  176. loadFont();
  177. if (GetTextMetrics (dc, &tm))
  178. {
  179. heightToPointsFactor = (72.0f / GetDeviceCaps (dc, LOGPIXELSY)) * heightInPoints / (float) tm.tmHeight;
  180. ascent = tm.tmAscent / (float) tm.tmHeight;
  181. defaultGlyph = getGlyphForChar (dc, tm.tmDefaultChar);
  182. createKerningPairs (dc, (float) tm.tmHeight);
  183. }
  184. }
  185. ~WindowsTypeface()
  186. {
  187. SelectObject (dc, previousFontH); // Replacing the previous font before deleting the DC avoids a warning in BoundsChecker
  188. DeleteDC (dc);
  189. if (fontH != 0)
  190. DeleteObject (fontH);
  191. }
  192. float getAscent() const { return ascent; }
  193. float getDescent() const { return 1.0f - ascent; }
  194. float getHeightToPointsFactor() const { return heightToPointsFactor; }
  195. float getStringWidth (const String& text)
  196. {
  197. const CharPointer_UTF16 utf16 (text.toUTF16());
  198. const size_t numChars = utf16.length();
  199. HeapBlock<int16> results (numChars + 1);
  200. results[numChars] = -1;
  201. float x = 0;
  202. if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast <WORD*> (results.getData()),
  203. GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
  204. {
  205. for (size_t i = 0; i < numChars; ++i)
  206. x += getKerning (dc, results[i], results[i + 1]);
  207. }
  208. return x;
  209. }
  210. void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets)
  211. {
  212. const CharPointer_UTF16 utf16 (text.toUTF16());
  213. const size_t numChars = utf16.length();
  214. HeapBlock<int16> results (numChars + 1);
  215. results[numChars] = -1;
  216. float x = 0;
  217. if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast <WORD*> (results.getData()),
  218. GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
  219. {
  220. resultGlyphs.ensureStorageAllocated ((int) numChars);
  221. xOffsets.ensureStorageAllocated ((int) numChars + 1);
  222. for (size_t i = 0; i < numChars; ++i)
  223. {
  224. resultGlyphs.add (results[i]);
  225. xOffsets.add (x);
  226. x += getKerning (dc, results[i], results[i + 1]);
  227. }
  228. }
  229. xOffsets.add (x);
  230. }
  231. bool getOutlineForGlyph (int glyphNumber, Path& glyphPath)
  232. {
  233. if (glyphNumber < 0)
  234. glyphNumber = defaultGlyph;
  235. GLYPHMETRICS gm;
  236. // (although GetGlyphOutline returns a DWORD, it may be -1 on failure, so treat it as signed int..)
  237. const int bufSize = (int) GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX,
  238. &gm, 0, 0, &identityMatrix);
  239. if (bufSize > 0)
  240. {
  241. HeapBlock<char> data (bufSize);
  242. GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm,
  243. bufSize, data, &identityMatrix);
  244. const TTPOLYGONHEADER* pheader = reinterpret_cast<TTPOLYGONHEADER*> (data.getData());
  245. const float scaleX = 1.0f / tm.tmHeight;
  246. const float scaleY = -scaleX;
  247. while ((char*) pheader < data + bufSize)
  248. {
  249. glyphPath.startNewSubPath (scaleX * pheader->pfxStart.x.value,
  250. scaleY * pheader->pfxStart.y.value);
  251. const TTPOLYCURVE* curve = (const TTPOLYCURVE*) ((const char*) pheader + sizeof (TTPOLYGONHEADER));
  252. const char* const curveEnd = ((const char*) pheader) + pheader->cb;
  253. while ((const char*) curve < curveEnd)
  254. {
  255. if (curve->wType == TT_PRIM_LINE)
  256. {
  257. for (int i = 0; i < curve->cpfx; ++i)
  258. glyphPath.lineTo (scaleX * curve->apfx[i].x.value,
  259. scaleY * curve->apfx[i].y.value);
  260. }
  261. else if (curve->wType == TT_PRIM_QSPLINE)
  262. {
  263. for (int i = 0; i < curve->cpfx - 1; ++i)
  264. {
  265. const float x2 = scaleX * curve->apfx[i].x.value;
  266. const float y2 = scaleY * curve->apfx[i].y.value;
  267. float x3 = scaleX * curve->apfx[i + 1].x.value;
  268. float y3 = scaleY * curve->apfx[i + 1].y.value;
  269. if (i < curve->cpfx - 2)
  270. {
  271. x3 = 0.5f * (x2 + x3);
  272. y3 = 0.5f * (y2 + y3);
  273. }
  274. glyphPath.quadraticTo (x2, y2, x3, y3);
  275. }
  276. }
  277. curve = (const TTPOLYCURVE*) &(curve->apfx [curve->cpfx]);
  278. }
  279. pheader = (const TTPOLYGONHEADER*) curve;
  280. glyphPath.closeSubPath();
  281. }
  282. }
  283. return true;
  284. }
  285. private:
  286. static const MAT2 identityMatrix;
  287. HFONT fontH;
  288. HGDIOBJ previousFontH;
  289. HDC dc;
  290. TEXTMETRIC tm;
  291. float ascent, heightToPointsFactor;
  292. int defaultGlyph, heightInPoints;
  293. struct KerningPair
  294. {
  295. int glyph1, glyph2;
  296. float kerning;
  297. bool operator== (const KerningPair& other) const noexcept
  298. {
  299. return glyph1 == other.glyph1 && glyph2 == other.glyph2;
  300. }
  301. bool operator< (const KerningPair& other) const noexcept
  302. {
  303. return glyph1 < other.glyph1
  304. || (glyph1 == other.glyph1 && glyph2 < other.glyph2);
  305. }
  306. };
  307. SortedSet<KerningPair> kerningPairs;
  308. void loadFont()
  309. {
  310. SetMapperFlags (dc, 0);
  311. SetMapMode (dc, MM_TEXT);
  312. LOGFONTW lf = { 0 };
  313. lf.lfCharSet = DEFAULT_CHARSET;
  314. lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  315. lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
  316. lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
  317. lf.lfQuality = PROOF_QUALITY;
  318. lf.lfItalic = (BYTE) (style == "Italic" ? TRUE : FALSE);
  319. lf.lfWeight = style == "Bold" ? FW_BOLD : FW_NORMAL;
  320. lf.lfHeight = -256;
  321. name.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
  322. HFONT standardSizedFont = CreateFontIndirect (&lf);
  323. if (standardSizedFont != 0)
  324. {
  325. if ((previousFontH = SelectObject (dc, standardSizedFont)) != 0)
  326. {
  327. fontH = standardSizedFont;
  328. OUTLINETEXTMETRIC otm;
  329. if (GetOutlineTextMetrics (dc, sizeof (otm), &otm) != 0)
  330. {
  331. heightInPoints = otm.otmEMSquare;
  332. lf.lfHeight = -(int) heightInPoints;
  333. fontH = CreateFontIndirect (&lf);
  334. SelectObject (dc, fontH);
  335. DeleteObject (standardSizedFont);
  336. }
  337. }
  338. }
  339. }
  340. void createKerningPairs (HDC dc, const float height)
  341. {
  342. HeapBlock<KERNINGPAIR> rawKerning;
  343. const DWORD numKPs = GetKerningPairs (dc, 0, 0);
  344. rawKerning.calloc (numKPs);
  345. GetKerningPairs (dc, numKPs, rawKerning);
  346. kerningPairs.ensureStorageAllocated ((int) numKPs);
  347. for (DWORD i = 0; i < numKPs; ++i)
  348. {
  349. KerningPair kp;
  350. kp.glyph1 = getGlyphForChar (dc, rawKerning[i].wFirst);
  351. kp.glyph2 = getGlyphForChar (dc, rawKerning[i].wSecond);
  352. const int standardWidth = getGlyphWidth (dc, kp.glyph1);
  353. kp.kerning = (standardWidth + rawKerning[i].iKernAmount) / height;
  354. kerningPairs.add (kp);
  355. kp.glyph2 = -1; // add another entry for the standard width version..
  356. kp.kerning = standardWidth / height;
  357. kerningPairs.add (kp);
  358. }
  359. }
  360. static int getGlyphForChar (HDC dc, juce_wchar character)
  361. {
  362. const WCHAR charToTest[] = { (WCHAR) character, 0 };
  363. WORD index = 0;
  364. if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR
  365. || index == 0xffff)
  366. return -1;
  367. return index;
  368. }
  369. static int getGlyphWidth (HDC dc, int glyphNumber)
  370. {
  371. GLYPHMETRICS gm;
  372. gm.gmCellIncX = 0;
  373. GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, 0, &identityMatrix);
  374. return gm.gmCellIncX;
  375. }
  376. float getKerning (HDC dc, const int glyph1, const int glyph2)
  377. {
  378. KerningPair kp;
  379. kp.glyph1 = glyph1;
  380. kp.glyph2 = glyph2;
  381. int index = kerningPairs.indexOf (kp);
  382. if (index < 0)
  383. {
  384. kp.glyph2 = -1;
  385. index = kerningPairs.indexOf (kp);
  386. if (index < 0)
  387. {
  388. kp.glyph2 = -1;
  389. kp.kerning = getGlyphWidth (dc, kp.glyph1) / (float) tm.tmHeight;
  390. kerningPairs.add (kp);
  391. return kp.kerning;
  392. }
  393. }
  394. return kerningPairs.getReference (index).kerning;
  395. }
  396. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTypeface)
  397. };
  398. const MAT2 WindowsTypeface::identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } };
  399. Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
  400. {
  401. #if JUCE_USE_DIRECTWRITE
  402. const Direct2DFactories& factories = Direct2DFactories::getInstance();
  403. if (factories.systemFonts != nullptr)
  404. return new WindowsDirectWriteTypeface (font, factories.systemFonts);
  405. #endif
  406. return new WindowsTypeface (font);
  407. }
  408. void Typeface::scanFolderForFonts (const File&)
  409. {
  410. jassertfalse; // not implemented on this platform
  411. }