1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-22 01:34:21 +00:00
JUCE/src/native/windows/juce_win32_Fonts.cpp
2011-03-17 16:47:17 +00:00

394 lines
13 KiB
C++

/*
==============================================================================
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.
==============================================================================
*/
// (This file gets included by juce_win32_NativeCode.cpp, rather than being
// compiled on its own).
#if JUCE_INCLUDED_FILE
//==============================================================================
static int CALLBACK wfontEnum2 (ENUMLOGFONTEXW* lpelfe,
NEWTEXTMETRICEXW*,
int type,
LPARAM lParam)
{
if (lpelfe != 0 && (type & RASTER_FONTTYPE) == 0)
{
const String fontName (lpelfe->elfLogFont.lfFaceName);
((StringArray*) lParam)->addIfNotAlreadyThere (fontName.removeCharacters ("@"));
}
return 1;
}
static int CALLBACK wfontEnum1 (ENUMLOGFONTEXW* lpelfe,
NEWTEXTMETRICEXW*,
int type,
LPARAM lParam)
{
if (lpelfe != 0 && (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, LF_FACESIZE - 1);
HDC dc = CreateCompatibleDC (0);
EnumFontFamiliesEx (dc, &lf,
(FONTENUMPROCW) &wfontEnum2,
lParam, 0);
DeleteDC (dc);
}
return 1;
}
const 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;
lf.lfFaceName[0] = 0;
EnumFontFamiliesEx (dc, &lf,
(FONTENUMPROCW) &wfontEnum1,
(LPARAM) &results, 0);
}
DeleteDC (dc);
results.sort (true);
return results;
}
extern bool juce_IsRunningInWine();
void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed, String& defaultFallback)
{
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)
}
}
//==============================================================================
class FontDCHolder : private DeletedAtShutdown
{
public:
//==============================================================================
FontDCHolder()
: fontH (0), previousFontH (0), dc (0), numKPs (0), size (0),
bold (false), italic (false)
{
}
~FontDCHolder()
{
deleteDCAndFont();
clearSingletonInstance();
}
juce_DeclareSingleton_SingleThreaded_Minimal (FontDCHolder);
//==============================================================================
HDC loadFont (const String& fontName_, const bool bold_, const bool italic_, const int size_)
{
if (fontName != fontName_ || bold != bold_ || italic != italic_ || size != size_)
{
fontName = fontName_;
bold = bold_;
italic = italic_;
size = size_;
deleteDCAndFont();
dc = CreateCompatibleDC (0);
SetMapperFlags (dc, 0);
SetMapMode (dc, MM_TEXT);
LOGFONTW lfw = { 0 };
lfw.lfCharSet = DEFAULT_CHARSET;
lfw.lfClipPrecision = CLIP_DEFAULT_PRECIS;
lfw.lfOutPrecision = OUT_OUTLINE_PRECIS;
lfw.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
lfw.lfQuality = PROOF_QUALITY;
lfw.lfItalic = (BYTE) (italic ? TRUE : FALSE);
lfw.lfWeight = bold ? FW_BOLD : FW_NORMAL;
fontName.copyToUTF16 (lfw.lfFaceName, LF_FACESIZE - 1);
lfw.lfHeight = size > 0 ? size : -256;
HFONT standardSizedFont = CreateFontIndirect (&lfw);
if (standardSizedFont != 0)
{
if ((previousFontH = SelectObject (dc, standardSizedFont)) != 0)
{
fontH = standardSizedFont;
if (size == 0)
{
OUTLINETEXTMETRIC otm;
if (GetOutlineTextMetrics (dc, sizeof (otm), &otm) != 0)
{
lfw.lfHeight = -(int) otm.otmEMSquare;
fontH = CreateFontIndirect (&lfw);
SelectObject (dc, fontH);
DeleteObject (standardSizedFont);
}
}
}
}
}
return dc;
}
//==============================================================================
KERNINGPAIR* getKerningPairs (int& numKPs_)
{
if (kps == 0)
{
numKPs = GetKerningPairs (dc, 0, 0);
kps.calloc (numKPs);
GetKerningPairs (dc, numKPs, kps);
}
numKPs_ = numKPs;
return kps;
}
private:
//==============================================================================
HFONT fontH;
HGDIOBJ previousFontH;
HDC dc;
String fontName;
HeapBlock <KERNINGPAIR> kps;
int numKPs, size;
bool bold, italic;
void deleteDCAndFont()
{
if (dc != 0)
{
SelectObject (dc, previousFontH); // Replacing the previous font before deleting the DC avoids a warning in BoundsChecker
DeleteDC (dc);
dc = 0;
}
if (fontH != 0)
{
DeleteObject (fontH);
fontH = 0;
}
kps.free();
}
JUCE_DECLARE_NON_COPYABLE (FontDCHolder);
};
juce_ImplementSingleton_SingleThreaded (FontDCHolder);
//==============================================================================
class WindowsTypeface : public CustomTypeface
{
public:
WindowsTypeface (const Font& font)
{
HDC dc = FontDCHolder::getInstance()->loadFont (font.getTypefaceName(),
font.isBold(), font.isItalic(), 0);
TEXTMETRIC tm;
tm.tmAscent = tm.tmHeight = 1;
tm.tmDefaultChar = 0;
GetTextMetrics (dc, &tm);
setCharacteristics (font.getTypefaceName(),
tm.tmAscent / (float) tm.tmHeight,
font.isBold(), font.isItalic(),
tm.tmDefaultChar);
}
bool loadGlyphIfPossible (juce_wchar character)
{
HDC dc = FontDCHolder::getInstance()->loadFont (name, isBold, isItalic, 0);
GLYPHMETRICS gm;
// if this is the fallback font, skip checking for the glyph's existence. This is because
// with fonts like Tahoma, GetGlyphIndices can say that a glyph doesn't exist, but it still
// gets correctly created later on.
if (! isFallbackFont)
{
const WCHAR charToTest[] = { (WCHAR) character, 0 };
WORD index = 0;
if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR
&& index == 0xffff)
{
return false;
}
}
Path glyphPath;
TEXTMETRIC tm;
if (! GetTextMetrics (dc, &tm))
{
addGlyph (character, glyphPath, 0);
return true;
}
const float height = (float) tm.tmHeight;
static const MAT2 identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } };
const int bufSize = GetGlyphOutline (dc, character, GGO_NATIVE,
&gm, 0, 0, &identityMatrix);
if (bufSize > 0)
{
HeapBlock<char> data (bufSize);
GetGlyphOutline (dc, character, GGO_NATIVE, &gm,
bufSize, data, &identityMatrix);
const TTPOLYGONHEADER* pheader = reinterpret_cast<TTPOLYGONHEADER*> (data.getData());
const float scaleX = 1.0f / height;
const float scaleY = -1.0f / height;
while ((char*) pheader < data + bufSize)
{
float x = scaleX * pheader->pfxStart.x.value;
float y = scaleY * pheader->pfxStart.y.value;
glyphPath.startNewSubPath (x, y);
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)
{
x = scaleX * curve->apfx[i].x.value;
y = scaleY * curve->apfx[i].y.value;
glyphPath.lineTo (x, y);
}
}
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, y3;
if (i < curve->cpfx - 2)
{
x3 = 0.5f * (x2 + scaleX * curve->apfx[i + 1].x.value);
y3 = 0.5f * (y2 + scaleY * curve->apfx[i + 1].y.value);
}
else
{
x3 = scaleX * curve->apfx[i + 1].x.value;
y3 = scaleY * curve->apfx[i + 1].y.value;
}
glyphPath.quadraticTo (x2, y2, x3, y3);
x = x3;
y = y3;
}
}
curve = (const TTPOLYCURVE*) &(curve->apfx [curve->cpfx]);
}
pheader = (const TTPOLYGONHEADER*) curve;
glyphPath.closeSubPath();
}
}
addGlyph (character, glyphPath, gm.gmCellIncX / height);
int numKPs;
const KERNINGPAIR* const kps = FontDCHolder::getInstance()->getKerningPairs (numKPs);
for (int i = 0; i < numKPs; ++i)
{
if (kps[i].wFirst == character)
addKerningPair (kps[i].wFirst, kps[i].wSecond,
kps[i].iKernAmount / height);
}
return true;
}
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTypeface);
};
const Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
{
return new WindowsTypeface (font);
}
#endif