/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: gdiFont
#Project: xdcGdi
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "gdiFont.h"
#include "xbXML.h"
#include "gdiPen.h"

gdiFontEngineScreen	g_gdiFontEngine("Gdi Font Engine");

gdiSizeLow	gdiFontObject::getFontSizeRef() const
{
	if (m_sizeRef.cx != -1)
		return m_sizeRef;

	IgdiDC &dc = m_pEngine->GetDC();
	dc.Select(*this);

	long spaceWidth = dc.GetTextSizeLow(L" ",1).cx;
	gdiSizeLow sz(dc.GetTextSizeLow(L"Yy",2));
	sz.cx /= 2;
	m_spaceWidth = spaceWidth;
	m_sizeRef = sz;
	return sz;
}

xbStringW gdiFontObject::getTextFace() const
{
	IgdiDC &dc = m_pEngine->GetDC();
	dc.Select(*this);
	return dc.GetTextFace();
}

void gdiFontObject::fontDestroy() const
{
	if (m_hFont == NULL)
		return;
	::DeleteObject(m_hFont);
	m_hFont = NULL;
}

void	gdiFontObject::fontCreate() const
{
	fontDestroy();

	int weight = (m_weight != gdiWeightDefault?int(m_weight):FW_NORMAL);
	if (m_bold) weight += (FW_BOLD - FW_NORMAL);
	if (weight < 0) weight = 0;
	else if (weight > 1000) weight = 1000;

	IgdiDC &dc = m_pEngine->GetDC();
	int sz = m_sz;
	if (sz < 0)
		sz = -MulDiv(sz, dc.GetPixelPerInchY(), gdiPOINT_PER_INCH);

	HFONT hFont = ::CreateFontW(m_sz,0,m_orient,m_orient,weight,
		m_italic?TRUE:FALSE,m_under?TRUE:FALSE,FALSE,
		(m_charSet != gdiCharSetDefault?m_charSet:ANSI_CHARSET),
		(m_precis != gdiPrecisDefault?m_precis:OUT_DEVICE_PRECIS),
		CLIP_CHARACTER_PRECIS,
		(m_quality != gdiQualityDefault?m_quality:DEFAULT_QUALITY),
		(m_pitch != gdiPitchDefault?m_pitch:DEFAULT_PITCH),
		(m_name.IsEmpty()?(LPCWSTR)NULL:m_name));
#ifdef _DEBUG
	int err = ::GetLastError();
#endif
	if (!hFont) 
		gdiEsys::Throw(xbHR_LAST,"gdiFontObject::fontCreate failure");

	m_hFont = hFont;

	try
	{
		dc.Select(*this);
		dc.TextMetrics((TEXTMETRICW*)&m_metrics);
	}
	catch(...)
	{
		fontDestroy();
		throw;
	}
}

ulong	gdiFontObject::getFontWidthRef() const
{
	if (m_sizeRef.cx != -1)
		return m_sizeRef.cx;
	return getFontSizeRef().cx;
}

ulong	gdiFontObject::getFontHeightRef() const
{
	if (m_sizeRef.cx != -1)
		return m_sizeRef.cy;
	return getFontSizeRef().cy;
}

ulong	gdiFontObject::getFontSpaceWidth() const
{
	if (m_sizeRef.cx != -1)
		return m_spaceWidth;
	getFontSizeRef();
	return m_spaceWidth;
}

const TEXTMETRICW &gdiFontObject::getFontMetrics() const
{
	return m_metrics;
}

void gdiFontObject::engineSet(gdiFontEngineBase *pEng)
{
	m_pEngine = pEng;
}

bool gdiFontObject::engineRelease()
{
	gdiFontEngineBase *pEng = m_pEngine;
	if (!pEng)
		return true;
	xbSectLock lck(&pEng->m_sect);
	if (!m_pEngine)
		return true;
	if (m_cRef)
		return false;
	m_pEngine = NULL;
	pEng->onFontReleased(this);
	return true;
}

gdiFontObject::operator HFONT() const
{
	xbRdbgAssert(m_hFont);
//	if (!m_font.m_hObject) 
//		fontCreate();
	return m_hFont;
}

HFONT gdiFontObject::GetHandle() const
{
	xbRdbgAssert(m_hFont);
//	if (!m_font.m_hObject) 
//		fontCreate();
	return m_hFont;
}

gdiFont::gdiFont(cbIgdiFontChanged *pCB,bool bEmpty)
{
	m_pEngine = &g_gdiFontEngine;
	m_pCB = pCB;
	m_bEmpty = bEmpty;
	m_sz = 0;
	m_orient = 0;
	m_shadDec = 0;
	m_weight = gdiWeightDefault;
	m_quality = gdiQualityDefault;
	m_precis = gdiPrecisDefault;
	m_charSet = gdiCharSetDefault;
	m_pitch = gdiPitchDefault;
	m_pParent = NULL;
	m_cChg = 0;
	m_cParentChg = 0;
	m_pFont = NULL;
}	

gdiFont::gdiFont(cbIgdiFontChanged *pCB,const gdiFont &font)
{
	m_pEngine = &g_gdiFontEngine;
	m_weight = gdiWeightDefault;
	m_quality = gdiQualityDefault;
	m_precis = gdiPrecisDefault;
	m_charSet = gdiCharSetDefault;
	m_pitch = gdiPitchDefault;
	m_pCB = NULL;
	m_bEmpty = true;
	m_sz = 0;
	m_orient = 0;
	m_shadDec = 0;
	m_pParent = NULL;
	m_cChg = 0;
	m_cParentChg = 0;
	m_pFont = NULL;
	Set(font);
	m_pCB = pCB;
}

gdiFont::gdiFont(gdiFont *pFontParent)
{
	m_pEngine = &g_gdiFontEngine;
	m_weight = gdiWeightDefault;
	m_quality = gdiQualityDefault;
	m_precis = gdiPrecisDefault;
	m_charSet = gdiCharSetDefault;
	m_pitch = gdiPitchDefault;
	m_bEmpty = pFontParent == NULL;
	m_sz = 0;
	m_orient = 0;
	m_shadDec = 0;
	m_cChg = 0;
	m_cParentChg = 0;
	m_pFont = NULL;
	m_pCB = NULL;
	m_pParent = pFontParent;
}

gdiFont::gdiFont(gdiFontEngineBase &engine,cbIgdiFontChanged *pCB,bool bEmpty)
{
	m_pEngine = &engine;
	m_pCB = pCB;
	m_bEmpty = bEmpty;
	m_sz = 0;
	m_orient = 0;
	m_shadDec = 0;
	m_weight = gdiWeightDefault;
	m_quality = gdiQualityDefault;
	m_precis = gdiPrecisDefault;
	m_charSet = gdiCharSetDefault;
	m_pitch = gdiPitchDefault;
	m_pParent = NULL;
	m_cChg = 0;
	m_cParentChg = 0;
	m_pFont = NULL;
}	

gdiFont::gdiFont(gdiFontEngineBase &engine,cbIgdiFontChanged *pCB,const gdiFont &font)
{
	m_pEngine = &engine;
	m_weight = gdiWeightDefault;
	m_quality = gdiQualityDefault;
	m_precis = gdiPrecisDefault;
	m_charSet = gdiCharSetDefault;
	m_pitch = gdiPitchDefault;
	m_pCB = NULL;
	m_bEmpty = true;
	m_sz = 0;
	m_orient = 0;
	m_shadDec = 0;
	m_pParent = NULL;
	m_cChg = 0;
	m_cParentChg = 0;
	m_pFont = NULL;
	Set(font);
	m_pCB = pCB;
}

gdiFont::gdiFont(gdiFontEngineBase &engine,gdiFont *pFontParent)
{
	m_pEngine = &engine;
	m_weight = gdiWeightDefault;
	m_quality = gdiQualityDefault;
	m_precis = gdiPrecisDefault;
	m_charSet = gdiCharSetDefault;
	m_pitch = gdiPitchDefault;
	m_bEmpty = pFontParent == NULL;
	m_sz = 0;
	m_orient = 0;
	m_shadDec = 0;
	m_cChg = 0;
	m_cParentChg = 0;
	m_pFont = NULL;
	m_pCB = NULL;
	m_pParent = pFontParent;
}

gdiFont::gdiFont(const gdiFont &font)
{
	m_pEngine = &g_gdiFontEngine;
	m_weight = gdiWeightDefault;
	m_quality = gdiQualityDefault;
	m_precis = gdiPrecisDefault;
	m_charSet = gdiCharSetDefault;
	m_pitch = gdiPitchDefault;
	m_pCB = NULL;
	m_bEmpty = true;
	m_sz = 0;
	m_shadDec = 0;
	m_orient = 0;
	m_pParent = NULL;
	m_cChg = 0;
	m_cParentChg = 0;
	m_pFont = NULL;
	Set(font);
}

void gdiFont::fontParentCheck()
{
	ulong cParentChg = m_pParent?m_pParent->GetFontChg():0;
	if (m_cParentChg == cParentChg)
		return;
	fontRelease();
	if (!(++m_cChg))
		m_cChg=1;
	m_cParentChg = cParentChg;
}

void gdiFont::fontCreate()
{
	xbRdbgAssert(!m_pFont);
	if (m_pFont)
		return;

	if (!m_orient && !HasValids())
	{
		if (!m_pParent)
		{
			m_pFont = m_pEngine->fontDefaultAddRef();
			return;
		}

		m_pFont = m_pParent->fontAddRef();
		return;
	}

	gdiFontSt fontSt;
	fontStFeed(&fontSt,gdiFONTST_ALL);

	m_pFont = m_pEngine->fontAddRef(fontSt);
}

void gdiFont::fontStFeed(gdiFontSt *pFontSt,ulong flags)
{
	if ((flags & gdiFONTST_NAME) && m_name.IsValid())
	{
		flags &= ~gdiFONTST_NAME;
		pFontSt->m_name = m_name;
	}
	if ((flags & gdiFONTST_SZ) && m_sz!=0)
	{
		flags &= ~gdiFONTST_SZ;
		pFontSt->m_sz = m_sz;
	}
	if ((flags & gdiFONTST_BOLD) && m_bold.IsValid())
	{
		flags &= ~gdiFONTST_BOLD;
		pFontSt->m_bold = m_bold;
	}
	if ((flags & gdiFONTST_ITALIC) && m_italic.IsValid())
	{
		flags &= ~gdiFONTST_ITALIC;
		pFontSt->m_italic = m_italic;
	}
	if ((flags & gdiFONTST_UNDER) && m_under.IsValid())
	{
		flags &= ~gdiFONTST_UNDER;
		pFontSt->m_under = m_under;
	}
	if ((flags & gdiFONTST_WEIGHT) && m_weight != gdiWeightDefault)
	{
		flags &= ~gdiFONTST_WEIGHT;
		pFontSt->m_weight = m_weight;
	}
	if ((flags & gdiFONTST_QUALITY) && m_quality != gdiQualityDefault)
	{
		flags &= ~gdiFONTST_QUALITY;
		pFontSt->m_quality = m_quality;
	}
	if ((flags & gdiFONTST_PRECIS) && m_precis != gdiPrecisDefault)
	{
		flags &= ~gdiFONTST_PRECIS;
		pFontSt->m_precis = m_precis;
	}
	if ((flags & gdiFONTST_CHARSET) && m_charSet != gdiCharSetDefault)
	{
		flags &= ~gdiFONTST_CHARSET;
		pFontSt->m_charSet = m_charSet;
	}
	if ((flags & gdiFONTST_PITCH) && m_pitch != gdiPitchDefault)
	{
		flags &= ~gdiFONTST_PITCH;
		pFontSt->m_pitch = m_pitch;
	}
	if ((flags & gdiFONTST_ORIENT) && m_orient!=0)
	{
		flags &= ~gdiFONTST_ORIENT;
		pFontSt->m_orient = m_orient;
	}
	if (!m_pParent || !flags)
		return;

	m_pParent->fontParentCheck();
	m_pParent->fontStFeed(pFontSt,flags);
}

gdiFont::operator HFONT() const
{
	if (m_bEmpty) 
		gdiEsys::Throw("gdiFont::operator HFONT() : empty font !");

	((gdiFont*)this)->fontCheck();

	return *m_pFont;
}
HFONT gdiFont::GetHandle() const
{
	if (m_bEmpty) 
		gdiEsys::Throw("gdiFont::operator HFONT() : empty font !");

	((gdiFont*)this)->fontCheck();

	return *m_pFont;
}

gdiFontObject	*gdiFont::fontAddRef()
{
	if (m_bEmpty) 
		return m_pEngine->fontDefaultAddRef();
	fontCheck();
	m_pFont->fontAddRef();
	return m_pFont;
}

bool	gdiFont::SetEmpty(bool bEmpty,bool update)
{
	if (bEmpty == m_bEmpty) return false;
	m_bEmpty = bEmpty;
	fontUpdate(update);
	return true;
}

void	gdiFont::fontUpdate(bool update)
{
	if (m_pFont)
		fontRelease();

	if (!(++m_cChg))
		m_cChg=1;

	if (update && m_pCB)
		m_pCB->onFontChanged(this);
}


gdiFont &gdiFont::operator=(const gdiFont &font)
{
	Set(font);
	return *this;
}

bool	gdiFont::Set(const gdiFont &font,bool update)
{
	if (*this == font) return false;

	m_bEmpty = font.m_bEmpty;
	m_name = font.m_name;
	m_sz = font.m_sz;
	m_shadDec = font.m_shadDec;
	m_bold = font.m_bold;
	m_italic = font.m_italic;
	m_under = font.m_under;

	m_weight = font.m_weight;
	m_quality = font.m_quality;
	m_precis = font.m_precis;
	m_charSet = font.m_charSet;
	m_pitch = font.m_pitch;

	fontUpdate(update);

	return true;
}

bool	gdiFont::SetAll(const xbStringW &str,int sz,bool bold,bool italic,bool under,bool update)
{
	if (!m_bEmpty && str == m_name && sz == m_sz && bold == m_bold && italic == m_italic
		&& under == m_under) return false;

	m_bEmpty = false;
	m_name = str;
	m_sz = sz;
	m_bold = bold;
	m_italic = italic;
	m_under = under;

	fontUpdate(update);

	return true;
}

bool	gdiFont::SetName(const xbStringW &str,bool update)
{
	if (!m_bEmpty && str == m_name) return false;
	m_bEmpty = false;
	m_name = str;
	fontUpdate(update);
	return true;
}

bool	gdiFont::SetSize(int sz,bool update)
{
	if (!m_bEmpty && sz == m_sz) return false;
	m_bEmpty = false;
	m_sz = sz;
	fontUpdate(update);
	return true;
}

bool	gdiFont::SetOrientation(int orient,bool update)
{
	if (orient == m_orient) return false;
	m_orient = orient;
	if (m_bEmpty) return false;
	fontUpdate(update);
	return true;
}

bool	gdiFont::SetUnderline(bool flg,bool update)
{
	if (!m_bEmpty && flg == m_under) return false;
	m_bEmpty = false;
	m_under = flg;
	fontUpdate(update);
	return true;
}

bool	gdiFont::SetItalic(bool flg,bool update)
{
	if (!m_bEmpty && flg == m_italic) return false;
	m_bEmpty = false;
	m_italic = flg;
	fontUpdate(update);
	return true;
}

bool	gdiFont::SetBold(bool flg,bool update)
{
	if (!m_bEmpty && flg == m_bold) return false;
	m_bEmpty = false;
	m_bold = flg;
	fontUpdate(update);
	return true;
}

bool	gdiFont::SetUnderline(const xbBool &flg,bool update)
{
	if (!m_bEmpty && flg == m_under) return false;
	m_bEmpty = false;
	m_under = flg;
	fontUpdate(update);
	return true;
}

bool	gdiFont::SetItalic(const xbBool &flg,bool update)
{
	if (!m_bEmpty && flg == m_italic) return false;
	m_bEmpty = false;
	m_italic = flg;
	fontUpdate(update);
	return true;
}

bool	gdiFont::SetBold(const xbBool &flg,bool update)
{
	if (!m_bEmpty && flg == m_bold) return false;
	m_bEmpty = false;
	m_bold = flg;
	fontUpdate(update);
	return true;
}

bool	gdiFont::SetShadowDec(int shadDec,bool update)
{
	if (!m_bEmpty && shadDec == m_shadDec) return false;
	m_bEmpty = false;
	m_shadDec = shadDec;
	if (update && m_pCB)
		m_pCB->onFontChanged(this);
	return true;
}

bool	gdiFont::SetPitch(gdiPitch val,bool update)
{
	if (!m_bEmpty && val == m_pitch) return false;
	m_bEmpty = false;
	m_pitch = val;
	fontUpdate(update);
	return true;
}

bool	gdiFont::SetWeight(gdiWeight val,bool update)
{
	if (!m_bEmpty && val == m_weight) return false;
	m_bEmpty = false;
	m_weight = val;
	fontUpdate(update);
	return true;
}

bool	gdiFont::SetQuality(gdiQuality val,bool update)
{
	if (!m_bEmpty && val == m_quality) return false;
	m_bEmpty = false;
	m_quality = val;
	fontUpdate(update);
	return true;
}

bool	gdiFont::SetPrecis(gdiPrecis val,bool update)
{
	if (!m_bEmpty && val == m_precis) return false;
	m_bEmpty = false;
	m_precis = val;
	fontUpdate(update);
	return true;
}

bool	gdiFont::SetCharSet(gdiCharSet val,bool update)
{
	if (!m_bEmpty && val == m_charSet) return false;
	m_bEmpty = false;
	m_charSet = val;
	fontUpdate(update);
	return true;
}

bool gdiFont::xmlSetParam(const xbString &prefix,const xbString &propname,const xbString &propvalue,bool bDefault,bool *pbUpdated)
{
	if (propname.Length <= prefix.Length
		|| (prefix.IsValid() && xbStrnicmp(propname,prefix,prefix.Length) != 0))
		return false;
	if (pbUpdated)
		*pbUpdated = false;

	LPCSTR pProp = (LPCSTR)propname+prefix.Length;
	if (xbStricmp(pProp,"fontname")==0 || xbStricmp(pProp,"face")==0)
	{
		if (bDefault)
			return true; //TODO: faire mieux avec une cb pour obtenir les valeurs par defaut a la place tcResetParams()

		if (SetName(xbStringW(propvalue),!pbUpdated) && pbUpdated)
			*pbUpdated = true;
		return true;
	}
	if (xbStricmp(pProp,"fontsize")==0 || xbStricmp(pProp,"size")==0)
	{
		if (bDefault)
			return true; //TODO: faire mieux avec une cb pour obtenir les valeurs par defaut a la place tcResetParams()

		long opl;
		const char *pchStop = NULL;
		if (xbStrToLong(propvalue,&opl,0,&pchStop)<=0)
			xbEapp::Throw("Invalid font size ("+propvalue+") specified !\nSyntax: size[px|pt]. Example: 16px or 8pt");
		for(;;)
		{
			if (!pchStop || !*pchStop)
			{
				break;
			}
			if (xbStricmp(pchStop,"pt")==0)
			{
				opl = -opl;
				break;
			}
			if (xbStricmp(pchStop,"px")==0)
			{
				break;
			}
			xbEapp::Throw("Invalid font size ("+propvalue+") specified !\nSyntax: size[px|pt], default is px. Example: 16px or 8pt");
		}
		if (SetSize(opl,!pbUpdated) && pbUpdated)
			*pbUpdated = true;
		return true;
	}
	if (xbStricmp(pProp,"shadowdec")==0)
	{
		if (bDefault)
			return true; //TODO: faire mieux avec une cb pour obtenir les valeurs par defaut a la place tcResetParams()
		if (SetShadowDec(::xbStrToInt(propvalue),!pbUpdated) && pbUpdated)
			*pbUpdated = true;
		return true;
	}
	if (xbStricmp(pProp,"italic")==0)
	{
		if (bDefault)
			return true; //TODO: faire mieux avec une cb pour obtenir les valeurs par defaut a la place tcResetParams()
		xbBool val;
		val.Set(propvalue);
		if (SetItalic(val,!pbUpdated) && pbUpdated)
			*pbUpdated = true;
		return true;
	}
	if (xbStricmp(pProp,"underline")==0)
	{
		if (bDefault)
			return true; //TODO: faire mieux avec une cb pour obtenir les valeurs par defaut a la place tcResetParams()
		xbBool val;
		val.Set(propvalue);
		if (SetUnderline(val,!pbUpdated) && pbUpdated)
			*pbUpdated = true;
		return true;
	}
	if (xbStricmp(pProp,"bold")==0)
	{
		if (bDefault)
			return true; //TODO: faire mieux avec une cb pour obtenir les valeurs par defaut a la place tcResetParams()
		xbBool val;
		val.Set(propvalue);
		if (SetBold(val,!pbUpdated) && pbUpdated)
			*pbUpdated = true;
		return true;
	}

	return false;
}

gdiSizeLow	gdiFont::GetFontSizeRef() const
{
	((gdiFont*)this)->fontCheck();
	return m_pFont->getFontSizeRef();
}

ulong	gdiFont::GetFontHeightRef() const
{
	((gdiFont*)this)->fontCheck();
	return m_pFont->getFontHeightRef();
}

ulong	gdiFont::GetFontWidthRef() const
{
	((gdiFont*)this)->fontCheck();
	return m_pFont->getFontWidthRef();
}

ulong	gdiFont::GetFontSpaceWidth() const
{
	((gdiFont*)this)->fontCheck();
	return m_pFont->getFontSpaceWidth();
}

const TEXTMETRICW &gdiFont::GetFontMetrics() const
{
	((gdiFont*)this)->fontCheck();
	return m_pFont->getFontMetrics();
}

gdiSizeLow	gdiFont::GetTextSize(IgdiDC &dc,const xbStringW &s,bool bShadowDec) const
{
	dc.Select(*this);

	gdiSizeLow sz(dc.GetTextSizeLow(s));

	if (bShadowDec) IncShadowSize(sz);
	
	return sz;
}

gdiSizeLow	gdiFont::GetTextSize(IgdiDC &dc,LPCWSTR pstr,int len,bool bShadowDec) const
{
	dc.Select(*this);

	gdiSizeLow sz(dc.GetTextSizeLow(pstr,len));

	if (bShadowDec) IncShadowSize(sz);
	
	return sz;
}

gdiSizeLow	gdiFont::GetTextSize(IgdiDC &dc,int width,LPCWSTR pstr,int len,bool bShadowDec) const
{
	if (width <= 0) 
		return GetTextSizeNorm(dc,pstr,len,bShadowDec);

	gdiRectLow rect(0,0,width,0);
	gdiSizeLow sz(0,0);

	DrawText(dc,rect,pstr,len,NULL,bShadowDec?gdiCOLOR_USED:NULL,&sz,NULL,NULL,NULL);

	return sz;
}

void gdiFont::DrawText(IgdiDC &dc,const gdiRectLow &rect,LPCWSTR pstr,int len,const gdiColor *pclr,const gdiColor *pshadclr,bool bMultiLine) const
{
	if (bMultiLine)
	{
		DrawText(dc,rect,pstr,len,pclr,pshadclr,NULL,NULL,NULL,NULL,NULL);
		return;
	}

	if (!pstr || !len) 
		return;

	bool bVert = false;
	if (m_orient == 900) 
		bVert = true;
	else 
	{
		if (m_orient != 0) 
			gdiEsys::Throw("gdiFont::DrawText() : orientation="+xbString(m_orient)+" not supported (0 or 900 only)");
	}

	dc.Select(*this);

	int shadDec = 0;
	int	xDec = 0,yDec = 0,xShadDec = 0,yShadDec = 0;		
	if (pclr && pshadclr && pclr->IsValid() && pshadclr->IsValid())
	{
		shadDec = GetShadowDec();
		if (bVert)
		{
			yDec = -shadDec;
			xShadDec = shadDec;
		}
		else
		{
			xShadDec = yShadDec = shadDec;
		}
	}
	else 
	{
		if (pclr && pclr->IsValid()) 
			dc.SetTextColor(*pclr);
	}

	gdiPointLow pt;

	pt.x = rect.left;
	if (bVert) pt.y = rect.bottom;
	else pt.y = rect.top;

	int opt = gdiText_Clipped;
	gdiBkMode bkmode = dc.GetBkMode();
	if (bkmode == gdiBk_Opaque) 
		opt |= gdiText_Opaque;

	if (shadDec)
	{
		dc.SetTextColor(*pshadclr);
		dc.TextOut(pt.x+xShadDec,pt.y+yShadDec,(gdiTextMode)opt,&rect,pstr,len);
		dc.SetBkMode(gdiBk_Transparent);
		dc.SetTextColor(*pclr);
		opt = gdiText_Clipped;
	}
	dc.TextOut(pt.x+xDec,pt.y+yDec,(gdiTextMode)opt,&rect,pstr,len);
	if (shadDec) 
		dc.SetBkMode(bkmode);
}

void gdiFont::DrawText(IgdiDC &dc,const gdiRectLow &rect,LPCWSTR pstr,int len,const gdiColor *pclr,const gdiColor *pshadclr,gdiSizeLow *pCalcSz,const gdiPointLow *pPtStart,gdiPointLow *pPtEnd,int *pHeightLn,const gdiPen *punderlinePen) const
{
	bool bCalcHeight = false;
	if (rect.IsEmpty())
	{
		if (!pCalcSz || rect.Width()<=0) 
			return;
		bCalcHeight = true;
	}
	if (!pstr || !len) 
		return;

	dc.Select(*this);

	bool bVert = false;
	gdiBkMode bkmode;
	if (!bCalcHeight)
	{
		if (m_orient == 900) 
			bVert = true;
		else
		{
			if (m_orient != 0) 
				gdiEsys::Throw("gdiFont::DrawText() : orientation="+xbString(m_orient)+" not supported (0 or 900 only)");
		}

		bkmode = dc.GetBkMode();
		if (bkmode == gdiBk_Opaque) 
		{
			dc.TextOut(0,0,gdiText_Opaque,&rect,NULL,0);
		}
		dc.SetBkMode(gdiBk_Transparent);
	}
	
	gdiPointLow pt;
	if (pPtStart) pt = *pPtStart;
	else
	{
		pt.x = rect.left;
		if (bVert) pt.y = rect.bottom;
		else pt.y = rect.top;
	}
	
	int		nch,nwch;
	gdiSizeLow	sz;
	int		width,height=0;
	if (pPtStart && pHeightLn) height = *pHeightLn;

	int shadDec = 0;
	int	xDec = 0,yDec = 0,xShadDec = 0,yShadDec = 0;		
	if ((pclr && pshadclr && pclr->IsValid() && pshadclr->IsValid())
		|| (bCalcHeight && pshadclr == gdiCOLOR_USED))
	{
		shadDec = GetShadowDec();
		if (bVert)
		{
			yDec = -shadDec;
			xShadDec = shadDec;
		}
		else
		{
			xShadDec = yShadDec = shadDec;
		}
	}
	else
	{
		if (!bCalcHeight && pclr && pclr->IsValid()) 
			dc.SetTextColor(*pclr);
	}

	if (bVert)
	{
		width = rect.Height() - (rect.bottom - pt.y) - shadDec;
	}
	else
	{
		width = rect.Width() - (pt.x - rect.left) - shadDec;
	}
	
	int xMax = 0;
	LPCWSTR	pstart = NULL;
	bool bContinue = true;
	bool bVideoInv = false,bUnderline = false;
	if (width <= 0) bContinue = false;
	while(bContinue)
	{
		if (len == 0) break;
		if (*pstr == 1) 
		{
			bVideoInv = !bVideoInv; 
			pstr++; len--;
			continue;
		}
		if (*pstr == 2) 
		{
			bUnderline = !bUnderline; 
			pstr++; len--;
			continue;
		}
		if (*pstr == L'\r')
		{
			pstr++; len--;
			continue;
		}
		if (*pstr == L'\n')
		{
			if (height == 0) 
				height = GetFontHeightPx();

			if (bVert)
			{
				pt.y = rect.bottom;
				pt.x += height;
				width = rect.Height() - shadDec;
				height = 0;
				if (!bCalcHeight && pt.x >= rect.right) bContinue = false;
			}
			else
			{
				pt.x = rect.left;
				pt.y += height;
				width = rect.Width() - shadDec;
				height = 0;
				if (!bCalcHeight && pt.y >= rect.bottom) bContinue = false;
			}
			pstr++; len--;
			continue;
		}
		pstart = pstr;
		while(len > 0 && *pstr == L' ') {pstr++;len--;}
		while(len > 0 && *pstr != L' ' && *pstr != 1 && *pstr != 2 
			&& *pstr != L'\n' && *pstr != L'\r') {pstr++;len--;}
		if (pstart == pstr) 
			break;

		bool bNL = false;
		while(bContinue)
		{
			nch = int((xbNptr(pstr) - xbNptr(pstart))/sizeof(wchar));
			nwch = 0;

			if (width > 0)
				dc.GetTextExtentLow(pstart,nch,width,&nwch,&sz);

			sz.cy = GetFontHeightPx();

			if (nwch < nch &&
				((bVert && pt.y < rect.bottom) || (!bVert && pt.x > rect.left)))
				nwch = 0;

			if (nwch > 0)
			{
				if (sz.cx > width) sz.cx = width;

				gdiRectLow rtxt(pt.x,pt.y,pt.x,pt.y);
				if (shadDec) {sz.cx += shadDec;sz.cy += shadDec;}
				if (sz.cy > height) height = sz.cy;
				if (bVert)
				{
					rtxt.top -= sz.cx;
					rtxt.right += sz.cy;
				}
				else
				{
					rtxt.right += sz.cx;
					rtxt.bottom += sz.cy;
				}
				gdiRectLow r;
				if (!bCalcHeight && r.Intersect(rtxt,rect))
				{
					LPCWSTR ptr = pstart;
					while(ptr < pstr && *ptr == L' ') ptr++;
					if (ptr < pstr)
					{
						if (shadDec)
						{
							dc.SetTextColor(*pshadclr);
							dc.TextOut(pt.x+xShadDec,pt.y+yShadDec,gdiText_Clipped,
								&r,pstart,nwch);
							dc.SetTextColor(*pclr);
						}
						dc.TextOut(pt.x+xDec,pt.y+yDec,gdiText_Clipped,&r,pstart,nwch);
						if (bUnderline)
						{
							gdiPen pen;
							if (!punderlinePen)
							{
								pen.Init(*pclr);
								punderlinePen = &pen;
							}
							dc.Select(*punderlinePen);
							if (bVert)
							{
								dc.MoveToLow(rtxt.right,rtxt.top);
								dc.LineToLow(rtxt.right,rtxt.bottom);
							}
							else
							{
								dc.MoveToLow(rtxt.left,rtxt.bottom);
								dc.LineToLow(rtxt.right,rtxt.bottom);
							}
						}
						if (bVideoInv) 
							dc.VideoInvert(r);
					}
				}
				if (bVert)
				{
					pt.y -= sz.cx;
				}
				else
				{
					pt.x += sz.cx;
					if (bCalcHeight && (pt.x - rect.left) > xMax)
						xMax = (pt.x - rect.left);
				}
				width -= sz.cx;
			}

			if (nch == nwch) break;
			pstart += nwch;
			if (height == 0) 
			{
				if (bNL) 
				{
					xbDbgOutput("gdiFont::DrawText() : width too little too write anything !");
					bContinue = false;
					break;
				}
				if (height == 0) 
					height = GetFontHeightPx();
			}
			bNL = true;
			if (bVert)
			{
				pt.y = rect.bottom;
				pt.x += height;
				width = rect.Height() - shadDec;
				height = 0;
				if (!bCalcHeight && pt.x >= rect.right) bContinue = false;
			}
			else
			{
				pt.x = rect.left;
				pt.y += height;
				width = rect.Width() - shadDec;
				height = 0;
				if (!bCalcHeight && pt.y >= rect.bottom) bContinue = false;
			}
			while(pstart < pstr && *pstart == L' ') pstart++;
			if (pstart == pstr) break;
		}
	}

	if (pPtEnd) *pPtEnd = pt;
	if (pCalcSz) 
	{
		pCalcSz->cx = xMax;
		pCalcSz->cy = pt.y + height - rect.top;
	}
	if (pHeightLn) *pHeightLn = height;

	if (!bCalcHeight) 
		dc.SetBkMode(bkmode);
}

gdiFontObject *gdiFontEngineBase::fontDefaultAddRef()
{
	gdiFontSt fontSt;
	return fontAddRef(fontSt);
}

gdiFontObject *gdiFontEngineBase::fontAddRef(const gdiFontSt &fontSt)
{
	xbLock lckStart(this);

	{
		::xbSectLock lck(&m_sect);
		gdiFontObject **ppFont = m_arrFont.SortedFind(&fontSt,fontFindCb);
		if (ppFont)
		{
			(*ppFont)->fontAddRef();
			return *ppFont;
		}
	}

	gdiFontObject *pFont = xbDnew gdiFontObject(fontSt);
	try
	{
		pFont->fontCreate();

		::xbSectLock lck(&m_sect);
		int pos;
		gdiFontObject **ppFont = m_arrFont.SortedFind(&fontSt,fontFindCb,&pos);
		if (ppFont)
		{
			(*ppFont)->fontAddRef();
			lck.Unlock();

			eProtect(pFont->fontRelease(););
			return *ppFont;
		}
		m_arrFont.Insert(pos, pFont);
		pFont->engineSet(this);
	}
	catch(...)
	{
		eProtect(pFont->fontRelease(););
		throw;
	}
	return pFont;
}

void gdiFontEngineBase::onFontReleased(gdiFontObject *pFont)
{
	::xbSectLock lck(&m_sect);
	int pos;
	gdiFontObject **ppFont = m_arrFont.SortedFind((gdiFontSt*)pFont,fontFindCb,&pos);
	xbRdbgAssert(ppFont && *ppFont == pFont);
	if (!ppFont)
		return;
	m_arrFont.Delete(pos);
}
