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.

428 lines
14KB

  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. 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. 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. HDC dc = CreateCompatibleDC (0);
  55. {
  56. LOGFONTW lf = { 0 };
  57. lf.lfWeight = FW_DONTCARE;
  58. lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
  59. lf.lfQuality = DEFAULT_QUALITY;
  60. lf.lfCharSet = DEFAULT_CHARSET;
  61. lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  62. lf.lfPitchAndFamily = FF_DONTCARE;
  63. EnumFontFamiliesEx (dc, &lf,
  64. (FONTENUMPROCW) &FontEnumerators::fontEnum1,
  65. (LPARAM) &results, 0);
  66. }
  67. DeleteDC (dc);
  68. results.sort (true);
  69. return results;
  70. }
  71. extern bool juce_IsRunningInWine();
  72. struct DefaultFontNames
  73. {
  74. DefaultFontNames()
  75. {
  76. if (juce_IsRunningInWine())
  77. {
  78. // If we're running in Wine, then use fonts that might be available on Linux..
  79. defaultSans = "Bitstream Vera Sans";
  80. defaultSerif = "Bitstream Vera Serif";
  81. defaultFixed = "Bitstream Vera Sans Mono";
  82. }
  83. else
  84. {
  85. defaultSans = "Verdana";
  86. defaultSerif = "Times";
  87. defaultFixed = "Lucida Console";
  88. defaultFallback = "Tahoma"; // (contains plenty of unicode characters)
  89. }
  90. }
  91. String defaultSans, defaultSerif, defaultFixed, defaultFallback;
  92. };
  93. Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
  94. {
  95. static DefaultFontNames defaultNames;
  96. String faceName (font.getTypefaceName());
  97. if (faceName == Font::getDefaultSansSerifFontName()) faceName = defaultNames.defaultSans;
  98. else if (faceName == Font::getDefaultSerifFontName()) faceName = defaultNames.defaultSerif;
  99. else if (faceName == Font::getDefaultMonospacedFontName()) faceName = defaultNames.defaultFixed;
  100. Font f (font);
  101. f.setTypefaceName (faceName);
  102. return Typeface::createSystemTypefaceFor (f);
  103. }
  104. //==============================================================================
  105. class WindowsTypeface : public Typeface
  106. {
  107. public:
  108. WindowsTypeface (const Font& font)
  109. : Typeface (font.getTypefaceName()),
  110. fontH (0),
  111. previousFontH (0),
  112. dc (CreateCompatibleDC (0)),
  113. ascent (1.0f),
  114. defaultGlyph (-1),
  115. bold (font.isBold()),
  116. italic (font.isItalic())
  117. {
  118. loadFont();
  119. if (GetTextMetrics (dc, &tm))
  120. {
  121. ascent = tm.tmAscent / (float) tm.tmHeight;
  122. defaultGlyph = getGlyphForChar (dc, tm.tmDefaultChar);
  123. createKerningPairs (dc, (float) tm.tmHeight);
  124. }
  125. }
  126. ~WindowsTypeface()
  127. {
  128. SelectObject (dc, previousFontH); // Replacing the previous font before deleting the DC avoids a warning in BoundsChecker
  129. DeleteDC (dc);
  130. if (fontH != 0)
  131. DeleteObject (fontH);
  132. }
  133. float getAscent() const { return ascent; }
  134. float getDescent() const { return 1.0f - ascent; }
  135. float getStringWidth (const String& text)
  136. {
  137. const CharPointer_UTF16 utf16 (text.toUTF16());
  138. const size_t numChars = utf16.length();
  139. HeapBlock<int16> results (numChars + 1);
  140. results[numChars] = -1;
  141. float x = 0;
  142. if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast <WORD*> (results.getData()),
  143. GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
  144. {
  145. for (size_t i = 0; i < numChars; ++i)
  146. x += getKerning (dc, results[i], results[i + 1]);
  147. }
  148. return x;
  149. }
  150. void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets)
  151. {
  152. const CharPointer_UTF16 utf16 (text.toUTF16());
  153. const size_t numChars = utf16.length();
  154. HeapBlock<int16> results (numChars + 1);
  155. results[numChars] = -1;
  156. float x = 0;
  157. if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast <WORD*> (results.getData()),
  158. GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
  159. {
  160. resultGlyphs.ensureStorageAllocated ((int) numChars);
  161. xOffsets.ensureStorageAllocated ((int) numChars + 1);
  162. for (size_t i = 0; i < numChars; ++i)
  163. {
  164. resultGlyphs.add (results[i]);
  165. xOffsets.add (x);
  166. x += getKerning (dc, results[i], results[i + 1]);
  167. }
  168. }
  169. xOffsets.add (x);
  170. }
  171. bool getOutlineForGlyph (int glyphNumber, Path& glyphPath)
  172. {
  173. if (glyphNumber < 0)
  174. glyphNumber = defaultGlyph;
  175. GLYPHMETRICS gm;
  176. // (although GetGlyphOutline returns a DWORD, it may be -1 on failure, so treat it as signed int..)
  177. const int bufSize = (int) GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX,
  178. &gm, 0, 0, &identityMatrix);
  179. if (bufSize > 0)
  180. {
  181. HeapBlock<char> data (bufSize);
  182. GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm,
  183. bufSize, data, &identityMatrix);
  184. const TTPOLYGONHEADER* pheader = reinterpret_cast<TTPOLYGONHEADER*> (data.getData());
  185. const float scaleX = 1.0f / tm.tmHeight;
  186. const float scaleY = -scaleX;
  187. while ((char*) pheader < data + bufSize)
  188. {
  189. glyphPath.startNewSubPath (scaleX * pheader->pfxStart.x.value,
  190. scaleY * pheader->pfxStart.y.value);
  191. const TTPOLYCURVE* curve = (const TTPOLYCURVE*) ((const char*) pheader + sizeof (TTPOLYGONHEADER));
  192. const char* const curveEnd = ((const char*) pheader) + pheader->cb;
  193. while ((const char*) curve < curveEnd)
  194. {
  195. if (curve->wType == TT_PRIM_LINE)
  196. {
  197. for (int i = 0; i < curve->cpfx; ++i)
  198. glyphPath.lineTo (scaleX * curve->apfx[i].x.value,
  199. scaleY * curve->apfx[i].y.value);
  200. }
  201. else if (curve->wType == TT_PRIM_QSPLINE)
  202. {
  203. for (int i = 0; i < curve->cpfx - 1; ++i)
  204. {
  205. const float x2 = scaleX * curve->apfx[i].x.value;
  206. const float y2 = scaleY * curve->apfx[i].y.value;
  207. float x3 = scaleX * curve->apfx[i + 1].x.value;
  208. float y3 = scaleY * curve->apfx[i + 1].y.value;
  209. if (i < curve->cpfx - 2)
  210. {
  211. x3 = 0.5f * (x2 + x3);
  212. y3 = 0.5f * (y2 + y3);
  213. }
  214. glyphPath.quadraticTo (x2, y2, x3, y3);
  215. }
  216. }
  217. curve = (const TTPOLYCURVE*) &(curve->apfx [curve->cpfx]);
  218. }
  219. pheader = (const TTPOLYGONHEADER*) curve;
  220. glyphPath.closeSubPath();
  221. }
  222. }
  223. return true;
  224. }
  225. private:
  226. static const MAT2 identityMatrix;
  227. HFONT fontH;
  228. HGDIOBJ previousFontH;
  229. HDC dc;
  230. TEXTMETRIC tm;
  231. float ascent;
  232. int defaultGlyph;
  233. bool bold, italic;
  234. struct KerningPair
  235. {
  236. int glyph1, glyph2;
  237. float kerning;
  238. bool operator== (const KerningPair& other) const noexcept
  239. {
  240. return glyph1 == other.glyph1 && glyph2 == other.glyph2;
  241. }
  242. bool operator< (const KerningPair& other) const noexcept
  243. {
  244. return glyph1 < other.glyph1
  245. || (glyph1 == other.glyph1 && glyph2 < other.glyph2);
  246. }
  247. };
  248. SortedSet<KerningPair> kerningPairs;
  249. void loadFont()
  250. {
  251. SetMapperFlags (dc, 0);
  252. SetMapMode (dc, MM_TEXT);
  253. LOGFONTW lf = { 0 };
  254. lf.lfCharSet = DEFAULT_CHARSET;
  255. lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  256. lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
  257. lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
  258. lf.lfQuality = PROOF_QUALITY;
  259. lf.lfItalic = (BYTE) (italic ? TRUE : FALSE);
  260. lf.lfWeight = bold ? FW_BOLD : FW_NORMAL;
  261. lf.lfHeight = -256;
  262. name.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
  263. HFONT standardSizedFont = CreateFontIndirect (&lf);
  264. if (standardSizedFont != 0)
  265. {
  266. if ((previousFontH = SelectObject (dc, standardSizedFont)) != 0)
  267. {
  268. fontH = standardSizedFont;
  269. OUTLINETEXTMETRIC otm;
  270. if (GetOutlineTextMetrics (dc, sizeof (otm), &otm) != 0)
  271. {
  272. lf.lfHeight = -(int) otm.otmEMSquare;
  273. fontH = CreateFontIndirect (&lf);
  274. SelectObject (dc, fontH);
  275. DeleteObject (standardSizedFont);
  276. }
  277. }
  278. }
  279. }
  280. void createKerningPairs (HDC dc, const float height)
  281. {
  282. HeapBlock<KERNINGPAIR> rawKerning;
  283. const DWORD numKPs = GetKerningPairs (dc, 0, 0);
  284. rawKerning.calloc (numKPs);
  285. GetKerningPairs (dc, numKPs, rawKerning);
  286. kerningPairs.ensureStorageAllocated ((int) numKPs);
  287. for (DWORD i = 0; i < numKPs; ++i)
  288. {
  289. KerningPair kp;
  290. kp.glyph1 = getGlyphForChar (dc, rawKerning[i].wFirst);
  291. kp.glyph2 = getGlyphForChar (dc, rawKerning[i].wSecond);
  292. const int standardWidth = getGlyphWidth (dc, kp.glyph1);
  293. kp.kerning = (standardWidth + rawKerning[i].iKernAmount) / height;
  294. kerningPairs.add (kp);
  295. kp.glyph2 = -1; // add another entry for the standard width version..
  296. kp.kerning = standardWidth / height;
  297. kerningPairs.add (kp);
  298. }
  299. }
  300. static int getGlyphForChar (HDC dc, juce_wchar character)
  301. {
  302. const WCHAR charToTest[] = { (WCHAR) character, 0 };
  303. WORD index = 0;
  304. if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR
  305. || index == 0xffff)
  306. return -1;
  307. return index;
  308. }
  309. static int getGlyphWidth (HDC dc, int glyphNumber)
  310. {
  311. GLYPHMETRICS gm;
  312. gm.gmCellIncX = 0;
  313. GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, 0, &identityMatrix);
  314. return gm.gmCellIncX;
  315. }
  316. float getKerning (HDC dc, const int glyph1, const int glyph2)
  317. {
  318. KerningPair kp;
  319. kp.glyph1 = glyph1;
  320. kp.glyph2 = glyph2;
  321. int index = kerningPairs.indexOf (kp);
  322. if (index < 0)
  323. {
  324. kp.glyph2 = -1;
  325. index = kerningPairs.indexOf (kp);
  326. if (index < 0)
  327. {
  328. kp.glyph2 = -1;
  329. kp.kerning = getGlyphWidth (dc, kp.glyph1) / (float) tm.tmHeight;
  330. kerningPairs.add (kp);
  331. return kp.kerning;
  332. }
  333. }
  334. return kerningPairs.getReference (index).kerning;
  335. }
  336. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTypeface);
  337. };
  338. const MAT2 WindowsTypeface::identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } };
  339. Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
  340. {
  341. #if JUCE_USE_DIRECTWRITE
  342. const Direct2DFactories& factories = Direct2DFactories::getInstance();
  343. if (factories.systemFonts != nullptr)
  344. return new WindowsDirectWriteTypeface (font, factories.systemFonts);
  345. else
  346. #endif
  347. return new WindowsTypeface (font);
  348. }