/*-----------------------------------------------------------------------------
[名称]	Unicoder.cpp
[概要]	ユニコード管理クラスの実装
[備考]	なし
[履歴]	2003-02-25	NC		コメント付加(moralog)
		2004-03-31 pnger	UnicodeToEntityRef()実装追加(moralog)
		2004-06-06 b0401111	(3344)共通設定のユニコード文字で追加のボタンを押すと強制終了(moralog)
-----------------------------------------------------------------------------*/

//-----------------------------------------------------------------------------
// インクルード
//-----------------------------------------------------------------------------
#include "stdafx.h"
#include "Unicoder.h"
#include "CharacterEntityReference.h"
#include "UnicodeFontDefine.h"
#include "wingdi.h"
#include <stdlib.h>
#include <ctype.h>
#include "Common.h"
#include "ExeCommon.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif


// 判定機能が存在するか？
// ▼ (3344) BGN 2004.06.07 (moralog) 関数の存在判定方法変更
// Unicode範囲取得関数
typedef DWORD (WINAPI *GETFONTUNICODERENGES)(
	HDC,
	LPGLYPHSET
);
HMODULE						s_hGDI32DLL( NULL );
GETFONTUNICODERENGES		s_pProcGETFONTUNICODERENGES( NULL );
// ▲ (3344) END


/*-----------------------------------------------------------------------------
[名称]	コンストラクタ
[機能]	インスタンスの生成。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
CUnicoder::CUnicoder()
: m_bEnable( TRUE )
{
// ▼ (3344) BGN 2004.06.07 (moralog) 関数の存在判定方法変更
	// Unicode範囲取得関数のDLL取得
	s_hGDI32DLL = ::LoadLibrary( _T("gdi32.dll") );
	if ( s_hGDI32DLL != NULL )
	{
		s_pProcGETFONTUNICODERENGES = (GETFONTUNICODERENGES)::GetProcAddress(
			s_hGDI32DLL,
			_T("GetFontUnicodeRanges")
		);
	}
// ▲ (3344) END
}


/*-----------------------------------------------------------------------------
[名称]	デストラクタ
[機能]	インスタンスの破棄。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
CUnicoder::~CUnicoder()
{
// ▼ (3344) BGN 2004.06.07 (moralog) 関数の存在判定方法変更
	// Unicode範囲取得関数用のDLL解放
	if ( s_hGDI32DLL != NULL )
	{
		::FreeLibrary( s_hGDI32DLL );
	}
// ▲ (3344) END
}


/*-----------------------------------------------------------------------------
[名称]	ユニコード→SJis変換（１文字）
[機能]	Unicode文字をSJisに変換する。
[戻値]	CString		変換済み文字列 [SJis] (-)
[備考]	必要であればHTML文字符号（SJis）に変換する。
		変換が必要なければそのままSJisコードに読み替える。
-----------------------------------------------------------------------------*/
int	CUnicoder::UnicodeToSJIS(
		const wchar_t	uniSrc,
		char*			sjisDst
)
{
	int		idxDst( 0 );
	{
		char		mbstrDst[3];
		wchar_t		wSrc[2];
		wSrc[0] = uniSrc;
		wSrc[1] = 0x00;
		size_t		size = wcstombs(
			mbstrDst,
			wSrc,
			3
		);

		// どうも255以下のときは wcstombs() が誤動作してるような気がする
		if( uniSrc >= 0x7F )
		if( uniSrc <= 0xFF )
		{
			// 変なものに変換してくれちゃうので、やめさせる
			size = (size_t)-1;
		}

		// 変換できないものは文字符号に直す
		if ( size != (size_t)-1 )
		{
			memcpy( sjisDst+idxDst, mbstrDst, size );
			idxDst += size;
		}
		else
		{
			int		nColumn = UnicodeToEntityRef(
				uniSrc,
				sjisDst+idxDst
			);
			if ( nColumn > 0 )
			{
				idxDst += nColumn;
			}
			else
			{
				sjisDst[idxDst++] = '&';
				sjisDst[idxDst++] = '#';
				_itoa( uniSrc, sjisDst+idxDst, 10 );
				idxDst += strlen( sjisDst+idxDst );
				sjisDst[idxDst++] = ';';
			}
		}
	}

	sjisDst[idxDst] = 0x00;
	return( idxDst );
}


/*-----------------------------------------------------------------------------
[名称]	ユニコード検査
[機能]	文字列がHTML文字符号かを検査し、文字符号であればUNICODEを返す。
[戻値]	long 文字符号バイト数　[byte] (0:文字符号でない、1〜:文字符号)
[備考]	10進表記、16進表記、実体参照に対応。
-----------------------------------------------------------------------------*/
long		CUnicoder::IsUnicode(
	const char*		pstrSrc,		// I:検査対象文字列 [-] (-) :-
	wchar_t*		pwcharUnicode	// O:Unicode [Unicode] (-) :-
)
{
	long	idxByte = 0;
	if ( m_bEnable )
	if ( pstrSrc[idxByte] == '&' )
	{
		idxByte++;

		if ( pstrSrc[idxByte] == '#' )
		{
			idxByte++;

			if ( pstrSrc[idxByte] == 'x' )
			{
				idxByte++;

				// 16進数タイプ
				wchar_t	lCode = 0;
				while( TRUE )
				{
					char iByte = pstrSrc[idxByte];
					if ( iByte == ';' )
					{
						idxByte++;
						*pwcharUnicode = lCode;
						break;
					}
					else
					if( 'a'<=iByte && iByte<='f')
					{
						lCode = (USHORT)(lCode*16 + 10+iByte-'a');
						idxByte++;
					}
					else
					if( 'A'<=iByte && iByte<='F')
					{
						lCode = (USHORT)(lCode*16 + 10+iByte-'A');
						idxByte++;
					}
					else
					if( '0'<=iByte && iByte<='9')
					{
						lCode = (USHORT)(lCode*16 + iByte-'0');
						idxByte++;
					}
					else
					{
						idxByte = (USHORT)0;
						break;
					}
				}
				return( idxByte );
			}
			// 10進数タイプ
			wchar_t	lCode = 0;
			while( TRUE )
			{
				char iByte = pstrSrc[idxByte];
				if ( iByte == ';' )
				{
					*pwcharUnicode = lCode;
					idxByte++;
					break;
				}
				else
				if( '0'<=iByte && iByte<='9')
				{
					lCode = (USHORT)(lCode*10 + iByte-'0');
					idxByte++;
				}
				else
				{
					idxByte = 0;
					break;
				}
			}
			return( idxByte );
		}
		// 実体参照
		int	nRef = sizeof(aEntitieRef)/sizeof(TEntitieRef);
		for( int idxRef=0 ; idxRef<nRef ; idxRef++ )
		{
			if ( memcmp(
				pstrSrc + idxByte,
				aEntitieRef[idxRef].pName,
				strlen( aEntitieRef[idxRef].pName )
			) == 0 )
			{
				if ( ';' == *(pstrSrc + idxByte + strlen( aEntitieRef[idxRef].pName )))
				{
					*pwcharUnicode = (USHORT)aEntitieRef[idxRef].uiCode; 
					idxByte += strlen( aEntitieRef[idxRef].pName ) + 1;
					return( idxByte );
				}
			}
		}
	}
	return( 0 );
}


/*-----------------------------------------------------------------------------
[名称]	描画
[機能]	ユニコード文字の描画。
[戻値]	なし
[備考]	なし
-----------------------------------------------------------------------------*/
void CUnicoder::DrawCharCode(
	HDC				hdc,			//   O:出力先DC [-] (-) :-
	const RECT&		rcDst,			// I  :出力先矩形 [pixel] (-) :-
	wchar_t			tCharCode		// I  :Unicode文字 [-] (-) :-
)
{
	HFONT	hfon = GetFont( hdc, tCharCode );
	HFONT	hfonOld = (HFONT)::SelectObject( hdc, hfon );
	// 文字の描画
	::TextOutW(
		hdc,
		rcDst.left,
		rcDst.top,
		&tCharCode,
		1
	);

	::SelectObject( hdc, hfonOld );
}


/*-----------------------------------------------------------------------------
[名称]	使用矩形計算
[機能]	ユニコード文字描画に要する矩形サイズの取得。
[戻値]	CSize	必要矩形 [pixel] (-)
[備考]	なし
-----------------------------------------------------------------------------*/
SIZE CUnicoder::ExtextCharCode(
	HDC				hdc,			//   O:出力先DC [-] (-) :-
	wchar_t			tCharCode		// I  :Unicode文字 [-] (-) :-
)
{
	HFONT	hfonOld = (HFONT)::SelectObject( hdc, GetFont( hdc, tCharCode ) );
	SIZE	sizeExtent;
	::GetTextExtentPoint32W(
		hdc,
		&tCharCode,
		1,
		&sizeExtent
	);
	::SelectObject( hdc, hfonOld );

	return( sizeExtent );
}


/*-----------------------------------------------------------------------------
[名称]	フォント取得
[機能]	ユニコード文字描画に要するフォントの取得。
[戻値]	CFont*	フォント [-] (-)
[備考]	なし
-----------------------------------------------------------------------------*/
HFONT CUnicoder::GetFont(
	HDC			hdc,		//   O:出力先DC [-] (-) :-
	wchar_t		tCharCode	// I  :Unicode文字 [-] (-) :-
)
{
	static	CLogFont	s_font;
	LPCTSTR	strDefaultFontName = _T("ＭＳ Ｐゴシック");
	BYTE	iDefaultChaeSet = SHIFTJIS_CHARSET;

	for( long idxBlock=0 ; idxBlock<sizeof(atUnicodeFontDefine)/sizeof(TUnicodeFontDefine) ; idxBlock++ )
	{
		if (atUnicodeFontDefine[idxBlock].iStartCode <= (long)tCharCode )
		if (atUnicodeFontDefine[idxBlock].iEndCode >= (long)tCharCode )
		{
//			strcpy( s_font.m_strFontName, atUnicodeFontDefine[idxBlock].strFontName );
			s_font.m_strFontName = atUnicodeFontDefine[idxBlock].strFontName;
			s_font.m_nCharSet = atUnicodeFontDefine[idxBlock].iCharSet;
//			if ( s_font.m_strFontName[0] == 0x00 )
			if ( s_font.m_strFontName.IsEmpty() )
			{
			// フォント名称が設定されていなければデフォルトフォントにする
//				strcpy( s_font.m_strFontName, strDefaultFontName );
				s_font.m_strFontName = strDefaultFontName;
				s_font.m_nCharSet = iDefaultChaeSet;
			}
			break;
		}
	}

	// 描画可能でなければデフォルトフォントにする
//	HFONT	font = s_font.GetFont( hdc );
	HFONT	font = (HFONT)*(s_font.GetFont( CDC::FromHandle( hdc ) ));
	if ( !IsDrawable( hdc, font, tCharCode ) )
	{
//		strcpy( s_font.m_strFontName, strDefaultFontName );
		s_font.m_strFontName = strDefaultFontName;
		s_font.m_nCharSet = iDefaultChaeSet;
	}

//	return( s_font.GetFont( hdc ) );
	return( *(s_font.GetFont( CDC::FromHandle( hdc ) ) ) );
}


/*-----------------------------------------------------------------------------
[名称]	唯一インスタンス取得
[機能]	唯一インスタンスの取得。
[戻値]	CUnicoder*	唯一インスタンス [-] (-)
[備考]	なし
-----------------------------------------------------------------------------*/
CUnicoder*	CUnicoder::GetInstance()
{
	static CUnicoder	Unicoder;
	return( &Unicoder );
}


/*-----------------------------------------------------------------------------
[名称]	Unicode→SJis変換（文字列）
[機能]	SJis文字列の取得。
[戻値]	CString		変換済み文字列 [SJis] (-)
[備考]	元SJis文字列の中の"?"に該当する部分だけを元Unicodeから変換する。
-----------------------------------------------------------------------------*/
long		CUnicoder::GetSJisString(
	const char*		pcstr,		// I  :元SJis文字列 [-] (-)
	const wchar_t*	pwcstr,		// I  :元Unicode文字列 [-] (-)
	char*			pDst
)
{
	long	nDstByte = GetSJisStringCore( pcstr, pwcstr, pDst );
	return( nDstByte );
}


/*-----------------------------------------------------------------------------
[名称]	Unicode→SJis変換
[機能]	SJis文字列の取得。
[戻値]	long	変換済みSJis長さ [byte] (-)
[備考]	元SJis文字列の中の"?"に該当する部分だけを元Unicodeから変換する。
		変換された部分はHTML文字符号で表現される。
-----------------------------------------------------------------------------*/
long	CUnicoder::GetSJisStringCore(
	const char*		pcstr,		// I  :元SJis文字列 [-] (-)
	const wchar_t*	pwcstr,		// I  :元Unicode文字列 [-] (-)
	char*			pDstBuffer	//   O:変換済みSJis文字列 [-] (-)
)
{
	// フツーにUNICODEから変換をかますと
	// 誤変換されることがあるっぽいので、必要な文字だけ変換してみる。

	long	nDstByte = 0;
	{
		// SJisを１文字づつ検査し、"?"を見つけたら該当するUNICODEデータから変換してみる
		long	idxUnicodeChar=0;
		while( *pcstr != 0x00 )
		{
			if( isleadbyte(*pcstr) != 0 )
			{
			// ２バイト文字
				if( pDstBuffer != NULL )
				{
					memcpy( pDstBuffer+nDstByte, pcstr, 2 );
				}
				pcstr+=2;
				nDstByte+=2;
			}
			else
			if ( *pcstr == '?' )
			{
				// Unicode文字かも？
				int			lenHTMLCharCode( 0 );
				if ( pDstBuffer != NULL )
				{
					lenHTMLCharCode = CUnicoder::UnicodeToSJIS(
						*(pwcstr+idxUnicodeChar),
						pDstBuffer+nDstByte
					);
				}
				else
				{
					char		strHTMLCharCode[HTMLCHARCODE_LENGTH];
					lenHTMLCharCode = CUnicoder::UnicodeToSJIS(
						*(pwcstr+idxUnicodeChar),
						strHTMLCharCode
					);
				}
				nDstByte += lenHTMLCharCode;
				pcstr++;
			}
			else
			{
				if( pDstBuffer != NULL )
				{
					*(pDstBuffer+nDstByte) = *pcstr;
				}
				nDstByte++;
				pcstr++;
			}
			idxUnicodeChar++;
		}
	}

	if( pDstBuffer != NULL )
	{
		pDstBuffer[nDstByte] = 0x00;
	}
	return( nDstByte );
}

/*-----------------------------------------------------------------------------
[名称]	Unicode→実体参照SJis変換
[機能]	ユニコードを実体参照に変換する。
[戻値]	int		変換済みSJis長さ [byte] (-)
[備考]	変換できなければ戻り値は0
-----------------------------------------------------------------------------*/
int		CUnicoder::UnicodeToEntityRef(
	const wchar_t	uniSrc,
	char*			sjisDst
)
{
	for( int idxItem=0 ; idxItem<sizeof(aEntitieRef)/sizeof(TEntitieRef) ; idxItem++ )
	{
		if ( aEntitieRef[idxItem].uiCode == uniSrc )
		{
			int	iLength = strlen( aEntitieRef[idxItem].pName );
			sjisDst[0] = '&';
			memcpy( sjisDst+1, aEntitieRef[idxItem].pName, iLength );
			sjisDst[ iLength+1 ] = ';';
			return( iLength+2 );
		}
	}
	return( 0 );
}

/*-----------------------------------------------------------------------------
[名称]	Unicode→SJis変換（文字列）
[機能]	SJis文字列の取得。
[戻値]	CString		変換済み文字列 [SJis] (-)
[備考]	元SJis文字列の中の"?"に該当する部分だけを元Unicodeから変換する。
-----------------------------------------------------------------------------*/
CString		CUnicoder::GetSJisString(
	const char*		pcstr,		// I  :元SJis文字列 [-] (-)
	const wchar_t*	pwcstr		// I  :元Unicode文字列 [-] (-)
)
{
	// まず格納に必要なバイト数を得る
	long	nDstByte = GetSJisStringCore( pcstr, pwcstr, NULL );

	// 変換済み文字列格納バッファを用意
	char*	pDstBuffer = new char[ nDstByte +1];

	// 変換処理
	GetSJisStringCore( pcstr, pwcstr, pDstBuffer );
	pDstBuffer[nDstByte] = 0x00;

	// 一時変数にバッファ内容を退避してバッファを解放
	CString	strDst = pDstBuffer;
	delete [] pDstBuffer;

	return( strDst );
}


/*-----------------------------------------------------------------------------
[名称]	描画可能確認
[機能]	選択中のフォントで文字コードが描画できるか確認する。
[戻値]	BOOL	描画可不可 [-] (TRUE:可、FALSE:不可)
[備考]	なし
-----------------------------------------------------------------------------*/
BOOL	CUnicoder::IsDrawable(
	HDC			hdc,		// I  :対象DC [-] (-) :-
	HFONT		font,		// I  :検査対象フォント [-] (-) :-
	wchar_t		tCharCode	// I  :Unicode文字 [-] (-) :-
)
{
	BOOL	bSupported( FALSE );

	// 判定機能が存在するか？
// ▼ (3344) BGN 2004.06.07 (moralog) 関数の存在判定方法変更
	GETFONTUNICODERENGES		pProc( NULL );
	pProc = s_pProcGETFONTUNICODERENGES;
/*
// ▽ original BGN
typedef DWORD (WINAPI *GETFONTUNICODERENGES)(
		HDC,
		LPGLYPHSET
	);

	GETFONTUNICODERENGES		pProc( NULL );
	{
		static BOOL					s_bChecked( FALSE );
		static GETFONTUNICODERENGES	s_pProc( NULL );
		if ( !s_bChecked )
		{

			CString	strPathName = ::GetExePathName();
			CString	strFileName;
			strFileName.Format(
				"%s.%s",
				::FilenameFromPathName( strPathName ),
				::ExtFromPathName( strPathName )
			);
			s_pProc = (GETFONTUNICODERENGES)::GetProcAddress(
				::GetModuleHandle( strFileName ),
				_T( "GetFontUnicodeRanges" )
			);
			s_bChecked = TRUE;
		}
		pProc = s_pProc;
	}
// △ original END
*/
// ▲ (3344) END

	if ( pProc != NULL )
	{
		HFONT	fontOld = (HFONT)::SelectObject( hdc, font );

		// フォントのサポート情報を得る
		GLYPHSET	tGlyphset;
		GLYPHSET*	pGlyphset = &tGlyphset;

		// レンジ情報のサイズを得る
		int	sizeRangeBuffer = pProc(
			hdc,
			NULL
		);

		// 必要な大きさのバッファを確保してレンジ情報取得
		int		size = sizeof(GLYPHSET) + sizeRangeBuffer;
		char*		pBuff = new char[size];
		memset( pBuff, 0x00, size );
		pGlyphset = (GLYPHSET*)pBuff;
		pGlyphset->cbThis = sizeof(GLYPHSET);
		pGlyphset->flAccel = 0;//GS_8BIT_INDICES;
//		pGlyphset->cGlyphsSupported;
		pGlyphset->cRanges = 0;
		pProc(
			hdc,
			pGlyphset
		);

		// 合致するレンジを捜す
		for( UINT idxRange=0 ; idxRange<pGlyphset->cRanges ; idxRange++ )
		{
			if ( pGlyphset->ranges[idxRange].wcLow <= tCharCode )
			if ( pGlyphset->ranges[idxRange].wcLow+pGlyphset->ranges[idxRange].cGlyphs > tCharCode )
			{
				// レンジが合致したので描画できる
				bSupported = TRUE;
				break;
			}
		}
		delete [] pBuff;

		::SelectObject( hdc, fontOld );
	}

	return( bSupported );
}


