Audio plugin host https://kx.studio/carla
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.

juce_win32_Fonts.cpp 17KB

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