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.

422 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. const DWORD bufSize = GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX,
  177. &gm, 0, 0, &identityMatrix);
  178. if (bufSize > 0)
  179. {
  180. HeapBlock<char> data (bufSize);
  181. GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm,
  182. bufSize, data, &identityMatrix);
  183. const TTPOLYGONHEADER* pheader = reinterpret_cast<TTPOLYGONHEADER*> (data.getData());
  184. const float scaleX = 1.0f / tm.tmHeight;
  185. const float scaleY = -scaleX;
  186. while ((char*) pheader < data + bufSize)
  187. {
  188. glyphPath.startNewSubPath (scaleX * pheader->pfxStart.x.value,
  189. scaleY * pheader->pfxStart.y.value);
  190. const TTPOLYCURVE* curve = (const TTPOLYCURVE*) ((const char*) pheader + sizeof (TTPOLYGONHEADER));
  191. const char* const curveEnd = ((const char*) pheader) + pheader->cb;
  192. while ((const char*) curve < curveEnd)
  193. {
  194. if (curve->wType == TT_PRIM_LINE)
  195. {
  196. for (int i = 0; i < curve->cpfx; ++i)
  197. glyphPath.lineTo (scaleX * curve->apfx[i].x.value,
  198. scaleY * curve->apfx[i].y.value);
  199. }
  200. else if (curve->wType == TT_PRIM_QSPLINE)
  201. {
  202. for (int i = 0; i < curve->cpfx - 1; ++i)
  203. {
  204. const float x2 = scaleX * curve->apfx[i].x.value;
  205. const float y2 = scaleY * curve->apfx[i].y.value;
  206. float x3 = scaleX * curve->apfx[i + 1].x.value;
  207. float y3 = scaleY * curve->apfx[i + 1].y.value;
  208. if (i < curve->cpfx - 2)
  209. {
  210. x3 = 0.5f * (x2 + x3);
  211. y3 = 0.5f * (y2 + y3);
  212. }
  213. glyphPath.quadraticTo (x2, y2, x3, y3);
  214. }
  215. }
  216. curve = (const TTPOLYCURVE*) &(curve->apfx [curve->cpfx]);
  217. }
  218. pheader = (const TTPOLYGONHEADER*) curve;
  219. glyphPath.closeSubPath();
  220. }
  221. }
  222. return true;
  223. }
  224. private:
  225. static const MAT2 identityMatrix;
  226. HFONT fontH;
  227. HGDIOBJ previousFontH;
  228. HDC dc;
  229. TEXTMETRIC tm;
  230. float ascent;
  231. int defaultGlyph;
  232. bool bold, italic;
  233. struct KerningPair
  234. {
  235. int glyph1, glyph2;
  236. float kerning;
  237. bool operator== (const KerningPair& other) const noexcept
  238. {
  239. return glyph1 == other.glyph1 && glyph2 == other.glyph2;
  240. }
  241. bool operator< (const KerningPair& other) const noexcept
  242. {
  243. return glyph1 < other.glyph1
  244. || (glyph1 == other.glyph1 && glyph2 < other.glyph2);
  245. }
  246. };
  247. SortedSet<KerningPair> kerningPairs;
  248. void loadFont()
  249. {
  250. SetMapperFlags (dc, 0);
  251. SetMapMode (dc, MM_TEXT);
  252. LOGFONTW lf = { 0 };
  253. lf.lfCharSet = DEFAULT_CHARSET;
  254. lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  255. lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
  256. lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
  257. lf.lfQuality = PROOF_QUALITY;
  258. lf.lfItalic = (BYTE) (italic ? TRUE : FALSE);
  259. lf.lfWeight = bold ? FW_BOLD : FW_NORMAL;
  260. lf.lfHeight = -256;
  261. name.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
  262. HFONT standardSizedFont = CreateFontIndirect (&lf);
  263. if (standardSizedFont != 0)
  264. {
  265. if ((previousFontH = SelectObject (dc, standardSizedFont)) != 0)
  266. {
  267. fontH = standardSizedFont;
  268. OUTLINETEXTMETRIC otm;
  269. if (GetOutlineTextMetrics (dc, sizeof (otm), &otm) != 0)
  270. {
  271. lf.lfHeight = -(int) otm.otmEMSquare;
  272. fontH = CreateFontIndirect (&lf);
  273. SelectObject (dc, fontH);
  274. DeleteObject (standardSizedFont);
  275. }
  276. }
  277. }
  278. }
  279. void createKerningPairs (HDC dc, const float height)
  280. {
  281. HeapBlock<KERNINGPAIR> rawKerning;
  282. const DWORD numKPs = GetKerningPairs (dc, 0, 0);
  283. rawKerning.calloc (numKPs);
  284. GetKerningPairs (dc, numKPs, rawKerning);
  285. kerningPairs.ensureStorageAllocated ((int) numKPs);
  286. for (DWORD i = 0; i < numKPs; ++i)
  287. {
  288. KerningPair kp;
  289. kp.glyph1 = getGlyphForChar (dc, rawKerning[i].wFirst);
  290. kp.glyph2 = getGlyphForChar (dc, rawKerning[i].wSecond);
  291. const int standardWidth = getGlyphWidth (dc, kp.glyph1);
  292. kp.kerning = (standardWidth + rawKerning[i].iKernAmount) / height;
  293. kerningPairs.add (kp);
  294. kp.glyph2 = -1; // add another entry for the standard width version..
  295. kp.kerning = standardWidth / height;
  296. kerningPairs.add (kp);
  297. }
  298. }
  299. static int getGlyphForChar (HDC dc, juce_wchar character)
  300. {
  301. const WCHAR charToTest[] = { (WCHAR) character, 0 };
  302. WORD index = 0;
  303. if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR
  304. || index == 0xffff)
  305. return -1;
  306. return index;
  307. }
  308. static int getGlyphWidth (HDC dc, int glyphNumber)
  309. {
  310. GLYPHMETRICS gm;
  311. gm.gmCellIncX = 0;
  312. GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, 0, &identityMatrix);
  313. return gm.gmCellIncX;
  314. }
  315. float getKerning (HDC dc, const int glyph1, const int glyph2)
  316. {
  317. KerningPair kp;
  318. kp.glyph1 = glyph1;
  319. kp.glyph2 = glyph2;
  320. int index = kerningPairs.indexOf (kp);
  321. if (index < 0)
  322. {
  323. kp.glyph2 = -1;
  324. index = kerningPairs.indexOf (kp);
  325. if (index < 0)
  326. {
  327. kp.glyph2 = -1;
  328. kp.kerning = getGlyphWidth (dc, kp.glyph1) / (float) tm.tmHeight;
  329. kerningPairs.add (kp);
  330. return kp.kerning;
  331. }
  332. }
  333. return kerningPairs.getReference (index).kerning;
  334. }
  335. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTypeface);
  336. };
  337. const MAT2 WindowsTypeface::identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } };
  338. Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
  339. {
  340. return new WindowsTypeface (font);
  341. }