2002-03-30 20:00:13 +00:00
|
|
|
#include "stdafx.h"
|
|
|
|
|
/*
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
|
|
|
Class: Font
|
|
|
|
|
|
|
|
|
|
Desc: See header.
|
|
|
|
|
|
2003-01-04 05:20:40 +00:00
|
|
|
Copyright (c) 2001-2003 by the person(s) listed below. All rights reserved.
|
2002-03-30 20:00:13 +00:00
|
|
|
Chris Danford
|
2003-01-04 05:17:51 +00:00
|
|
|
Glenn Maynard
|
2002-03-30 20:00:13 +00:00
|
|
|
-----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "Font.h"
|
|
|
|
|
#include "IniFile.h"
|
2002-08-23 01:06:36 +00:00
|
|
|
|
2002-03-30 20:00:13 +00:00
|
|
|
#include "RageTextureManager.h"
|
|
|
|
|
#include "RageUtil.h"
|
2002-05-01 19:14:55 +00:00
|
|
|
#include "RageLog.h"
|
2002-07-27 19:29:51 +00:00
|
|
|
#include "RageException.h"
|
2003-01-05 07:55:31 +00:00
|
|
|
#include "FontManager.h"
|
2003-01-06 02:50:25 +00:00
|
|
|
#include "GameState.h"
|
2003-01-10 02:02:51 +00:00
|
|
|
#include "ThemeManager.h"
|
|
|
|
|
#include "GameManager.h"
|
2003-01-16 20:21:07 +00:00
|
|
|
#include "FontCharmaps.h"
|
|
|
|
|
#include "FontCharAliases.h"
|
2003-01-05 07:55:31 +00:00
|
|
|
|
2003-01-06 04:24:28 +00:00
|
|
|
const longchar Font::DEFAULT_GLYPH = 0xFFFFFF;
|
2002-06-14 22:25:22 +00:00
|
|
|
|
2003-01-04 05:17:51 +00:00
|
|
|
FontPage::FontPage()
|
2002-03-30 20:00:13 +00:00
|
|
|
{
|
2003-01-04 05:17:51 +00:00
|
|
|
m_pTexture = NULL;
|
2003-01-03 20:59:50 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-10 00:31:46 +00:00
|
|
|
void FontPage::Load( const FontPageSettings &cfg )
|
2003-01-03 20:59:50 +00:00
|
|
|
{
|
2003-01-10 00:31:46 +00:00
|
|
|
m_sTexturePath = cfg.TexturePath;
|
2002-03-30 20:00:13 +00:00
|
|
|
|
2002-09-03 22:31:06 +00:00
|
|
|
// load texture
|
|
|
|
|
m_sTexturePath.MakeLower();
|
2003-01-11 21:20:35 +00:00
|
|
|
RageTextureID ID(m_sTexturePath);
|
|
|
|
|
ID.bStretch = true;
|
|
|
|
|
|
2003-01-15 02:10:58 +00:00
|
|
|
m_pTexture = TEXTUREMAN->LoadTexture( ID );
|
2002-09-03 22:31:06 +00:00
|
|
|
ASSERT( m_pTexture != NULL );
|
2002-03-30 20:00:13 +00:00
|
|
|
|
2002-09-03 22:31:06 +00:00
|
|
|
// load character widths
|
2003-01-05 05:13:45 +00:00
|
|
|
vector<int> FrameWidths;
|
2003-01-03 22:53:22 +00:00
|
|
|
int i;
|
2003-01-16 20:21:07 +00:00
|
|
|
|
|
|
|
|
int default_width = m_pTexture->GetSourceFrameWidth();
|
|
|
|
|
if(cfg.DefaultWidth != -1)
|
|
|
|
|
default_width = cfg.DefaultWidth;
|
|
|
|
|
|
2003-01-03 22:53:22 +00:00
|
|
|
// Assume each character is the width of the frame by default.
|
2003-01-04 05:17:51 +00:00
|
|
|
for( i=0; i<m_pTexture->GetNumFrames(); i++ )
|
2003-01-03 22:53:22 +00:00
|
|
|
{
|
2003-01-05 03:32:41 +00:00
|
|
|
map<int,int>::const_iterator it = cfg.GlyphWidths.find(i);
|
|
|
|
|
if(it != cfg.GlyphWidths.end())
|
2003-01-03 22:53:22 +00:00
|
|
|
{
|
2003-01-05 03:32:41 +00:00
|
|
|
FrameWidths.push_back(it->second);
|
|
|
|
|
} else {
|
2003-01-16 20:21:07 +00:00
|
|
|
FrameWidths.push_back(default_width);
|
2003-01-03 22:53:22 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-05 03:32:41 +00:00
|
|
|
if( cfg.AddToAllWidths )
|
2002-03-30 20:00:13 +00:00
|
|
|
{
|
2003-01-16 20:21:07 +00:00
|
|
|
for( int i=0; i<m_pTexture->GetNumFrames(); i++ )
|
2003-01-05 03:32:41 +00:00
|
|
|
FrameWidths[i] += cfg.AddToAllWidths;
|
2002-03-30 20:00:13 +00:00
|
|
|
}
|
2002-03-31 07:55:25 +00:00
|
|
|
|
2003-01-05 03:32:41 +00:00
|
|
|
if( cfg.ScaleAllWidthsBy != 1 )
|
2002-09-04 03:49:08 +00:00
|
|
|
{
|
2003-01-16 20:21:07 +00:00
|
|
|
for( int i=0; i<m_pTexture->GetNumFrames(); i++ )
|
2003-01-05 03:32:41 +00:00
|
|
|
FrameWidths[i] = int(roundf( FrameWidths[i] * cfg.ScaleAllWidthsBy ));
|
2002-09-04 03:49:08 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-05 03:32:41 +00:00
|
|
|
m_iCharToGlyphNo = cfg.CharToGlyphNo;
|
|
|
|
|
|
2003-01-08 02:32:30 +00:00
|
|
|
LineSpacing = cfg.LineSpacing;
|
2003-01-05 03:32:41 +00:00
|
|
|
if(LineSpacing == -1)
|
|
|
|
|
LineSpacing = m_pTexture->GetSourceFrameHeight();
|
2002-03-30 20:00:13 +00:00
|
|
|
|
2003-01-08 02:32:30 +00:00
|
|
|
/* All characters on a page have the same vertical spacing, baseline
|
|
|
|
|
* and ascender. */
|
|
|
|
|
if(cfg.Baseline != -1 && cfg.Top == -1)
|
|
|
|
|
RageException::Throw("Font %s has Baseline but no Top; must have both or neither",
|
2003-01-10 00:31:46 +00:00
|
|
|
m_sTexturePath.GetString());
|
2003-01-08 02:32:30 +00:00
|
|
|
if(cfg.Baseline == -1 && cfg.Top != -1)
|
|
|
|
|
RageException::Throw("Font %s has Baseline but no Top; must have both or neither",
|
2003-01-10 00:31:46 +00:00
|
|
|
m_sTexturePath.GetString());
|
2003-01-08 02:32:30 +00:00
|
|
|
|
|
|
|
|
int baseline=0;
|
|
|
|
|
if(cfg.Baseline == -1 && cfg.Top == -1)
|
|
|
|
|
{
|
|
|
|
|
/* We don't have a top and baseline. Assume we're centered in the
|
|
|
|
|
* frame, and that LineSpacing is the total height. */
|
|
|
|
|
float center = m_pTexture->GetSourceFrameHeight()/2.0f;
|
|
|
|
|
height = LineSpacing;
|
|
|
|
|
baseline = int(center + LineSpacing/2);
|
|
|
|
|
}
|
|
|
|
|
else if(cfg.Baseline != -1 && cfg.Top != -1)
|
|
|
|
|
{
|
|
|
|
|
baseline = cfg.Baseline;
|
|
|
|
|
height = baseline-cfg.Top;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Shift the character up so the top will be rendered at the baseline. */
|
2003-01-10 03:48:21 +00:00
|
|
|
vshift = (float) -baseline;
|
2003-01-08 02:32:30 +00:00
|
|
|
|
|
|
|
|
SetTextureCoords(FrameWidths);
|
2003-01-05 03:32:41 +00:00
|
|
|
SetExtraPixels(cfg.DrawExtraPixelsLeft, cfg.DrawExtraPixelsRight);
|
2003-01-08 02:32:30 +00:00
|
|
|
|
|
|
|
|
LOG->Trace("Font %s: height %i, baseline %i ( == top %i)",
|
2003-01-10 00:31:46 +00:00
|
|
|
m_sTexturePath.GetString(), height, baseline, baseline-height);
|
2002-03-30 20:00:13 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-08 02:32:30 +00:00
|
|
|
void FontPage::SetTextureCoords(const vector<int> &widths)
|
2003-01-03 20:59:50 +00:00
|
|
|
{
|
|
|
|
|
for(int i = 0; i < m_pTexture->GetNumFrames(); ++i)
|
|
|
|
|
{
|
2003-01-03 22:53:22 +00:00
|
|
|
glyph g;
|
|
|
|
|
|
2003-01-06 23:49:43 +00:00
|
|
|
g.fp = this;
|
|
|
|
|
|
2003-01-03 20:59:50 +00:00
|
|
|
/* Make a copy of each texture rect, reducing each to the actual dimensions
|
|
|
|
|
* of the character (most characters don't take a full block). */
|
2003-01-03 22:53:22 +00:00
|
|
|
g.rect = *m_pTexture->GetTextureCoordRect(i);;
|
2003-01-03 20:59:50 +00:00
|
|
|
|
2003-01-04 01:47:01 +00:00
|
|
|
/* Set the width and height to the width and line spacing, respectively. */
|
|
|
|
|
g.width = float(widths[i]);
|
|
|
|
|
g.height = float(m_pTexture->GetSourceFrameHeight());
|
2003-01-03 20:59:50 +00:00
|
|
|
|
2003-01-04 07:55:42 +00:00
|
|
|
/* By default, advance one pixel more than the width. (This could be
|
|
|
|
|
* an option.) */
|
|
|
|
|
g.hadvance = int(g.width + 1);
|
2003-01-03 22:53:22 +00:00
|
|
|
|
2003-01-04 01:47:01 +00:00
|
|
|
/* Do the same thing with X. Do this by changing the actual rendered
|
|
|
|
|
* rect, instead of shifting it, so we don't render more than we need to. */
|
|
|
|
|
g.hshift = 0;
|
|
|
|
|
{
|
|
|
|
|
int iPixelsToChopOff = m_pTexture->GetSourceFrameWidth() - widths[i];
|
2003-01-04 07:55:42 +00:00
|
|
|
if((iPixelsToChopOff % 2) == 1)
|
|
|
|
|
{
|
|
|
|
|
/* We don't want to chop off an odd number of pixels, since that'll
|
|
|
|
|
* put our texture coordinates between texels and make things blurrier. */
|
|
|
|
|
iPixelsToChopOff--;
|
|
|
|
|
g.width++;
|
|
|
|
|
}
|
2003-01-11 21:20:35 +00:00
|
|
|
float fTexCoordsToChopOff = float(iPixelsToChopOff) / m_pTexture->GetSourceWidth();
|
2003-01-04 07:55:42 +00:00
|
|
|
|
2003-01-04 01:47:01 +00:00
|
|
|
g.rect.left += fTexCoordsToChopOff/2;
|
|
|
|
|
g.rect.right -= fTexCoordsToChopOff/2;
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-04 07:55:42 +00:00
|
|
|
g.Texture = m_pTexture;
|
|
|
|
|
|
2003-01-03 22:53:22 +00:00
|
|
|
glyphs.push_back(g);
|
2003-01-03 20:59:50 +00:00
|
|
|
}
|
2002-08-13 23:26:46 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-04 05:17:51 +00:00
|
|
|
void FontPage::SetExtraPixels(int DrawExtraPixelsLeft, int DrawExtraPixelsRight)
|
2003-01-03 20:59:50 +00:00
|
|
|
{
|
2003-01-04 05:17:51 +00:00
|
|
|
/* Hack: do one more than we were asked to; I think a lot of fonts are one
|
|
|
|
|
* too low. */
|
|
|
|
|
DrawExtraPixelsRight++;
|
|
|
|
|
DrawExtraPixelsLeft++;
|
|
|
|
|
|
2003-01-04 07:55:42 +00:00
|
|
|
if((DrawExtraPixelsLeft % 2) == 1)
|
|
|
|
|
DrawExtraPixelsLeft++;
|
|
|
|
|
|
2003-01-03 20:59:50 +00:00
|
|
|
/* Adjust for DrawExtraPixelsLeft and DrawExtraPixelsRight. */
|
2003-01-03 22:53:22 +00:00
|
|
|
for(unsigned i = 0; i < glyphs.size(); ++i)
|
2003-01-03 20:59:50 +00:00
|
|
|
{
|
|
|
|
|
int iFrameWidth = m_pTexture->GetSourceFrameWidth();
|
2003-01-04 07:55:42 +00:00
|
|
|
float iCharWidth = glyphs[i].width;
|
2003-01-03 20:59:50 +00:00
|
|
|
|
2003-01-04 07:55:42 +00:00
|
|
|
/* Extra pixels to draw to the left and right. We don't have to
|
|
|
|
|
* worry about alignment here; CharWidth is always even (by
|
|
|
|
|
* SetTextureCoords) and iFrameWidth are almost always even. */
|
2003-01-04 01:47:01 +00:00
|
|
|
float ExtraLeft = min( float(DrawExtraPixelsLeft), (iFrameWidth-iCharWidth)/2.0f );
|
|
|
|
|
float ExtraRight = min( float(DrawExtraPixelsRight), (iFrameWidth-iCharWidth)/2.0f );
|
2003-01-03 20:59:50 +00:00
|
|
|
|
|
|
|
|
/* Move left and expand right. */
|
2003-01-06 02:50:25 +00:00
|
|
|
glyphs[i].rect.left -= ExtraLeft / m_pTexture->GetTextureWidth();
|
|
|
|
|
glyphs[i].rect.right += ExtraRight / m_pTexture->GetTextureWidth();
|
2003-01-04 01:47:01 +00:00
|
|
|
glyphs[i].hshift -= ExtraLeft;
|
|
|
|
|
glyphs[i].width += ExtraLeft + ExtraRight;
|
2003-01-03 20:59:50 +00:00
|
|
|
}
|
|
|
|
|
}
|
2002-08-13 23:26:46 +00:00
|
|
|
|
2003-01-04 05:17:51 +00:00
|
|
|
FontPage::~FontPage()
|
2002-03-30 20:00:13 +00:00
|
|
|
{
|
|
|
|
|
if( m_pTexture != NULL )
|
2003-01-03 22:53:22 +00:00
|
|
|
TEXTUREMAN->UnloadTexture( m_pTexture );
|
2002-03-30 20:00:13 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-05 08:11:05 +00:00
|
|
|
int Font::GetLineWidthInSourcePixels( const lstring &szLine ) const
|
2002-03-30 20:00:13 +00:00
|
|
|
{
|
2003-01-04 07:55:42 +00:00
|
|
|
int LineWidth = 0;
|
2002-03-30 20:00:13 +00:00
|
|
|
|
2002-12-29 22:55:26 +00:00
|
|
|
for( unsigned i=0; i<szLine.size(); i++ )
|
2003-01-04 07:55:42 +00:00
|
|
|
LineWidth += GetGlyph(szLine[i]).hadvance;
|
2002-03-30 20:00:13 +00:00
|
|
|
|
2003-01-04 07:55:42 +00:00
|
|
|
return LineWidth;
|
2002-03-30 20:00:13 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-05 08:11:05 +00:00
|
|
|
int Font::GetLineHeightInSourcePixels( const lstring &szLine ) const
|
2003-01-04 01:47:01 +00:00
|
|
|
{
|
2003-01-08 02:32:30 +00:00
|
|
|
int iLineHeight = 0;
|
2003-01-04 05:17:51 +00:00
|
|
|
|
2003-01-08 02:32:30 +00:00
|
|
|
/* The spacing of a line is the spacing of its tallest used font page. XXX */
|
2003-01-04 07:55:42 +00:00
|
|
|
for( unsigned i=0; i<szLine.size(); i++ )
|
2003-01-08 02:32:30 +00:00
|
|
|
iLineHeight = max(iLineHeight, GetGlyph(szLine[i]).fp->height);
|
2003-01-10 03:54:29 +00:00
|
|
|
|
2003-01-08 02:32:30 +00:00
|
|
|
return iLineHeight;
|
|
|
|
|
}
|
2003-01-04 05:17:51 +00:00
|
|
|
|
2003-01-08 02:32:30 +00:00
|
|
|
int Font::GetLineSpacingInSourcePixels( const lstring &szLine ) const
|
|
|
|
|
{
|
|
|
|
|
int iLineSpacing = 0;
|
|
|
|
|
|
|
|
|
|
/* The spacing of a line is the spacing of its tallest used font page. XXX */
|
|
|
|
|
for( unsigned i=0; i<szLine.size(); i++ )
|
|
|
|
|
iLineSpacing = max(iLineSpacing, GetGlyph(szLine[i]).fp->LineSpacing);
|
2003-01-10 03:54:29 +00:00
|
|
|
|
2003-01-04 07:55:42 +00:00
|
|
|
return iLineSpacing;
|
2003-01-04 05:17:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Font::Font()
|
|
|
|
|
{
|
|
|
|
|
//LOG->Trace( "Font::LoadFromFontName(%s)", sASCIITexturePath.GetString() );
|
|
|
|
|
|
|
|
|
|
m_iRefCount = 1;
|
2003-01-10 00:31:46 +00:00
|
|
|
def = NULL;
|
2003-01-04 05:17:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Font::~Font()
|
2003-01-10 00:31:46 +00:00
|
|
|
{
|
|
|
|
|
Unload();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Font::Unload()
|
2003-01-04 05:17:51 +00:00
|
|
|
{
|
2003-01-07 08:16:55 +00:00
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
for(i = 0; i < pages.size(); ++i)
|
2003-01-04 05:17:51 +00:00
|
|
|
delete pages[i];
|
2003-01-10 00:31:46 +00:00
|
|
|
pages.clear();
|
|
|
|
|
|
|
|
|
|
m_iCharToGlyph.clear();
|
|
|
|
|
def = NULL;
|
|
|
|
|
|
|
|
|
|
/* Don't clear the refcount. We've unloaded, but that doesn't mean things
|
|
|
|
|
* aren't still pointing to us. */
|
2003-01-04 01:47:01 +00:00
|
|
|
}
|
2002-03-30 20:00:13 +00:00
|
|
|
|
2003-01-10 02:02:51 +00:00
|
|
|
void Font::Reload()
|
|
|
|
|
{
|
|
|
|
|
Unload();
|
|
|
|
|
ASSERT(!path.empty());
|
|
|
|
|
Load(path, Chars);
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-10 00:31:46 +00:00
|
|
|
|
2003-01-04 05:17:51 +00:00
|
|
|
void Font::AddPage(FontPage *fp)
|
|
|
|
|
{
|
|
|
|
|
pages.push_back(fp);
|
|
|
|
|
|
2003-01-05 08:11:05 +00:00
|
|
|
for(map<longchar,int>::const_iterator it = fp->m_iCharToGlyphNo.begin();
|
2003-01-04 05:17:51 +00:00
|
|
|
it != fp->m_iCharToGlyphNo.end(); ++it)
|
|
|
|
|
{
|
|
|
|
|
m_iCharToGlyph[it->first] = &fp->glyphs[it->second];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-10 02:02:51 +00:00
|
|
|
void Font::MergeFont(Font &f)
|
2003-01-05 07:55:31 +00:00
|
|
|
{
|
2003-01-16 22:49:22 +00:00
|
|
|
/* If we don't have a font page yet, and f does, grab the default font
|
|
|
|
|
* page. It'll usually be overridden later on by one of our own font
|
|
|
|
|
* pages; this will be used only if we don't have any font pages at
|
|
|
|
|
* all. */
|
|
|
|
|
if(def == NULL)
|
|
|
|
|
def = f.def;
|
|
|
|
|
|
2003-01-10 02:02:51 +00:00
|
|
|
for(map<longchar,glyph*>::iterator it = f.m_iCharToGlyph.begin();
|
|
|
|
|
it != f.m_iCharToGlyph.end(); ++it)
|
2003-01-05 07:55:31 +00:00
|
|
|
{
|
|
|
|
|
m_iCharToGlyph[it->first] = it->second;
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-10 02:02:51 +00:00
|
|
|
pages.insert(pages.end(), f.pages.begin(), f.pages.end());
|
|
|
|
|
|
|
|
|
|
f.pages.clear();
|
2003-01-05 07:55:31 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-05 08:11:05 +00:00
|
|
|
const glyph &Font::GetGlyph( longchar c ) const
|
2003-01-04 05:17:51 +00:00
|
|
|
{
|
2003-01-06 04:24:28 +00:00
|
|
|
ASSERT(c >= 0 && c <= 0xFFFFFF);
|
|
|
|
|
|
2003-01-06 02:50:25 +00:00
|
|
|
/* See if there's a game-specific version of this character. */
|
|
|
|
|
int gc = FontManager::MakeGameGlyph(c, GAMESTATE->m_CurGame);
|
|
|
|
|
map<longchar,glyph*>::const_iterator it = m_iCharToGlyph.find(gc);
|
2003-01-04 05:17:51 +00:00
|
|
|
|
2003-01-06 02:50:25 +00:00
|
|
|
/* If there isn't, try the regular character. */
|
|
|
|
|
if(it == m_iCharToGlyph.end()) it = m_iCharToGlyph.find(c);
|
2003-01-04 05:17:51 +00:00
|
|
|
|
2003-01-06 02:50:25 +00:00
|
|
|
/* If *that's* missing, use the default glyph. */
|
|
|
|
|
if(it == m_iCharToGlyph.end()) it = m_iCharToGlyph.find(DEFAULT_GLYPH);
|
|
|
|
|
|
|
|
|
|
if(it == m_iCharToGlyph.end())
|
|
|
|
|
RageException::Throw( "The default glyph is missing from the font '%s'", path.GetString() );
|
|
|
|
|
|
2003-01-04 05:17:51 +00:00
|
|
|
return *it->second;
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-05 08:11:05 +00:00
|
|
|
RageTexture *Font::GetGlyphTexture( longchar c )
|
2003-01-04 07:55:42 +00:00
|
|
|
{
|
2003-01-06 02:50:25 +00:00
|
|
|
glyph &g = const_cast<glyph &> (GetGlyph(c));
|
|
|
|
|
return g.Texture;
|
2003-01-04 07:55:42 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-05 03:32:41 +00:00
|
|
|
void Font::CapsOnly()
|
2003-01-04 05:17:51 +00:00
|
|
|
{
|
2003-01-05 03:32:41 +00:00
|
|
|
/* For each uppercase character that we have a mapping for, add
|
|
|
|
|
* a lowercase one. */
|
|
|
|
|
for(char c = 'A'; c <= 'Z'; ++c)
|
2003-01-04 05:17:51 +00:00
|
|
|
{
|
2003-01-05 08:11:05 +00:00
|
|
|
map<longchar,glyph*>::const_iterator it = m_iCharToGlyph.find(c);
|
2003-01-04 05:17:51 +00:00
|
|
|
|
2003-01-05 03:32:41 +00:00
|
|
|
if(it == m_iCharToGlyph.end())
|
|
|
|
|
continue;
|
2003-01-04 05:17:51 +00:00
|
|
|
|
2003-01-05 05:13:45 +00:00
|
|
|
m_iCharToGlyph[(char) tolower(c)] = it->second;
|
2003-01-04 05:17:51 +00:00
|
|
|
}
|
|
|
|
|
}
|
2003-01-06 23:49:43 +00:00
|
|
|
|
|
|
|
|
void Font::SetDefaultGlyph(FontPage *fp)
|
|
|
|
|
{
|
|
|
|
|
ASSERT(fp);
|
|
|
|
|
ASSERT(!fp->glyphs.empty());
|
|
|
|
|
def = fp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-01-16 23:22:21 +00:00
|
|
|
CString Font::GetFontName(CString FileName)
|
2003-01-16 22:49:22 +00:00
|
|
|
{
|
|
|
|
|
CString orig = FileName;
|
|
|
|
|
|
2003-01-16 23:22:21 +00:00
|
|
|
CString sDir, sFName, sExt;
|
|
|
|
|
splitpath( FileName, sDir, sFName, sExt );
|
|
|
|
|
FileName = sFName;
|
|
|
|
|
|
2003-01-16 22:49:22 +00:00
|
|
|
/* If it ends in an extension, remove it. */
|
|
|
|
|
if(regex("\\....", FileName))
|
|
|
|
|
FileName.erase(FileName.size()-4);
|
|
|
|
|
|
|
|
|
|
/* If it ends in a dimension spec, remove it. */
|
|
|
|
|
CStringArray mat;
|
|
|
|
|
if(regex("( [0-9]+x[0-9]+)$", FileName, mat))
|
|
|
|
|
FileName.erase(FileName.size()-mat[0].size());
|
|
|
|
|
|
|
|
|
|
/* If it ends in a page name, remove it. */
|
|
|
|
|
if(regex("( \\[.+\\])$", FileName, mat))
|
|
|
|
|
FileName.erase(FileName.size()-mat[0].size());
|
|
|
|
|
|
|
|
|
|
TrimRight(FileName);
|
|
|
|
|
|
|
|
|
|
if(FileName.empty())
|
|
|
|
|
RageException::Throw("Can't parse font filename \"%s\"", orig.GetString());
|
|
|
|
|
|
2003-01-16 23:22:21 +00:00
|
|
|
FileName.MakeLower();
|
2003-01-16 22:49:22 +00:00
|
|
|
return FileName;
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-16 23:22:21 +00:00
|
|
|
|
2003-01-16 22:49:22 +00:00
|
|
|
/* Given a file in a font, find all of the files for the font.
|
|
|
|
|
*
|
|
|
|
|
* Possibilities:
|
|
|
|
|
*
|
|
|
|
|
* Normal 16x16.png
|
|
|
|
|
* Normal [other] 16x16.png
|
|
|
|
|
* Normal [more] 8x8.png
|
|
|
|
|
* Normal 16x16.ini
|
|
|
|
|
* Normal.ini
|
|
|
|
|
*
|
|
|
|
|
* Any of the above should find all of the above. Allow the
|
|
|
|
|
* extension to be omitted. */
|
|
|
|
|
void Font::GetFontPaths(const CString &sFontOrTextureFilePath,
|
2003-01-10 02:02:51 +00:00
|
|
|
CStringArray &TexturePaths, CString &IniPath)
|
|
|
|
|
{
|
2003-01-16 22:49:22 +00:00
|
|
|
CString sDir, sFName, sExt;
|
|
|
|
|
splitpath( sFontOrTextureFilePath, sDir, sFName, sExt );
|
2003-01-10 02:02:51 +00:00
|
|
|
|
2003-01-16 22:49:22 +00:00
|
|
|
/* Don't give us a redir; resolve those before sending them here. */
|
|
|
|
|
ASSERT(sExt.CompareNoCase("redir"));
|
2003-01-10 02:02:51 +00:00
|
|
|
|
2003-01-16 22:49:22 +00:00
|
|
|
/* sFName can't be empty, or we don't know what to search for. */
|
|
|
|
|
ASSERT(!sFName.empty());
|
2003-01-10 02:02:51 +00:00
|
|
|
|
2003-01-16 22:49:22 +00:00
|
|
|
CString FontName = GetFontName(sFName);
|
2003-01-10 02:02:51 +00:00
|
|
|
|
2003-01-16 22:49:22 +00:00
|
|
|
CStringArray Files;
|
|
|
|
|
GetDirListing( sDir+"/"+FontName + "*", Files, false, false );
|
2003-01-10 02:02:51 +00:00
|
|
|
|
2003-01-16 22:49:22 +00:00
|
|
|
for(unsigned i = 0; i < Files.size(); ++i)
|
2003-01-10 02:02:51 +00:00
|
|
|
{
|
2003-01-16 22:49:22 +00:00
|
|
|
/* We now have a list of possibilities, but it may include false positives,
|
|
|
|
|
* such as "Normal2" when the font name is "Normal". Weed them. */
|
|
|
|
|
if(GetFontName(Files[i]).CompareNoCase(FontName))
|
|
|
|
|
continue;
|
2003-01-10 02:02:51 +00:00
|
|
|
|
2003-01-16 22:49:22 +00:00
|
|
|
/* If it's an INI, and we don't already have an INI, use it. */
|
|
|
|
|
if(!Files[i].Right(4).CompareNoCase(".ini"))
|
|
|
|
|
{
|
|
|
|
|
if(!IniPath.empty())
|
|
|
|
|
RageException::Throw("More than one INI found\n%s\n%s", IniPath.GetString(), Files[i].GetString());
|
|
|
|
|
|
|
|
|
|
IniPath = sDir+"/"+Files[i];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2003-01-10 02:02:51 +00:00
|
|
|
|
2003-01-16 22:49:22 +00:00
|
|
|
TexturePaths.push_back(sDir+"/"+Files[i]);
|
|
|
|
|
}
|
2003-01-10 02:02:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CString Font::GetPageNameFromFileName(const CString &fn)
|
|
|
|
|
{
|
|
|
|
|
unsigned begin = fn.find_first_of('[');
|
|
|
|
|
if(begin == fn.npos) return "main";
|
|
|
|
|
unsigned end = fn.find_first_of(']', begin);
|
|
|
|
|
if(end == fn.npos) return "main";
|
|
|
|
|
begin++; end--;
|
|
|
|
|
if(end == begin) return "main";
|
|
|
|
|
return fn.substr(begin, end-begin+1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Font::LoadFontPageSettings(FontPageSettings &cfg, IniFile &ini, const CString &TexturePath, const CString &PageName, CString sChars)
|
|
|
|
|
{
|
|
|
|
|
cfg.TexturePath = TexturePath;
|
|
|
|
|
|
|
|
|
|
/* If we have any characters to map, add them. */
|
|
|
|
|
for( unsigned n=0; n<sChars.size(); n++ )
|
|
|
|
|
{
|
|
|
|
|
char c = sChars[n];
|
|
|
|
|
cfg.CharToGlyphNo[c] = n;
|
|
|
|
|
}
|
|
|
|
|
int NumFrames = RageTexture::GetFrameCountFromFileName(TexturePath);
|
|
|
|
|
|
|
|
|
|
ini.RenameKey("Char Widths", "main");
|
|
|
|
|
|
|
|
|
|
LOG->Trace("Loading font page '%s' settings from page name '%s'",
|
|
|
|
|
TexturePath.GetString(), PageName.GetString());
|
|
|
|
|
|
|
|
|
|
ini.GetValueI( PageName, "DrawExtraPixelsLeft", cfg.DrawExtraPixelsLeft );
|
|
|
|
|
ini.GetValueI( PageName, "DrawExtraPixelsRight", cfg.DrawExtraPixelsRight );
|
|
|
|
|
ini.GetValueI( PageName, "AddToAllWidths", cfg.AddToAllWidths );
|
|
|
|
|
ini.GetValueF( PageName, "ScaleAllWidthsBy", cfg.ScaleAllWidthsBy );
|
|
|
|
|
ini.GetValueI( PageName, "LineSpacing", cfg.LineSpacing );
|
|
|
|
|
ini.GetValueI( PageName, "Top", cfg.Top );
|
|
|
|
|
ini.GetValueI( PageName, "Baseline", cfg.Baseline );
|
2003-01-16 20:21:07 +00:00
|
|
|
ini.GetValueI( PageName, "DefaultWidth", cfg.DefaultWidth );
|
2003-01-10 02:02:51 +00:00
|
|
|
|
|
|
|
|
/* Iterate over all keys. */
|
|
|
|
|
const IniFile::key *k = ini.GetKey(PageName);
|
|
|
|
|
if(k == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for(IniFile::key::const_iterator key = k->begin(); key != k->end(); ++key)
|
|
|
|
|
{
|
|
|
|
|
CString val = key->first;
|
|
|
|
|
CString data = key->second;
|
|
|
|
|
|
|
|
|
|
val.MakeUpper();
|
|
|
|
|
|
|
|
|
|
/* If val is an integer, it's a width, eg. "10=27". */
|
|
|
|
|
if(IsAnInt(val))
|
|
|
|
|
{
|
|
|
|
|
cfg.GlyphWidths[atoi(val)] = atoi(data);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* "map XXXX=frame" maps a char to a frame. */
|
|
|
|
|
if(val.substr(0, 4) == "MAP ")
|
|
|
|
|
{
|
|
|
|
|
/* map CODEPOINT=frame. CODEPOINT can be
|
|
|
|
|
* 1. U+hexval
|
|
|
|
|
* 2. an alias ("kakumei1")
|
|
|
|
|
* 3. a game type followed by a game alias, eg "pump menuleft"
|
|
|
|
|
*
|
|
|
|
|
* map 1=2 is the same as
|
|
|
|
|
* range unicode #1-1=2
|
|
|
|
|
*/
|
2003-01-17 20:44:06 +00:00
|
|
|
CString codepoint = val.substr(4); /* "CODEPOINT" */
|
2003-01-10 02:02:51 +00:00
|
|
|
|
|
|
|
|
Game game = GAME_INVALID;
|
|
|
|
|
|
|
|
|
|
if(codepoint.find_first_of(' ') != codepoint.npos)
|
|
|
|
|
{
|
|
|
|
|
/* There's a space; the first word should be a game type. Split it. */
|
|
|
|
|
unsigned pos = codepoint.find_first_of(' ');
|
|
|
|
|
CString gamename = codepoint.substr(0, pos);
|
|
|
|
|
codepoint = codepoint.substr(pos+1);
|
|
|
|
|
|
|
|
|
|
game = GameManager::StringToGameType(gamename);
|
|
|
|
|
|
|
|
|
|
if(game == GAME_INVALID)
|
2003-01-17 20:44:06 +00:00
|
|
|
{
|
|
|
|
|
LOG->Warn( "Font definition '%s' uses unknown game type '%s'",
|
2003-01-10 02:02:51 +00:00
|
|
|
ini.GetPath().GetString(), gamename.GetString() );
|
2003-01-17 20:44:06 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2003-01-10 02:02:51 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-16 20:21:07 +00:00
|
|
|
longchar c=-1;
|
2003-01-10 02:02:51 +00:00
|
|
|
if(codepoint.substr(0, 2) == "U+" && IsHexVal(codepoint.substr(2)))
|
|
|
|
|
sscanf(codepoint.substr(2).c_str(), "%x", &c);
|
|
|
|
|
else
|
2003-01-16 20:21:07 +00:00
|
|
|
c = FontCharAliases::GetChar(codepoint);
|
|
|
|
|
|
|
|
|
|
if(c == -1)
|
2003-01-17 20:44:06 +00:00
|
|
|
{
|
|
|
|
|
LOG->Warn("Font definition '%s' has an invalid value '%s'.",
|
2003-01-10 02:02:51 +00:00
|
|
|
ini.GetPath().GetString(), val.GetString() );
|
2003-01-17 20:44:06 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2003-01-10 02:02:51 +00:00
|
|
|
|
|
|
|
|
if(game != GAME_INVALID) c = FontManager::MakeGameGlyph(c, game);
|
|
|
|
|
|
|
|
|
|
cfg.CharToGlyphNo[c] = atoi(data);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2003-01-16 20:21:07 +00:00
|
|
|
|
2003-01-10 02:02:51 +00:00
|
|
|
if(val.substr(0, 6) == "RANGE ")
|
|
|
|
|
{
|
2003-01-16 20:21:07 +00:00
|
|
|
/* range CODESET=first_frame or
|
|
|
|
|
* range CODESET #start-end=first_frame
|
2003-01-10 02:02:51 +00:00
|
|
|
* eg
|
2003-01-16 20:21:07 +00:00
|
|
|
* range CP1252=0 (default for 256-frame fonts)
|
2003-01-10 02:02:51 +00:00
|
|
|
* range ASCII=0 (default for 128-frame fonts)
|
|
|
|
|
*
|
|
|
|
|
* (Start and end are in hex.)
|
|
|
|
|
*
|
|
|
|
|
* Map two high-bit portions of ISO-8859- to one font:
|
|
|
|
|
* range ISO-8859-2 #80-FF=0
|
|
|
|
|
* range ISO-8859-3 #80-FF=128
|
|
|
|
|
*
|
|
|
|
|
* Map hiragana to 0-84:
|
|
|
|
|
* range Unicode #3041-3094=0
|
|
|
|
|
*/
|
2003-01-16 20:21:07 +00:00
|
|
|
vector<CString> matches;
|
|
|
|
|
bool match = regex("^RANGE ([A-Z\\-]+)( ?#([0-9A-F]+)-([0-9A-F]+))?$" , val, matches);
|
|
|
|
|
|
|
|
|
|
ASSERT(matches.size() == 4); /* 4 parens */
|
2003-01-10 02:02:51 +00:00
|
|
|
|
2003-01-16 20:21:07 +00:00
|
|
|
if(!match || matches[0].empty())
|
|
|
|
|
RageException::Throw("Font definition '%s' has an invalid range '%s': parse error",
|
|
|
|
|
ini.GetPath().GetString(), val.GetString() );
|
|
|
|
|
|
|
|
|
|
/* We must have either 1 match (just the codeset) or 4 (the whole thing). */
|
2003-01-10 02:02:51 +00:00
|
|
|
|
2003-01-16 20:21:07 +00:00
|
|
|
int cnt = -1;
|
|
|
|
|
int first = 0;
|
|
|
|
|
if(!matches[2].empty())
|
|
|
|
|
{
|
|
|
|
|
sscanf(matches[2].GetString(), "%x", &first);
|
|
|
|
|
int last;
|
|
|
|
|
sscanf(matches[3].GetString(), "%x", &last);
|
|
|
|
|
if(last < first)
|
|
|
|
|
RageException::Throw("Font definition '%s' has an invalid range '%s': %i < %i.",
|
|
|
|
|
ini.GetPath().GetString(), val.GetString(), last < first );
|
|
|
|
|
|
|
|
|
|
cnt = last-first+1;
|
|
|
|
|
}
|
2003-01-10 02:02:51 +00:00
|
|
|
|
2003-01-16 20:21:07 +00:00
|
|
|
CString ret = cfg.MapRange(matches[0], first, atoi(data), cnt);
|
|
|
|
|
if(!ret.empty())
|
|
|
|
|
RageException::Throw("Font definition '%s' has an invalid range '%s': %s.",
|
|
|
|
|
ini.GetPath().GetString(), val.GetString(), ret.GetString() );
|
2003-01-10 02:02:51 +00:00
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
2003-01-16 20:21:07 +00:00
|
|
|
|
|
|
|
|
/* If it's 128 or 256 frames, default to ASCII or CP1252,
|
|
|
|
|
* respectively. If it's anything else, we don't know what it
|
|
|
|
|
* is, so don't make any default mappings (the INI needs to do
|
|
|
|
|
* it itself). */
|
|
|
|
|
if(cfg.CharToGlyphNo.empty() && NumFrames == 128)
|
|
|
|
|
cfg.MapRange("ascii", 0, 0, -1);
|
|
|
|
|
else if(cfg.CharToGlyphNo.empty() && NumFrames == 256)
|
|
|
|
|
cfg.MapRange("cp1252", 0, 0, -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CString FontPageSettings::MapRange(CString Mapping, int map_offset, int glyphno, int cnt)
|
|
|
|
|
{
|
|
|
|
|
if(!Mapping.CompareNoCase("Unicode"))
|
|
|
|
|
{
|
|
|
|
|
/* Special case. */
|
|
|
|
|
if(cnt == -1)
|
|
|
|
|
return "Can't map all of Unicode to one font page"; /* don't do that */
|
|
|
|
|
|
|
|
|
|
/* What's a practical limit? A 2048x2048 texture could contain 16x16 characters,
|
|
|
|
|
* which is 16384 glyphs. (Use a grayscale map and that's only 4 megs.) Let's use
|
|
|
|
|
* that as a cap. (We don't want to go crazy if someone says "range Unicode
|
|
|
|
|
* #0-FFFFFFFF".) */
|
|
|
|
|
if(cnt > 16384)
|
|
|
|
|
return ssprintf("Can't map %i glyphs to one font page", cnt);
|
|
|
|
|
|
|
|
|
|
while(cnt)
|
|
|
|
|
{
|
|
|
|
|
CharToGlyphNo[map_offset] = glyphno;
|
|
|
|
|
map_offset++;
|
|
|
|
|
glyphno++;
|
|
|
|
|
cnt--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const wchar_t *mapping = FontCharmaps::get_char_map(Mapping);
|
|
|
|
|
if(mapping == NULL)
|
|
|
|
|
return "Unknown mapping";
|
|
|
|
|
|
|
|
|
|
while(*mapping != 0 && map_offset) { mapping++; map_offset--; }
|
|
|
|
|
if(map_offset)
|
|
|
|
|
return "Map overflow"; /* there aren't enough characters in the map */
|
|
|
|
|
|
|
|
|
|
/* If cnt is -1, set it to the number of characters in the map. */
|
|
|
|
|
if(cnt == -1)
|
|
|
|
|
for(cnt = 0; mapping[cnt] != 0; ++cnt) ;
|
|
|
|
|
|
|
|
|
|
while(*mapping != 0)
|
|
|
|
|
{
|
|
|
|
|
if(*mapping != FontCharmaps::M_SKIP)
|
|
|
|
|
CharToGlyphNo[*mapping] = glyphno;
|
|
|
|
|
mapping++;
|
|
|
|
|
glyphno++;
|
|
|
|
|
cnt--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(cnt)
|
|
|
|
|
return "Map overflow"; /* there aren't enough characters in the map */
|
|
|
|
|
|
|
|
|
|
return "";
|
2003-01-10 02:02:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static CStringArray LoadStack;
|
|
|
|
|
|
|
|
|
|
/* A font set is a set of files, eg:
|
|
|
|
|
*
|
|
|
|
|
* Normal 16x16.png
|
|
|
|
|
* Normal [other] 16x16.png
|
|
|
|
|
* Normal [more] 8x8.png
|
|
|
|
|
* Normal 16x16.ini (the 16x16 here is optional)
|
|
|
|
|
*
|
|
|
|
|
* Only one texture is required; the INI is optional. [1] This is
|
|
|
|
|
* designed to be backwards-compatible.
|
|
|
|
|
*
|
|
|
|
|
* sFontOrTextureFilePath can be a partial path, eg.
|
|
|
|
|
* "Themes/default/Fonts/Normal"
|
|
|
|
|
* or a complete path to a texture file (in which case no other
|
|
|
|
|
* files will be searched for).
|
|
|
|
|
*
|
|
|
|
|
* The entire font can be redirected; that's handled in ThemeManager.
|
|
|
|
|
* Individual font files can not be redirected.
|
|
|
|
|
*
|
|
|
|
|
* TODO:
|
|
|
|
|
* [main]
|
|
|
|
|
* import=FontName,FontName2 (load other fonts)
|
|
|
|
|
*
|
|
|
|
|
* [1] If a file has no INI and sChars is not set, it will receive a default
|
|
|
|
|
* mapping of ASCII or ISO-8859-1 if the font has exactly 128 or 256 frames.
|
|
|
|
|
* However, if it doesn't, we don't know what it is and the font will receive
|
|
|
|
|
* no default mapping. A font isn't useful with no characters mapped.
|
|
|
|
|
*/
|
|
|
|
|
void Font::Load(const CString &sFontOrTextureFilePath, CString sChars)
|
|
|
|
|
{
|
|
|
|
|
/* Check for recursion (recursive imports). */
|
|
|
|
|
{
|
|
|
|
|
for(unsigned i = 0; i < LoadStack.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
if(LoadStack[i] == sFontOrTextureFilePath)
|
|
|
|
|
{
|
|
|
|
|
CString str = join("\n", LoadStack);
|
|
|
|
|
str += "\n" + sFontOrTextureFilePath;
|
|
|
|
|
RageException::Throw("Font import recursion detected\n%s", str.GetString());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LoadStack.push_back(sFontOrTextureFilePath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The font is not already loaded. Figure out what we have. */
|
|
|
|
|
// LOG->Trace( "FontManager::LoadFont(%s).", sFontFilePath.GetString() );
|
|
|
|
|
|
|
|
|
|
path = sFontOrTextureFilePath;
|
|
|
|
|
Chars = sChars;
|
|
|
|
|
|
|
|
|
|
/* Get the filenames associated with this font. */
|
|
|
|
|
CStringArray TexturePaths;
|
|
|
|
|
CString IniPath;
|
|
|
|
|
GetFontPaths(sFontOrTextureFilePath, TexturePaths, IniPath);
|
2003-01-16 22:49:22 +00:00
|
|
|
|
|
|
|
|
/* If we don't have at least one INI or at least one texture path,
|
|
|
|
|
* we have nothing at all. */
|
|
|
|
|
ASSERT(!IniPath.empty() || TexturePaths.size());
|
|
|
|
|
|
2003-01-10 02:02:51 +00:00
|
|
|
bool CapitalsOnly = false;
|
|
|
|
|
|
|
|
|
|
/* If we have an INI, load it. */
|
|
|
|
|
IniFile ini;
|
|
|
|
|
if( !IniPath.empty() )
|
|
|
|
|
{
|
|
|
|
|
ini.SetPath( IniPath );
|
|
|
|
|
ini.ReadFile();
|
|
|
|
|
ini.RenameKey("Char Widths", "main");
|
|
|
|
|
ini.GetValueB( "main", "CapitalsOnly", CapitalsOnly );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
/* If this is a top-level font (not a subfont), load the default font first. */
|
|
|
|
|
CStringArray ImportList;
|
|
|
|
|
if(LoadStack.size() == 1)
|
|
|
|
|
ImportList.push_back("default font");
|
|
|
|
|
|
|
|
|
|
/* Check to see if we need to import any other fonts. Do this
|
|
|
|
|
* before loading this font, so any characters in this font
|
|
|
|
|
* override imported characters. */
|
|
|
|
|
CString imports;
|
|
|
|
|
ini.GetValue( "main", "import", imports );
|
|
|
|
|
split(imports, ",", ImportList, true);
|
|
|
|
|
for(unsigned i = 0; i < ImportList.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
CString path = THEME->GetPathTo("Fonts", ImportList[i]);
|
|
|
|
|
Font subfont;
|
|
|
|
|
subfont.Load(path, "");
|
|
|
|
|
MergeFont(subfont);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Load each font page. */
|
|
|
|
|
for(unsigned i = 0; i < TexturePaths.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
FontPage *fp = new FontPage;
|
|
|
|
|
|
|
|
|
|
/* Grab the page name, eg "foo" from "Normal [foo].png". */
|
|
|
|
|
CString pagename = GetPageNameFromFileName(TexturePaths[i]);
|
|
|
|
|
|
|
|
|
|
/* Load settings for this page from the INI. */
|
|
|
|
|
FontPageSettings cfg;
|
|
|
|
|
LoadFontPageSettings(cfg, ini, TexturePaths[i], pagename, sChars);
|
|
|
|
|
|
|
|
|
|
/* Go. */
|
|
|
|
|
fp->Load(cfg);
|
|
|
|
|
|
|
|
|
|
/* Expect at least as many frames as we have premapped characters. */
|
|
|
|
|
/* Make sure that we don't map characters to frames we don't actually
|
|
|
|
|
* have. This can happen if the font is too small for an sChars. */
|
|
|
|
|
for(map<longchar,int>::const_iterator it = fp->m_iCharToGlyphNo.begin();
|
|
|
|
|
it != fp->m_iCharToGlyphNo.end(); ++it)
|
|
|
|
|
{
|
|
|
|
|
if(it->second < fp->m_pTexture->GetNumFrames()) continue; /* OK */
|
|
|
|
|
RageException::Throw( "The font '%s' maps %s to frame %i, but the font only has %i frames.",
|
|
|
|
|
TexturePaths[i].GetString(), WcharDisplayText(wchar_t(it->first)).GetString(), it->second, fp->m_pTexture->GetNumFrames() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG->Trace("Adding page %s (%s) to %s; %i glyphs",
|
|
|
|
|
TexturePaths[i].GetString(), pagename.GetString(),
|
|
|
|
|
sFontOrTextureFilePath.GetString(), fp->m_iCharToGlyphNo.size());
|
|
|
|
|
AddPage(fp);
|
|
|
|
|
|
|
|
|
|
/* If this is the first font loaded, or it's called "main", this page's
|
|
|
|
|
* properties become the font's properties. */
|
|
|
|
|
if(i == 0 || pagename == "main")
|
|
|
|
|
SetDefaultGlyph(fp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(CapitalsOnly)
|
|
|
|
|
CapsOnly();
|
|
|
|
|
|
|
|
|
|
if(m_iCharToGlyph.empty())
|
|
|
|
|
LOG->Warn("Font %s has no characters", sFontOrTextureFilePath.GetString());
|
|
|
|
|
|
|
|
|
|
LoadStack.pop_back();
|
|
|
|
|
}
|