|
- /*
- ==============================================================================
-
- This file is part of the JUCE library - "Jules' Utility Class Extensions"
- Copyright 2004-11 by Raw Material Software Ltd.
-
- ------------------------------------------------------------------------------
-
- JUCE can be redistributed and/or modified under the terms of the GNU General
- Public License (Version 2), as published by the Free Software Foundation.
- A copy of the license is included in the JUCE distribution, or can be found
- online at www.gnu.org/licenses.
-
- JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- ------------------------------------------------------------------------------
-
- To release a closed-source product which uses JUCE, commercial licenses are
- available: visit www.rawmaterialsoftware.com/juce for more information.
-
- ==============================================================================
- */
-
- namespace FontEnumerators
- {
- int CALLBACK fontEnum2 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam)
- {
- if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0)
- {
- const String fontName (lpelfe->elfLogFont.lfFaceName);
- ((StringArray*) lParam)->addIfNotAlreadyThere (fontName.removeCharacters ("@"));
- }
-
- return 1;
- }
-
- int CALLBACK fontEnum1 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam)
- {
- if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0)
- {
- LOGFONTW lf = { 0 };
- lf.lfWeight = FW_DONTCARE;
- lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
- lf.lfQuality = DEFAULT_QUALITY;
- lf.lfCharSet = DEFAULT_CHARSET;
- lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
- lf.lfPitchAndFamily = FF_DONTCARE;
-
- const String fontName (lpelfe->elfLogFont.lfFaceName);
- fontName.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
-
- HDC dc = CreateCompatibleDC (0);
- EnumFontFamiliesEx (dc, &lf,
- (FONTENUMPROCW) &fontEnum2,
- lParam, 0);
- DeleteDC (dc);
- }
-
- return 1;
- }
- }
-
- StringArray Font::findAllTypefaceNames()
- {
- StringArray results;
- HDC dc = CreateCompatibleDC (0);
-
- {
- LOGFONTW lf = { 0 };
- lf.lfWeight = FW_DONTCARE;
- lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
- lf.lfQuality = DEFAULT_QUALITY;
- lf.lfCharSet = DEFAULT_CHARSET;
- lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
- lf.lfPitchAndFamily = FF_DONTCARE;
-
- EnumFontFamiliesEx (dc, &lf,
- (FONTENUMPROCW) &FontEnumerators::fontEnum1,
- (LPARAM) &results, 0);
- }
-
- DeleteDC (dc);
-
- results.sort (true);
- return results;
- }
-
- extern bool juce_IsRunningInWine();
-
- struct DefaultFontNames
- {
- DefaultFontNames()
- {
- if (juce_IsRunningInWine())
- {
- // If we're running in Wine, then use fonts that might be available on Linux..
- defaultSans = "Bitstream Vera Sans";
- defaultSerif = "Bitstream Vera Serif";
- defaultFixed = "Bitstream Vera Sans Mono";
- }
- else
- {
- defaultSans = "Verdana";
- defaultSerif = "Times";
- defaultFixed = "Lucida Console";
- defaultFallback = "Tahoma"; // (contains plenty of unicode characters)
- }
- }
-
- String defaultSans, defaultSerif, defaultFixed, defaultFallback;
- };
-
- Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
- {
- static DefaultFontNames defaultNames;
-
- String faceName (font.getTypefaceName());
-
- if (faceName == Font::getDefaultSansSerifFontName()) faceName = defaultNames.defaultSans;
- else if (faceName == Font::getDefaultSerifFontName()) faceName = defaultNames.defaultSerif;
- else if (faceName == Font::getDefaultMonospacedFontName()) faceName = defaultNames.defaultFixed;
-
- Font f (font);
- f.setTypefaceName (faceName);
- return Typeface::createSystemTypefaceFor (f);
- }
-
- //==============================================================================
- class WindowsTypeface : public Typeface
- {
- public:
- WindowsTypeface (const Font& font)
- : Typeface (font.getTypefaceName()),
- fontH (0),
- previousFontH (0),
- dc (CreateCompatibleDC (0)),
- ascent (1.0f),
- defaultGlyph (-1),
- bold (font.isBold()),
- italic (font.isItalic())
- {
- loadFont();
-
- if (GetTextMetrics (dc, &tm))
- {
- ascent = tm.tmAscent / (float) tm.tmHeight;
- defaultGlyph = getGlyphForChar (dc, tm.tmDefaultChar);
- createKerningPairs (dc, (float) tm.tmHeight);
- }
- }
-
- ~WindowsTypeface()
- {
- SelectObject (dc, previousFontH); // Replacing the previous font before deleting the DC avoids a warning in BoundsChecker
- DeleteDC (dc);
-
- if (fontH != 0)
- DeleteObject (fontH);
- }
-
- float getAscent() const { return ascent; }
- float getDescent() const { return 1.0f - ascent; }
-
- float getStringWidth (const String& text)
- {
- const CharPointer_UTF16 utf16 (text.toUTF16());
- const size_t numChars = utf16.length();
- HeapBlock<int16> results (numChars + 1);
- results[numChars] = -1;
- float x = 0;
-
- if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast <WORD*> (results.getData()),
- GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
- {
- for (size_t i = 0; i < numChars; ++i)
- x += getKerning (dc, results[i], results[i + 1]);
- }
-
- return x;
- }
-
- void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets)
- {
- const CharPointer_UTF16 utf16 (text.toUTF16());
- const size_t numChars = utf16.length();
- HeapBlock<int16> results (numChars + 1);
- results[numChars] = -1;
- float x = 0;
-
- if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast <WORD*> (results.getData()),
- GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
- {
- resultGlyphs.ensureStorageAllocated ((int) numChars);
- xOffsets.ensureStorageAllocated ((int) numChars + 1);
-
- for (size_t i = 0; i < numChars; ++i)
- {
- resultGlyphs.add (results[i]);
- xOffsets.add (x);
- x += getKerning (dc, results[i], results[i + 1]);
- }
- }
-
- xOffsets.add (x);
- }
-
- bool getOutlineForGlyph (int glyphNumber, Path& glyphPath)
- {
- if (glyphNumber < 0)
- glyphNumber = defaultGlyph;
-
- GLYPHMETRICS gm;
- // (although GetGlyphOutline returns a DWORD, it may be -1 on failure, so treat it as signed int..)
- const int bufSize = (int) GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX,
- &gm, 0, 0, &identityMatrix);
-
- if (bufSize > 0)
- {
- HeapBlock<char> data (bufSize);
- GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm,
- bufSize, data, &identityMatrix);
-
- const TTPOLYGONHEADER* pheader = reinterpret_cast<TTPOLYGONHEADER*> (data.getData());
-
- const float scaleX = 1.0f / tm.tmHeight;
- const float scaleY = -scaleX;
-
- while ((char*) pheader < data + bufSize)
- {
- glyphPath.startNewSubPath (scaleX * pheader->pfxStart.x.value,
- scaleY * pheader->pfxStart.y.value);
-
- const TTPOLYCURVE* curve = (const TTPOLYCURVE*) ((const char*) pheader + sizeof (TTPOLYGONHEADER));
- const char* const curveEnd = ((const char*) pheader) + pheader->cb;
-
- while ((const char*) curve < curveEnd)
- {
- if (curve->wType == TT_PRIM_LINE)
- {
- for (int i = 0; i < curve->cpfx; ++i)
- glyphPath.lineTo (scaleX * curve->apfx[i].x.value,
- scaleY * curve->apfx[i].y.value);
- }
- else if (curve->wType == TT_PRIM_QSPLINE)
- {
- for (int i = 0; i < curve->cpfx - 1; ++i)
- {
- const float x2 = scaleX * curve->apfx[i].x.value;
- const float y2 = scaleY * curve->apfx[i].y.value;
- float x3 = scaleX * curve->apfx[i + 1].x.value;
- float y3 = scaleY * curve->apfx[i + 1].y.value;
-
- if (i < curve->cpfx - 2)
- {
- x3 = 0.5f * (x2 + x3);
- y3 = 0.5f * (y2 + y3);
- }
-
- glyphPath.quadraticTo (x2, y2, x3, y3);
- }
- }
-
- curve = (const TTPOLYCURVE*) &(curve->apfx [curve->cpfx]);
- }
-
- pheader = (const TTPOLYGONHEADER*) curve;
-
- glyphPath.closeSubPath();
- }
- }
-
- return true;
- }
-
- private:
- static const MAT2 identityMatrix;
- HFONT fontH;
- HGDIOBJ previousFontH;
- HDC dc;
- TEXTMETRIC tm;
- float ascent;
- int defaultGlyph;
- bool bold, italic;
-
- struct KerningPair
- {
- int glyph1, glyph2;
- float kerning;
-
- bool operator== (const KerningPair& other) const noexcept
- {
- return glyph1 == other.glyph1 && glyph2 == other.glyph2;
- }
-
- bool operator< (const KerningPair& other) const noexcept
- {
- return glyph1 < other.glyph1
- || (glyph1 == other.glyph1 && glyph2 < other.glyph2);
- }
- };
-
- SortedSet<KerningPair> kerningPairs;
-
- void loadFont()
- {
- SetMapperFlags (dc, 0);
- SetMapMode (dc, MM_TEXT);
-
- LOGFONTW lf = { 0 };
- lf.lfCharSet = DEFAULT_CHARSET;
- lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
- lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
- lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
- lf.lfQuality = PROOF_QUALITY;
- lf.lfItalic = (BYTE) (italic ? TRUE : FALSE);
- lf.lfWeight = bold ? FW_BOLD : FW_NORMAL;
- lf.lfHeight = -256;
- name.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
-
- HFONT standardSizedFont = CreateFontIndirect (&lf);
-
- if (standardSizedFont != 0)
- {
- if ((previousFontH = SelectObject (dc, standardSizedFont)) != 0)
- {
- fontH = standardSizedFont;
-
- OUTLINETEXTMETRIC otm;
- if (GetOutlineTextMetrics (dc, sizeof (otm), &otm) != 0)
- {
- lf.lfHeight = -(int) otm.otmEMSquare;
- fontH = CreateFontIndirect (&lf);
-
- SelectObject (dc, fontH);
- DeleteObject (standardSizedFont);
- }
- }
- }
- }
-
- void createKerningPairs (HDC dc, const float height)
- {
- HeapBlock<KERNINGPAIR> rawKerning;
- const DWORD numKPs = GetKerningPairs (dc, 0, 0);
- rawKerning.calloc (numKPs);
- GetKerningPairs (dc, numKPs, rawKerning);
-
- kerningPairs.ensureStorageAllocated ((int) numKPs);
-
- for (DWORD i = 0; i < numKPs; ++i)
- {
- KerningPair kp;
- kp.glyph1 = getGlyphForChar (dc, rawKerning[i].wFirst);
- kp.glyph2 = getGlyphForChar (dc, rawKerning[i].wSecond);
-
- const int standardWidth = getGlyphWidth (dc, kp.glyph1);
- kp.kerning = (standardWidth + rawKerning[i].iKernAmount) / height;
- kerningPairs.add (kp);
-
- kp.glyph2 = -1; // add another entry for the standard width version..
- kp.kerning = standardWidth / height;
- kerningPairs.add (kp);
- }
- }
-
- static int getGlyphForChar (HDC dc, juce_wchar character)
- {
- const WCHAR charToTest[] = { (WCHAR) character, 0 };
- WORD index = 0;
-
- if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR
- || index == 0xffff)
- return -1;
-
- return index;
- }
-
- static int getGlyphWidth (HDC dc, int glyphNumber)
- {
- GLYPHMETRICS gm;
- gm.gmCellIncX = 0;
- GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, 0, &identityMatrix);
- return gm.gmCellIncX;
- }
-
- float getKerning (HDC dc, const int glyph1, const int glyph2)
- {
- KerningPair kp;
- kp.glyph1 = glyph1;
- kp.glyph2 = glyph2;
- int index = kerningPairs.indexOf (kp);
-
- if (index < 0)
- {
- kp.glyph2 = -1;
- index = kerningPairs.indexOf (kp);
-
- if (index < 0)
- {
- kp.glyph2 = -1;
- kp.kerning = getGlyphWidth (dc, kp.glyph1) / (float) tm.tmHeight;
- kerningPairs.add (kp);
- return kp.kerning;
- }
- }
-
- return kerningPairs.getReference (index).kerning;
- }
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTypeface);
- };
-
- const MAT2 WindowsTypeface::identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } };
-
- Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
- {
- #if JUCE_USE_DIRECTWRITE
- const Direct2DFactories& factories = Direct2DFactories::getInstance();
-
- if (factories.systemFonts != nullptr)
- return new WindowsDirectWriteTypeface (font, factories.systemFonts);
- else
- #endif
- return new WindowsTypeface (font);
- }
|