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.

652 lines
22KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. /* This is some quick-and-dirty code to extract the typeface name from a lump of TTF file data.
  22. It's needed because although win32 will happily load a TTF file from in-memory data, it won't
  23. tell you the name of the damned font that it just loaded.. and in order to actually use the font,
  24. you need to know its name!! Anyway, this awful hack seems to work for most fonts.
  25. */
  26. namespace TTFNameExtractor
  27. {
  28. struct OffsetTable
  29. {
  30. uint32 version;
  31. uint16 numTables, searchRange, entrySelector, rangeShift;
  32. };
  33. struct TableDirectory
  34. {
  35. char tag[4];
  36. uint32 checkSum, offset, length;
  37. };
  38. struct NamingTable
  39. {
  40. uint16 formatSelector;
  41. uint16 numberOfNameRecords;
  42. uint16 offsetStartOfStringStorage;
  43. };
  44. struct NameRecord
  45. {
  46. uint16 platformID, encodingID, languageID;
  47. uint16 nameID, stringLength, offsetFromStorageArea;
  48. };
  49. static String parseNameRecord (MemoryInputStream& input, const NameRecord& nameRecord,
  50. const int64 directoryOffset, const int64 offsetOfStringStorage)
  51. {
  52. String result;
  53. const int64 oldPos = input.getPosition();
  54. input.setPosition (directoryOffset + offsetOfStringStorage + ByteOrder::swapIfLittleEndian (nameRecord.offsetFromStorageArea));
  55. const int stringLength = (int) ByteOrder::swapIfLittleEndian (nameRecord.stringLength);
  56. const int platformID = ByteOrder::swapIfLittleEndian (nameRecord.platformID);
  57. if (platformID == 0 || platformID == 3)
  58. {
  59. const int numChars = stringLength / 2 + 1;
  60. HeapBlock<uint16> buffer;
  61. buffer.calloc (numChars + 1);
  62. input.read (buffer, stringLength);
  63. for (int i = 0; i < numChars; ++i)
  64. buffer[i] = ByteOrder::swapIfLittleEndian (buffer[i]);
  65. static_assert (sizeof (CharPointer_UTF16::CharType) == sizeof (uint16), "Sanity check UTF-16 type");
  66. result = CharPointer_UTF16 ((CharPointer_UTF16::CharType*) buffer.getData());
  67. }
  68. else
  69. {
  70. HeapBlock<char> buffer;
  71. buffer.calloc (stringLength + 1);
  72. input.read (buffer, stringLength);
  73. result = CharPointer_UTF8 (buffer.getData());
  74. }
  75. input.setPosition (oldPos);
  76. return result;
  77. }
  78. static String parseNameTable (MemoryInputStream& input, int64 directoryOffset)
  79. {
  80. input.setPosition (directoryOffset);
  81. NamingTable namingTable = { 0 };
  82. input.read (&namingTable, sizeof (namingTable));
  83. for (int i = 0; i < (int) ByteOrder::swapIfLittleEndian (namingTable.numberOfNameRecords); ++i)
  84. {
  85. NameRecord nameRecord = { 0 };
  86. input.read (&nameRecord, sizeof (nameRecord));
  87. if (ByteOrder::swapIfLittleEndian (nameRecord.nameID) == 4)
  88. {
  89. const String result (parseNameRecord (input, nameRecord, directoryOffset,
  90. ByteOrder::swapIfLittleEndian (namingTable.offsetStartOfStringStorage)));
  91. if (result.isNotEmpty())
  92. return result;
  93. }
  94. }
  95. return {};
  96. }
  97. static String getTypefaceNameFromFile (MemoryInputStream& input)
  98. {
  99. OffsetTable offsetTable = { 0 };
  100. input.read (&offsetTable, sizeof (offsetTable));
  101. for (int i = 0; i < (int) ByteOrder::swapIfLittleEndian (offsetTable.numTables); ++i)
  102. {
  103. TableDirectory tableDirectory;
  104. zerostruct (tableDirectory);
  105. input.read (&tableDirectory, sizeof (tableDirectory));
  106. if (String (tableDirectory.tag, sizeof (tableDirectory.tag)).equalsIgnoreCase ("name"))
  107. return parseNameTable (input, ByteOrder::swapIfLittleEndian (tableDirectory.offset));
  108. }
  109. return {};
  110. }
  111. }
  112. namespace FontEnumerators
  113. {
  114. static int CALLBACK fontEnum2 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam)
  115. {
  116. if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0)
  117. {
  118. const String fontName (lpelfe->elfLogFont.lfFaceName);
  119. ((StringArray*) lParam)->addIfNotAlreadyThere (fontName.removeCharacters ("@"));
  120. }
  121. return 1;
  122. }
  123. static int CALLBACK fontEnum1 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam)
  124. {
  125. if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0)
  126. {
  127. LOGFONTW lf = { 0 };
  128. lf.lfWeight = FW_DONTCARE;
  129. lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
  130. lf.lfQuality = DEFAULT_QUALITY;
  131. lf.lfCharSet = DEFAULT_CHARSET;
  132. lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  133. lf.lfPitchAndFamily = FF_DONTCARE;
  134. const String fontName (lpelfe->elfLogFont.lfFaceName);
  135. fontName.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
  136. HDC dc = CreateCompatibleDC (0);
  137. EnumFontFamiliesEx (dc, &lf,
  138. (FONTENUMPROCW) &fontEnum2,
  139. lParam, 0);
  140. DeleteDC (dc);
  141. }
  142. return 1;
  143. }
  144. }
  145. StringArray Font::findAllTypefaceNames()
  146. {
  147. StringArray results;
  148. #if JUCE_USE_DIRECTWRITE
  149. SharedResourcePointer<Direct2DFactories> factories;
  150. if (factories->systemFonts != nullptr)
  151. {
  152. ComSmartPtr<IDWriteFontFamily> fontFamily;
  153. uint32 fontFamilyCount = 0;
  154. fontFamilyCount = factories->systemFonts->GetFontFamilyCount();
  155. for (uint32 i = 0; i < fontFamilyCount; ++i)
  156. {
  157. HRESULT hr = factories->systemFonts->GetFontFamily (i, fontFamily.resetAndGetPointerAddress());
  158. if (SUCCEEDED (hr))
  159. results.addIfNotAlreadyThere (getFontFamilyName (fontFamily));
  160. }
  161. }
  162. else
  163. #endif
  164. {
  165. HDC dc = CreateCompatibleDC (0);
  166. {
  167. LOGFONTW lf = { 0 };
  168. lf.lfWeight = FW_DONTCARE;
  169. lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
  170. lf.lfQuality = DEFAULT_QUALITY;
  171. lf.lfCharSet = DEFAULT_CHARSET;
  172. lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  173. lf.lfPitchAndFamily = FF_DONTCARE;
  174. EnumFontFamiliesEx (dc, &lf,
  175. (FONTENUMPROCW) &FontEnumerators::fontEnum1,
  176. (LPARAM) &results, 0);
  177. }
  178. DeleteDC (dc);
  179. }
  180. results.sort (true);
  181. return results;
  182. }
  183. StringArray Font::findAllTypefaceStyles (const String& family)
  184. {
  185. if (FontStyleHelpers::isPlaceholderFamilyName (family))
  186. return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family));
  187. StringArray results;
  188. #if JUCE_USE_DIRECTWRITE
  189. SharedResourcePointer<Direct2DFactories> factories;
  190. if (factories->systemFonts != nullptr)
  191. {
  192. BOOL fontFound = false;
  193. uint32 fontIndex = 0;
  194. HRESULT hr = factories->systemFonts->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound);
  195. if (! fontFound)
  196. fontIndex = 0;
  197. // Get the font family using the search results
  198. // Fonts like: Times New Roman, Times New Roman Bold, Times New Roman Italic are all in the same font family
  199. ComSmartPtr<IDWriteFontFamily> fontFamily;
  200. hr = factories->systemFonts->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress());
  201. // Get the font faces
  202. ComSmartPtr<IDWriteFont> dwFont;
  203. uint32 fontFacesCount = 0;
  204. fontFacesCount = fontFamily->GetFontCount();
  205. for (uint32 i = 0; i < fontFacesCount; ++i)
  206. {
  207. hr = fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress());
  208. // Ignore any algorithmically generated bold and oblique styles..
  209. if (dwFont->GetSimulations() == DWRITE_FONT_SIMULATIONS_NONE)
  210. results.addIfNotAlreadyThere (getFontFaceName (dwFont));
  211. }
  212. }
  213. else
  214. #endif
  215. {
  216. results.add ("Regular");
  217. results.add ("Italic");
  218. results.add ("Bold");
  219. results.add ("Bold Italic");
  220. }
  221. return results;
  222. }
  223. extern bool juce_isRunningInWine();
  224. struct DefaultFontNames
  225. {
  226. DefaultFontNames()
  227. {
  228. if (juce_isRunningInWine())
  229. {
  230. // If we're running in Wine, then use fonts that might be available on Linux..
  231. defaultSans = "Bitstream Vera Sans";
  232. defaultSerif = "Bitstream Vera Serif";
  233. defaultFixed = "Bitstream Vera Sans Mono";
  234. }
  235. else
  236. {
  237. defaultSans = "Verdana";
  238. defaultSerif = "Times New Roman";
  239. defaultFixed = "Lucida Console";
  240. defaultFallback = "Tahoma"; // (contains plenty of unicode characters)
  241. }
  242. }
  243. String defaultSans, defaultSerif, defaultFixed, defaultFallback;
  244. };
  245. Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
  246. {
  247. static DefaultFontNames defaultNames;
  248. Font newFont (font);
  249. const String& faceName = font.getTypefaceName();
  250. if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans);
  251. else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif);
  252. else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed);
  253. if (font.getTypefaceStyle() == getDefaultStyle())
  254. newFont.setTypefaceStyle ("Regular");
  255. return Typeface::createSystemTypefaceFor (newFont);
  256. }
  257. //==============================================================================
  258. class WindowsTypeface : public Typeface
  259. {
  260. public:
  261. WindowsTypeface (const Font& font)
  262. : Typeface (font.getTypefaceName(), font.getTypefaceStyle()),
  263. fontH (0), previousFontH (0),
  264. dc (CreateCompatibleDC (0)), memoryFont (0),
  265. ascent (1.0f), heightToPointsFactor (1.0f),
  266. defaultGlyph (-1)
  267. {
  268. loadFont();
  269. }
  270. WindowsTypeface (const void* data, size_t dataSize)
  271. : Typeface (String(), String()),
  272. fontH (0), previousFontH (0),
  273. dc (CreateCompatibleDC (0)), memoryFont (0),
  274. ascent (1.0f), heightToPointsFactor (1.0f),
  275. defaultGlyph (-1)
  276. {
  277. DWORD numInstalled = 0;
  278. memoryFont = AddFontMemResourceEx (const_cast<void*> (data), (DWORD) dataSize,
  279. nullptr, &numInstalled);
  280. MemoryInputStream m (data, dataSize, false);
  281. name = TTFNameExtractor::getTypefaceNameFromFile (m);
  282. loadFont();
  283. }
  284. ~WindowsTypeface()
  285. {
  286. SelectObject (dc, previousFontH); // Replacing the previous font before deleting the DC avoids a warning in BoundsChecker
  287. DeleteDC (dc);
  288. if (fontH != 0)
  289. DeleteObject (fontH);
  290. if (memoryFont != 0)
  291. RemoveFontMemResourceEx (memoryFont);
  292. }
  293. float getAscent() const { return ascent; }
  294. float getDescent() const { return 1.0f - ascent; }
  295. float getHeightToPointsFactor() const { return heightToPointsFactor; }
  296. float getStringWidth (const String& text)
  297. {
  298. const CharPointer_UTF16 utf16 (text.toUTF16());
  299. const size_t numChars = utf16.length();
  300. HeapBlock<int16> results (numChars + 1);
  301. results[numChars] = -1;
  302. float x = 0;
  303. if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast<WORD*> (results.getData()),
  304. GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
  305. {
  306. for (size_t i = 0; i < numChars; ++i)
  307. x += getKerning (dc, results[i], results[i + 1]);
  308. }
  309. return x;
  310. }
  311. void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets)
  312. {
  313. const CharPointer_UTF16 utf16 (text.toUTF16());
  314. const size_t numChars = utf16.length();
  315. HeapBlock<int16> results (numChars + 1);
  316. results[numChars] = -1;
  317. float x = 0;
  318. if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast<WORD*> (results.getData()),
  319. GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
  320. {
  321. resultGlyphs.ensureStorageAllocated ((int) numChars);
  322. xOffsets.ensureStorageAllocated ((int) numChars + 1);
  323. for (size_t i = 0; i < numChars; ++i)
  324. {
  325. resultGlyphs.add (results[i]);
  326. xOffsets.add (x);
  327. x += getKerning (dc, results[i], results[i + 1]);
  328. }
  329. }
  330. xOffsets.add (x);
  331. }
  332. bool getOutlineForGlyph (int glyphNumber, Path& glyphPath)
  333. {
  334. if (glyphNumber < 0)
  335. glyphNumber = defaultGlyph;
  336. GLYPHMETRICS gm;
  337. // (although GetGlyphOutline returns a DWORD, it may be -1 on failure, so treat it as signed int..)
  338. const int bufSize = (int) GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX,
  339. &gm, 0, 0, &identityMatrix);
  340. if (bufSize > 0)
  341. {
  342. HeapBlock<char> data (bufSize);
  343. GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm,
  344. bufSize, data, &identityMatrix);
  345. const TTPOLYGONHEADER* pheader = reinterpret_cast<TTPOLYGONHEADER*> (data.getData());
  346. const float scaleX = 1.0f / tm.tmHeight;
  347. const float scaleY = -scaleX;
  348. while ((char*) pheader < data + bufSize)
  349. {
  350. glyphPath.startNewSubPath (scaleX * pheader->pfxStart.x.value,
  351. scaleY * pheader->pfxStart.y.value);
  352. const TTPOLYCURVE* curve = (const TTPOLYCURVE*) ((const char*) pheader + sizeof (TTPOLYGONHEADER));
  353. const char* const curveEnd = ((const char*) pheader) + pheader->cb;
  354. while ((const char*) curve < curveEnd)
  355. {
  356. if (curve->wType == TT_PRIM_LINE)
  357. {
  358. for (int i = 0; i < curve->cpfx; ++i)
  359. glyphPath.lineTo (scaleX * curve->apfx[i].x.value,
  360. scaleY * curve->apfx[i].y.value);
  361. }
  362. else if (curve->wType == TT_PRIM_QSPLINE)
  363. {
  364. for (int i = 0; i < curve->cpfx - 1; ++i)
  365. {
  366. const float x2 = scaleX * curve->apfx[i].x.value;
  367. const float y2 = scaleY * curve->apfx[i].y.value;
  368. float x3 = scaleX * curve->apfx[i + 1].x.value;
  369. float y3 = scaleY * curve->apfx[i + 1].y.value;
  370. if (i < curve->cpfx - 2)
  371. {
  372. x3 = 0.5f * (x2 + x3);
  373. y3 = 0.5f * (y2 + y3);
  374. }
  375. glyphPath.quadraticTo (x2, y2, x3, y3);
  376. }
  377. }
  378. curve = (const TTPOLYCURVE*) &(curve->apfx [curve->cpfx]);
  379. }
  380. pheader = (const TTPOLYGONHEADER*) curve;
  381. glyphPath.closeSubPath();
  382. }
  383. }
  384. return true;
  385. }
  386. private:
  387. static const MAT2 identityMatrix;
  388. HFONT fontH;
  389. HGDIOBJ previousFontH;
  390. HDC dc;
  391. TEXTMETRIC tm;
  392. HANDLE memoryFont;
  393. float ascent, heightToPointsFactor;
  394. int defaultGlyph, heightInPoints;
  395. struct KerningPair
  396. {
  397. int glyph1, glyph2;
  398. float kerning;
  399. bool operator== (const KerningPair& other) const noexcept
  400. {
  401. return glyph1 == other.glyph1 && glyph2 == other.glyph2;
  402. }
  403. bool operator< (const KerningPair& other) const noexcept
  404. {
  405. return glyph1 < other.glyph1
  406. || (glyph1 == other.glyph1 && glyph2 < other.glyph2);
  407. }
  408. };
  409. SortedSet<KerningPair> kerningPairs;
  410. void loadFont()
  411. {
  412. SetMapperFlags (dc, 0);
  413. SetMapMode (dc, MM_TEXT);
  414. LOGFONTW lf = { 0 };
  415. lf.lfCharSet = DEFAULT_CHARSET;
  416. lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  417. lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
  418. lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
  419. lf.lfQuality = PROOF_QUALITY;
  420. lf.lfItalic = (BYTE) (style.contains ("Italic") ? TRUE : FALSE);
  421. lf.lfWeight = style.contains ("Bold") ? FW_BOLD : FW_NORMAL;
  422. lf.lfHeight = -256;
  423. name.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
  424. HFONT standardSizedFont = CreateFontIndirect (&lf);
  425. if (standardSizedFont != 0)
  426. {
  427. if ((previousFontH = SelectObject (dc, standardSizedFont)) != 0)
  428. {
  429. fontH = standardSizedFont;
  430. OUTLINETEXTMETRIC otm;
  431. if (GetOutlineTextMetrics (dc, sizeof (otm), &otm) != 0)
  432. {
  433. heightInPoints = otm.otmEMSquare;
  434. lf.lfHeight = -(int) heightInPoints;
  435. fontH = CreateFontIndirect (&lf);
  436. SelectObject (dc, fontH);
  437. DeleteObject (standardSizedFont);
  438. }
  439. }
  440. }
  441. if (GetTextMetrics (dc, &tm))
  442. {
  443. float dpi = (GetDeviceCaps (dc, LOGPIXELSX) + GetDeviceCaps (dc, LOGPIXELSY)) / 2.0f;
  444. heightToPointsFactor = (dpi / GetDeviceCaps (dc, LOGPIXELSY)) * heightInPoints / (float) tm.tmHeight;
  445. ascent = tm.tmAscent / (float) tm.tmHeight;
  446. defaultGlyph = getGlyphForChar (dc, tm.tmDefaultChar);
  447. createKerningPairs (dc, (float) tm.tmHeight);
  448. }
  449. }
  450. void createKerningPairs (HDC hdc, const float height)
  451. {
  452. HeapBlock<KERNINGPAIR> rawKerning;
  453. const DWORD numKPs = GetKerningPairs (hdc, 0, 0);
  454. rawKerning.calloc (numKPs);
  455. GetKerningPairs (hdc, numKPs, rawKerning);
  456. kerningPairs.ensureStorageAllocated ((int) numKPs);
  457. for (DWORD i = 0; i < numKPs; ++i)
  458. {
  459. KerningPair kp;
  460. kp.glyph1 = getGlyphForChar (hdc, rawKerning[i].wFirst);
  461. kp.glyph2 = getGlyphForChar (hdc, rawKerning[i].wSecond);
  462. const int standardWidth = getGlyphWidth (hdc, kp.glyph1);
  463. kp.kerning = (standardWidth + rawKerning[i].iKernAmount) / height;
  464. kerningPairs.add (kp);
  465. kp.glyph2 = -1; // add another entry for the standard width version..
  466. kp.kerning = standardWidth / height;
  467. kerningPairs.add (kp);
  468. }
  469. }
  470. static int getGlyphForChar (HDC dc, juce_wchar character)
  471. {
  472. const WCHAR charToTest[] = { (WCHAR) character, 0 };
  473. WORD index = 0;
  474. if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR
  475. || index == 0xffff)
  476. return -1;
  477. return index;
  478. }
  479. static int getGlyphWidth (HDC dc, int glyphNumber)
  480. {
  481. GLYPHMETRICS gm;
  482. gm.gmCellIncX = 0;
  483. GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, 0, &identityMatrix);
  484. return gm.gmCellIncX;
  485. }
  486. float getKerning (HDC hdc, const int glyph1, const int glyph2)
  487. {
  488. KerningPair kp;
  489. kp.glyph1 = glyph1;
  490. kp.glyph2 = glyph2;
  491. int index = kerningPairs.indexOf (kp);
  492. if (index < 0)
  493. {
  494. kp.glyph2 = -1;
  495. index = kerningPairs.indexOf (kp);
  496. if (index < 0)
  497. {
  498. kp.glyph2 = -1;
  499. kp.kerning = getGlyphWidth (hdc, kp.glyph1) / (float) tm.tmHeight;
  500. kerningPairs.add (kp);
  501. return kp.kerning;
  502. }
  503. }
  504. return kerningPairs.getReference (index).kerning;
  505. }
  506. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTypeface)
  507. };
  508. const MAT2 WindowsTypeface::identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } };
  509. Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
  510. {
  511. #if JUCE_USE_DIRECTWRITE
  512. SharedResourcePointer<Direct2DFactories> factories;
  513. if (factories->systemFonts != nullptr)
  514. {
  515. ScopedPointer<WindowsDirectWriteTypeface> wtf (new WindowsDirectWriteTypeface (font, factories->systemFonts));
  516. if (wtf->loadedOk())
  517. return wtf.release();
  518. }
  519. #endif
  520. return new WindowsTypeface (font);
  521. }
  522. Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize)
  523. {
  524. return new WindowsTypeface (data, dataSize);
  525. }
  526. void Typeface::scanFolderForFonts (const File&)
  527. {
  528. jassertfalse; // not implemented on this platform
  529. }
  530. } // namespace juce