Blob Blame Raw


#ifndef __LP64__

#include <vector>
#include <iostream>
#include <string>
#include "tmathutil.h"
#include "tdebugmessage.h"
#include "tfont.h"
#include "tstroke.h"
#include "tcurves.h"
#include "tconvert.h"
#include "tvectorimage.h"

using namespace std;

//----------------------------------------- structures -------------------------------------------------------------------

typedef struct {
	Float32Point origin;  // The origin of the current glyph
	Boolean first;		  // Keeps track of which segment is first in a glyph
	Float32Point current; // The current pen position (used to filter degenerate cases)

	float adv;
	TVectorImageP m_image;
	std::vector<TThickPoint> m_points;

} MyCurveCallbackData;

typedef struct {
	ATSGlyphRef glyphID;		 // The glyphID.  This is simply an index into a table in the font.
	Float32Point relativeOrigin; // The origin of this glyph -- relative to the origin of the line.
} MyGlyphRecord;

//----------------------------------------- callback---------------------------------------------------------------------

OSStatus MyQuadraticLineProc(const Float32Point *pt1, const Float32Point *pt2, void *callBackDataPtr)
{
	/*
    // Adjust the points according to the glyph origin
    float x1 = ((MyCurveCallbackData *)callBackDataPtr)->origin.x + pt1->x;
    float y1 = ((MyCurveCallbackData *)callBackDataPtr)->origin.y + pt1->y;
    float x2 = ((MyCurveCallbackData *)callBackDataPtr)->origin.x + pt2->x;
    float y2 = ((MyCurveCallbackData *)callBackDataPtr)->origin.y + pt2->y;
    */

	MyCurveCallbackData *data = (MyCurveCallbackData *)callBackDataPtr;

	if (data->m_points.empty())
		data->m_points.push_back(TThickPoint(pt1->x, pt1->y, 0));
	//else
	//assert(isAlmostEqual(pt1 e back)

	TThickPoint lastPoint = TThickPoint(pt2->x, pt2->y, 0);
	data->m_points.push_back((data->m_points.back() + lastPoint) * 0.5);
	data->m_points.push_back(lastPoint);

	return noErr;
}

OSStatus MyQuadraticCurveProc(const Float32Point *pt1, const Float32Point *controlPt, const Float32Point *pt2, void *callBackDataPtr)
{
	/*
    // Adjust the points according to the glyph origin    
    float x1 = ((MyCurveCallbackData *)callBackDataPtr)->origin.x + pt1->x;
    float y1 = ((MyCurveCallbackData *)callBackDataPtr)->origin.y + pt1->y;
    float x2 = ((MyCurveCallbackData *)callBackDataPtr)->origin.x + pt2->x;
    float y2 = ((MyCurveCallbackData *)callBackDataPtr)->origin.y + pt2->y;
    float cpx = ((MyCurveCallbackData *)callBackDataPtr)->origin.x + controlPt->x;
    float cpy = ((MyCurveCallbackData *)callBackDataPtr)->origin.y + controlPt->y;    
    */
	MyCurveCallbackData *data = (MyCurveCallbackData *)callBackDataPtr;

	if (data->m_points.empty())
		data->m_points.push_back(TThickPoint(pt1->x, pt1->y, 0));
	//else
	//assert(isAlmostEqual(pt1 e back)

	data->m_points.push_back(TThickPoint(controlPt->x, controlPt->y, 0));
	data->m_points.push_back(TThickPoint(pt2->x, pt2->y, 0));

	return noErr;
}

OSStatus MyQuadraticNewPathProc(void *callBackDataPtr)
{
	assert(((MyCurveCallbackData *)callBackDataPtr)->m_points.empty());
	return noErr;
}

OSStatus MyQuadraticClosePathProc(void *callBackDataPtr)
{
	MyCurveCallbackData *data = (MyCurveCallbackData *)callBackDataPtr;

	assert(data->m_points.size() >= 3 && data->m_points.size() & 1); //il numero di punti di controllo devono essere dispari e >= 3

	TStroke *stroke = new TStroke(data->m_points);
	stroke->setSelfLoop(true);

	data->m_points.clear();
	data->m_image->addStroke(stroke);

	return noErr;
}

//------------------------------------------------------------------------------------------------------------------

void GetGlyphIDsAndPositions(ATSUTextLayout iLayout, UniCharArrayOffset iStart, UniCharCount iLength, MyGlyphRecord **oGlyphRecordArray, ItemCount *oNumGlyphs)
{
	// This block of code uses the new Direct Access APIs, which are only available on Mac OS X 10.2 and later systems
	//

	ATSLayoutRecord *layoutRecords;
	ItemCount numRecords;
	Fixed *deltaYs;
	ItemCount numDeltaYs;
	unsigned int i;
	OSStatus status;

	// Get the arrays of glyph information
	status = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(
		iLayout, iStart,
		kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,
		(void **)&layoutRecords, &numRecords);
	assert(status == noErr);

	status = ATSUDirectGetLayoutDataArrayPtrFromTextLayout(
		iLayout, iStart,
		kATSUDirectDataBaselineDeltaFixedArray,
		(void **)&deltaYs, &numDeltaYs);
	assert(status == noErr);

	// Build the array of MyGlyphRecords
	*oGlyphRecordArray = (MyGlyphRecord *)malloc(numRecords * sizeof(MyGlyphRecord));
	*oNumGlyphs = numRecords;

	for (i = 0; i < *oNumGlyphs; i++) {
		// Fill in the glyphID
		(*oGlyphRecordArray)[i].glyphID = layoutRecords[i].glyphID;

		// Set up the relative origin of the glyph
		//
		// The real position is the x coordinate of the glyph, relative to the beginning of the line
		// The baseline delta (deltaY), if any, is the y coordinate of the glyph, relative to the baseline
		//
		(*oGlyphRecordArray)[i].relativeOrigin.x = Fix2X(layoutRecords[i].realPos);

		if (deltaYs == NULL) {
			(*oGlyphRecordArray)[i].relativeOrigin.y = 0.0;
		} else {
			(*oGlyphRecordArray)[i].relativeOrigin.y = 0.0 - Fix2X(deltaYs[i]);
		}
	}

	// Free the arrays of glyph information
	if (deltaYs != NULL) {
		status = ATSUDirectReleaseLayoutDataArrayPtr(NULL, kATSUDirectDataBaselineDeltaFixedArray, (void **)&deltaYs);
		assert(status == noErr);
	}
	status = ATSUDirectReleaseLayoutDataArrayPtr(NULL, kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, (void **)&layoutRecords);
	assert(status == noErr);
}

void drawQuadratics(ATSUTextLayout iLayout, ATSUStyle iStyle, UniCharArrayOffset start, UniCharCount length, MyCurveCallbackData &data)
{
	//boh ----------------
	Fixed penX = 0;
	Fixed penY = 0;
	// -------------------

	MyGlyphRecord *glyphRecordArray;
	ItemCount numGlyphs;
	ATSQuadraticNewPathUPP newPathProc;
	ATSQuadraticLineUPP lineProc;
	ATSQuadraticCurveUPP curveProc;
	ATSQuadraticClosePathUPP closePathProc;

	OSStatus status;
	unsigned int i;

	// Create the Quadratic callbacks
	newPathProc = NewATSQuadraticNewPathUPP(MyQuadraticNewPathProc);
	lineProc = NewATSQuadraticLineUPP(MyQuadraticLineProc);
	curveProc = NewATSQuadraticCurveUPP(MyQuadraticCurveProc);
	closePathProc = NewATSQuadraticClosePathUPP(MyQuadraticClosePathProc);

	// Get the array of glyph information
	GetGlyphIDsAndPositions(iLayout, start, length, &glyphRecordArray, &numGlyphs);

	// Loop over all the glyphs
	for (i = 0; i < numGlyphs; i++) {

		// Set up the absolute origin of the glyph
		data.origin.x = Fix2X(penX) + glyphRecordArray[i].relativeOrigin.x;
		data.origin.y = Fix2X(penY) + glyphRecordArray[i].relativeOrigin.y;

		// Reset state for quadratic drawing (the callbacks only do a MoveTo on the very first segment)
		data.first = true;

		// If this is a deleted glyph (-1), don't draw it.  Otherwise, go ahead.
		if (glyphRecordArray[i].glyphID != kATSDeletedGlyphcode) {
			status = ATSUGlyphGetQuadraticPaths(iStyle, glyphRecordArray[i].glyphID, newPathProc, lineProc, curveProc, closePathProc, &data, &status);
			assert(status == noErr);
		}
	}
	// Free the array of glyph information
	free(glyphRecordArray);

	// Dispose of the Quadratic callbacks

	DisposeATSQuadraticNewPathUPP(newPathProc);
	DisposeATSQuadraticLineUPP(lineProc);
	DisposeATSQuadraticCurveUPP(curveProc);
	DisposeATSQuadraticClosePathUPP(closePathProc);
}

//=============================================================================

struct TFont::Impl {
	bool m_hasKerning;
	int m_hasVertical;

	//  KerningPairs m_kerningPairs;

	ATSUStyle m_style;
	ATSUFontID m_fontId;
	ATSUTextLayout m_layout;
	Fixed m_size;
	int m_ascender;
	int m_descender;

	Impl(ATSUFontID fontId, int size);
	~Impl();

	//void getChar();
};

//-----------------------------------------------------------------------------

TFont::TFont(ATSUFontID fontId, int size)
{
	m_pimpl = new Impl(fontId, size);
}

//-----------------------------------------------------------------------------

TFont::~TFont()
{
	delete m_pimpl;
}

//-----------------------------------------------------------------------------

TFont::Impl::Impl(ATSUFontID fontId, int size)
	: m_fontId(fontId), m_size(Long2Fix(size))
{
	OSStatus status;

	long response;
	status = Gestalt(gestaltATSUFeatures, &response);
	assert(response & gestaltATSUDirectAccess);

	status = ATSUCreateStyle(&m_style);
	assert(status == noErr);

	ATSUAttributeTag tags[2];
	ByteCount sizes[2];
	ATSUAttributeValuePtr values[2];

	tags[0] = kATSUFontTag;
	sizes[0] = sizeof(ATSUFontID);
	values[0] = &fontId;

	tags[1] = kATSUSizeTag;
	sizes[1] = sizeof(Fixed);
	values[1] = &m_size;

	status = ATSUSetAttributes(m_style, 2, tags, sizes, values);
	//assert(status==noErr);

	UniChar dummyStr[] = {'H', 'e', 'l', 'l', 'o'};
	UniCharCount length = sizeof(dummyStr) / sizeof(UniChar);

	status = ATSUCreateTextLayoutWithTextPtr(
		dummyStr, kATSUFromTextBeginning, kATSUToTextEnd, length, 1,
		&length, &m_style, &m_layout);
	//assert(status==noErr);

	ATSTrapezoid glyphBounds;
	status = ATSUGetGlyphBounds(m_layout, 0, 0,
								kATSUFromTextBeginning, kATSUToTextEnd, kATSUseFractionalOrigins,
								1, &glyphBounds, NULL);

	m_ascender = -FixedToInt(glyphBounds.upperLeft.y);
	assert(m_ascender > 0);
	m_descender = -FixedToInt(glyphBounds.lowerLeft.y);
	assert(m_descender < 0);
}

//-----------------------------------------------------------------------------

TFont::Impl::~Impl()
{
}

//-----------------------------------------------------------------------------

TPoint TFont::drawChar(TVectorImageP &image, wchar_t charcode, wchar_t nextCharCode) const
{
	OSStatus status;

	UniChar subString[2];
	subString[0] = charcode;
	subString[1] = 0 /*nextCharCode*/;
	UniCharCount length = sizeof(subString) / sizeof(UniChar);

	status = ATSUCreateTextLayoutWithTextPtr(subString, kATSUFromTextBeginning, kATSUToTextEnd,
											 length, 1,
											 &length, &(m_pimpl->m_style), &(m_pimpl->m_layout));
	assert(status == noErr);

	MyCurveCallbackData data;
	data.m_image = image;

	drawQuadratics(m_pimpl->m_layout, m_pimpl->m_style, kATSUFromTextBeginning, kATSUToTextEnd, data);
	image->transform(TScale(1, -1));

	image->group(0, image->getStrokeCount());

	return getDistance(charcode, nextCharCode);
}

//-----------------------------------------------------------------------------
namespace
{

void appDrawChar(TRasterGR8P &outImage, TFont::Impl *pimpl, wchar_t charcode)
{
	OSStatus status;
	UniChar subString[2];
	subString[0] = charcode;
	subString[1] = 0;
	UniCharCount length = sizeof(subString) / sizeof(UniChar);

	status = ATSUCreateTextLayoutWithTextPtr(subString, kATSUFromTextBeginning, kATSUToTextEnd,
											 length, 1,
											 &length, &(pimpl->m_style), &(pimpl->m_layout));
	assert(status == noErr);

	ATSTrapezoid glyphBounds;
	status = ATSUGetGlyphBounds(pimpl->m_layout, 0, 0,
								kATSUFromTextBeginning, kATSUToTextEnd, kATSUseFractionalOrigins,
								1, &glyphBounds, NULL);

	int height = FixedToInt(glyphBounds.lowerLeft.y) - FixedToInt(glyphBounds.upperLeft.y);
	int width = tmax(FixedToInt(glyphBounds.lowerRight.x), FixedToInt(glyphBounds.upperRight.x)) -
				tmin(FixedToInt(glyphBounds.lowerLeft.x), FixedToInt(glyphBounds.upperLeft.x));

	outImage = TRasterGR8P(width, height);
	TPixelGR8 bgp;
	bgp.value = 255;
	outImage->fill(bgp);
	void *data = outImage->getRawData();

	CGColorSpaceRef grayColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
	CGContextRef gContext = CGBitmapContextCreate(data, width, height, 8, width, grayColorSpace, kCGImageAlphaNone);

#if defined(DEBUG) || defined(_DEBUG)

	int bpc = CGBitmapContextGetBitsPerComponent(gContext);
	if (bpc != 8)
		std::cout << "BitsPerComponent: " << bpc << std::endl;

	int bpp = CGBitmapContextGetBitsPerPixel(gContext);
	if (bpp != 8)
		std::cout << "BitsPerPixel: " << bpp << std::endl;

	int bytesPerRow = CGBitmapContextGetBytesPerRow(gContext);
	int newWidth = CGBitmapContextGetWidth(gContext);
	if (bytesPerRow != width || newWidth != width)
		std::cout << "BytesPerRow: " << bytesPerRow
				  << " Old width= " << width
				  << " New width= " << newWidth << std::endl;

	int newHeight = CGBitmapContextGetHeight(gContext);
	if (newHeight != height)
		std::cout << " Old height= " << height << " New height= " << newHeight << std::endl;

	assert(CGBitmapContextGetColorSpace(gContext) == grayColorSpace);

#endif

	ATSUAttributeTag tags[1];
	ByteCount sizes[1];
	ATSUAttributeValuePtr values[1];

	tags[0] = kATSUCGContextTag;
	sizes[0] = sizeof(CGContextRef);
	values[0] = &gContext;
	status = ATSUSetLayoutControls(pimpl->m_layout, 1, tags, sizes, values);
	assert(status == noErr);

	ATSUDrawText(pimpl->m_layout, kATSUFromTextBeginning, kATSUToTextEnd, 0, glyphBounds.lowerLeft.y);
}
}
//-----------------------------------------------------------------------------

TPoint TFont::drawChar(TRasterGR8P &outImage, TPoint &unused, wchar_t charcode, wchar_t nextCharCode) const
{
	appDrawChar(outImage, m_pimpl, charcode);
	outImage->yMirror();
	return getDistance(charcode, nextCharCode);
}

//-----------------------------------------------------------------------------

TPoint TFont::drawChar(TRasterCM32P &outImage, TPoint &unused, int inkId, wchar_t charcode, wchar_t nextCharCode) const
{
	TRasterGR8P grayAppImage;
	appDrawChar(grayAppImage, m_pimpl, charcode);

	int lx = grayAppImage->getLx();
	int ly = grayAppImage->getLy();

	outImage = TRasterCM32P(lx, ly);

	assert(TPixelCM32::getMaxTone() == 255);
	TPixelCM32 bgColor(0, 0, TPixelCM32::getMaxTone());
	grayAppImage->lock();
	outImage->lock();
	int ty = 0;
	for (int gy = ly - 1; gy >= 0; --gy, ++ty) {
		TPixelGR8 *srcPix = grayAppImage->pixels(gy);
		TPixelCM32 *tarPix = outImage->pixels(ty);
		for (int x = 0; x < lx; ++x) {
			int tone = srcPix->value;

			if (tone == 255)
				*tarPix = bgColor;
			else
				*tarPix = TPixelCM32(inkId, 0, tone);

			++srcPix;
			++tarPix;
		}
	}
	grayAppImage->unlock();
	outImage->unlock();

	return getDistance(charcode, nextCharCode);
}

//-----------------------------------------------------------------------------

TPoint TFont::getDistance(wchar_t firstChar, wchar_t secondChar) const
{
	OSStatus status;
	UniChar subString[2];
	subString[0] = firstChar;
	subString[1] = secondChar;
	UniCharCount length = sizeof(subString) / sizeof(UniChar);

	status = ATSUCreateTextLayoutWithTextPtr(subString, kATSUFromTextBeginning, kATSUToTextEnd,
											 length, 1,
											 &length, &(m_pimpl->m_style), &(m_pimpl->m_layout));
	assert(status == noErr);

	MyGlyphRecord *glyphRecordArray;
	ItemCount numGlyphs;

	// Get the array of glyph information
	GetGlyphIDsAndPositions(m_pimpl->m_layout, kATSUFromTextBeginning, kATSUToTextEnd, &glyphRecordArray, &numGlyphs);

	assert(numGlyphs >= 2);

	assert(glyphRecordArray[0].relativeOrigin.x == 0);
	int advance = (int)(glyphRecordArray[1].relativeOrigin.x - glyphRecordArray[0].relativeOrigin.x);
	if (advance == 0) {
		subString[1] = 0;
		status = ATSUCreateTextLayoutWithTextPtr(subString, kATSUFromTextBeginning, kATSUToTextEnd,
												 length, 1,
												 &length, &(m_pimpl->m_style), &(m_pimpl->m_layout));

		GetGlyphIDsAndPositions(m_pimpl->m_layout, kATSUFromTextBeginning, kATSUToTextEnd, &glyphRecordArray, &numGlyphs);
		advance = (int)(glyphRecordArray[1].relativeOrigin.x - glyphRecordArray[0].relativeOrigin.x);
	}
	return TPoint(advance, 0);
}

//-----------------------------------------------------------------------------

int TFont::getMaxHeight() const
{
	return m_pimpl->m_ascender - m_pimpl->m_descender;
}

//-----------------------------------------------------------------------------

int TFont::getMaxWidth() const
{
	assert(!"not implemented yet");
	return 100;
}
//-----------------------------------------------------------------------------

int TFont::getLineAscender() const
{
	return m_pimpl->m_ascender;
}

//-----------------------------------------------------------------------------

int TFont::getLineDescender() const
{
	return m_pimpl->m_descender;
}

//-----------------------------------------------------------------------------

bool TFont::hasKerning() const
{
	return true;
}

//-----------------------------------------------------------------------------

bool TFont::hasVertical() const
{
	return false;
}

//-----------------------------------------------------------------------------

#include <string>
#include <map>

typedef std::map<std::string, ATSUFontID> FontFamily;

typedef std::map<std::string, FontFamily> FamilyMap;

struct TFontManager::Impl {
	FamilyMap m_families;
	bool m_loaded;
	ATSUFontID m_currentAtsuFontId;
	TFont *m_currentFont;
	wstring m_currentFamily;
	wstring m_currentTypeface;
	int m_size;

	Impl()
		: m_currentAtsuFontId(0), m_currentFont(0), m_loaded(false), m_size(70)
	{
	}

	bool setFontName(ATSUFontID fontId, int platform, int script, int lang);
	bool addFont(ATSUFontID);
	void loadFontNames();
	bool setFont(std::wstring family, std::wstring style);
};

using namespace std;

bool TFontManager::Impl::setFontName(ATSUFontID fontId, int platform, int script, int lang)
{
	ByteCount oActualNameLength;
	ItemCount oFontCount;
	OSStatus status;

	char *buffer = 0;
	char *buffer2 = 0;

	// chiedo la lunhezza del Full Family Name per allocare il buffer
	status = ATSUFindFontName(fontId,
							  kFontFullName,
							  platform,
							  script,
							  lang,
							  0, 0,
							  &oActualNameLength,
							  0);

	if (status != noErr || oActualNameLength <= 1)
		return false;

	buffer = new char[oActualNameLength + 1];
	// chiedo il Full Family Name
	status = ATSUFindFontName(fontId,
							  kFontFullName,
							  platform,
							  script,
							  lang,
							  oActualNameLength, buffer,
							  &oActualNameLength,
							  &oFontCount);

	if (status != noErr || oActualNameLength <= 1 || buffer[0] == '\0') {
		delete[] buffer;
		return false;
	}
	buffer[oActualNameLength] = '\0';

	//-------------------

	// chiedo la lunhezza del Typeface Name per allocare il buffer
	status = ATSUFindFontName(fontId,
							  kFontStyleName,
							  platform,
							  script,
							  lang,
							  0, 0,
							  &oActualNameLength,
							  0);

	if (status != noErr || oActualNameLength <= 1) {
		delete[] buffer;
		return false;
	}
	buffer2 = new char[oActualNameLength + 1];

	// chiedo il Typeface Name
	status = ATSUFindFontName(fontId,
							  kFontStyleName,
							  platform,
							  script,
							  lang,
							  oActualNameLength, buffer2,
							  &oActualNameLength,
							  &oFontCount);

	if (status != noErr || oActualNameLength <= 1 || buffer2[0] == '\0') {
		delete[] buffer;
		delete[] buffer2;
		return false;
	} else
		buffer2[oActualNameLength] = '\0';

	string s_family(buffer);
	FontFamily &family = m_families[s_family];
	family[string(buffer2)] = fontId;
	delete[] buffer;
	delete[] buffer2;
	return true;
}

bool TFontManager::Impl::addFont(ATSUFontID fontId)
{
	int platform, script, lang;

	// per ottimizzare, ciclo solo sui valori
	// piu' comuni
	for (lang = -1; lang <= 0; lang++)
		for (platform = -1; platform <= 1; platform++)
			for (script = -1; script <= 0; script++)
				if (setFontName(fontId, platform, script, lang))
					return true;

	//poi li provo tutti
	for (lang = -1; lang <= 139; lang++)
		for (script = -1; script <= 32; script++)
			for (platform = -1; platform <= 4; platform++) {
				// escludo quelli nel tri-ciclo for precedente.
				// Purtoppo si deve fare cosi:
				// non si puo' fare partendo con indici piu' alti nei cicli for!
				if (-1 <= lang && lang <= 0 &&
					-1 <= script && script <= 0 &&
					-1 <= platform && platform <= 1)
					continue;

				if (setFontName(fontId, platform, script, lang))
					return true;
			}

	return false;
}

void TFontManager::Impl::loadFontNames()
{
	if (m_loaded)
		return;

	ItemCount oFontCount, fontCount;
	ATSUFontCount(&oFontCount);
	fontCount = oFontCount;
	ATSUFontID *oFontIDs = new ATSUFontID[fontCount];
	ATSUGetFontIDs(oFontIDs, fontCount, &oFontCount);
	assert(fontCount == oFontCount);

	for (unsigned int i = 0; i < fontCount; i++)
		addFont(oFontIDs[i]);

	delete[] oFontIDs;
	m_loaded = true;
}

bool TFontManager::Impl::setFont(std::wstring family, std::wstring typeface)
{
	if (family == m_currentFamily && (typeface == m_currentTypeface || typeface == L""))
		return false;

	FamilyMap::iterator family_it = m_families.find(toString(family));
	if (family_it == m_families.end())
		throw TFontCreationError();

	m_currentFamily = family;
	FontFamily::iterator style_it;
	if (typeface == L"") {
		style_it = ((*family_it).second).find(toString(m_currentTypeface));
		if (style_it == (*family_it).second.end())
			style_it = ((*family_it).second).begin();

		typeface = toWideString(style_it->first);
	} else
		style_it = ((*family_it).second).find(toString(typeface));

	if (style_it == (*family_it).second.end())
		throw TFontCreationError();

	m_currentTypeface = typeface;
	m_currentAtsuFontId = (*style_it).second;
	return true;
}

//---------------------------------------------------------

TFontManager::TFontManager()
{
	m_pimpl = new TFontManager::Impl();
}

//---------------------------------------------------------

TFontManager::~TFontManager()
{
	delete m_pimpl;
}

//---------------------------------------------------------

TFontManager *TFontManager::instance()
{
	static TFontManager theManager;
	return &theManager;
}

//---------------------------------------------------------

void TFontManager::loadFontNames()
{
	m_pimpl->loadFontNames();
}

//---------------------------------------------------------

void TFontManager::setFamily(const wstring family)
{
	bool changed = m_pimpl->setFont(family, L"");
	if (changed) {
		delete m_pimpl->m_currentFont;
		m_pimpl->m_currentFont = new TFont(m_pimpl->m_currentAtsuFontId, m_pimpl->m_size);
	}
}

//---------------------------------------------------------

void TFontManager::setTypeface(const wstring typeface)
{
	bool changed = m_pimpl->setFont(m_pimpl->m_currentFamily, typeface);
	if (changed) {
		delete m_pimpl->m_currentFont;
		m_pimpl->m_currentFont = new TFont(m_pimpl->m_currentAtsuFontId, m_pimpl->m_size);
	}
}

//---------------------------------------------------------

void TFontManager::setSize(int size)
{
	if (m_pimpl->m_size != size) {
		m_pimpl->m_size = size;
		delete m_pimpl->m_currentFont;
		m_pimpl->m_currentFont = new TFont(m_pimpl->m_currentAtsuFontId, m_pimpl->m_size);
	}
}

//---------------------------------------------------------

wstring TFontManager::getCurrentFamily() const
{
	return m_pimpl->m_currentFamily;
}

//---------------------------------------------------------

wstring TFontManager::getCurrentTypeface() const
{
	return m_pimpl->m_currentTypeface;
}

//---------------------------------------------------------

TFont *TFontManager::getCurrentFont()
{
	if (m_pimpl->m_currentFont)
		return m_pimpl->m_currentFont;

	if (!m_pimpl->m_currentFont)
		loadFontNames();

	assert(!m_pimpl->m_families.empty());
	setFamily(toWideString(m_pimpl->m_families.begin()->first));

	return m_pimpl->m_currentFont;
}

//---------------------------------------------------------

void TFontManager::getAllFamilies(vector<wstring> &families) const
{
	families.clear();
	families.reserve(m_pimpl->m_families.size());

	FamilyMap::iterator it = m_pimpl->m_families.begin();
	for (; it != m_pimpl->m_families.end(); ++it) {
		families.push_back(toWideString(it->first));
	}
}

//---------------------------------------------------------

void TFontManager::getAllTypefaces(vector<wstring> &typefaces) const
{
	typefaces.clear();
	FamilyMap::iterator it_family = m_pimpl->m_families.find(toString(m_pimpl->m_currentFamily));
	if (it_family == m_pimpl->m_families.end())
		return;
	FontFamily &typefaceSet = it_family->second;

	typefaces.reserve(typefaceSet.size());
	FontFamily::iterator it_typeface = typefaceSet.begin();
	for (; it_typeface != typefaceSet.end(); ++it_typeface) {
		typefaces.push_back(toWideString(it_typeface->first));
	}
}

//---------------------------------------------------------

void TFontManager::setVertical(bool vertical) {}

//---------------------------------------------------------

#endif