mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-22 01:34:21 +00:00
394 lines
13 KiB
C++
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
|