Files
EgtGeomKernel/OsFont.cpp
T
Dario Sassi 41a38fef3b EgtGeomKernel 1.5f1 :
- aggiunta entità testo (con font Nfe e di sistema)
- in tutte le rotate ora l'angolo è in gradi
- aggiunta trasformazione Shear (scorrimento)
- aggiunta trsformazione LocToLoc
- Set/GetInfo specializzate per i diversi tipi di informazioni
- Copy e Relocate con possibilità di indicare l'entità di riferimento rispetto a cui inserire
- aggiunte trasformazioni a PolyLine.
2014-06-03 13:19:54 +00:00

518 lines
17 KiB
C++

//----------------------------------------------------------------------------
// EgalTech 2014-2014
//----------------------------------------------------------------------------
// File : OsFont.cpp Data : 01.06.14 Versione : 1.5f1
// Contenuto : Implementazione della classe NfeFont (formato font proprietario).
//
//
//
// Modifiche : 01.06.14 DS Creazione modulo.
//
//
//----------------------------------------------------------------------------
//--------------------------- Include ----------------------------------------
#include "stdafx.h"
#include "OsFont.h"
#include "/EgtDev/Include/EGkGeomDB.h"
#include "/EgtDev/Include/EGkGdbIterator.h"
#include "/EgtDev/Include/EGkCurve.h"
#include "/EgtDev/Include/EGkCurveAux.h"
#include "/EgtDev/Include/EgkCurveComposite.h"
#include "/EgtDev/Include/EgkCurveLine.h"
#include "/EgtDev/Include/EgkCurveBezier.h"
#include "/EgtDev/Include/EgtPointerOwner.h"
#include "/EgtDev/Include/EgnStringUtils.h"
#include "/EgtDev/Include/EGnStringConverter.h"
#include "/EgtDev/Include/EGnStringDecoder.h"
#include "/EgtDev/Include/EgnFileUtils.h"
using namespace std ;
//----------------------------------------------------------------------------
const int OSF_HSTD = 100 ;
//----------------------------------------------------------------------------
OsFont::OsFont( void)
{
m_hDC = nullptr ;
m_hFont = nullptr ;
}
//----------------------------------------------------------------------------
OsFont::~OsFont( void)
{
if ( m_hFont != nullptr)
DeleteObject( m_hFont) ;
m_hFont = nullptr ;
if ( m_hDC != nullptr)
ReleaseDC( NULL, m_hDC) ;
m_hDC = nullptr ;
}
//----------------------------------------------------------------------------
bool
OsFont::SetCurrFont( const string& sFont, int nWeight, bool bItalic,
double dHeight, double dRatio, double dAddAdvance)
{
string sFontUp = sFont ;
ToUpper( sFontUp) ;
// se il font non è già caricato
if ( m_sFont != sFontUp) {
// pulisco la path del font
m_sFont.clear() ;
// cancello eventuale precedente font
if ( m_hFont != nullptr)
DeleteObject( m_hFont) ;
// se necessario, recupero il device di schermo
if ( m_hDC == nullptr)
m_hDC = GetDC( nullptr) ;
if ( m_hDC == nullptr)
return false ;
::SetMapMode( m_hDC, MM_TEXT) ;
// creo il font
m_hFont = CreateFontW( - OSF_HSTD, 0, 0, 0, nWeight, bItalic, false, false, DEFAULT_CHARSET,
OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, DEFAULT_PITCH,
stringtoW( sFont)) ;
if ( m_hFont == nullptr)
return false ;
// aggiorno dati del font
if ( ! CalcFontData())
return false ;
// aggiorno la path del font
m_sFont = sFontUp ;
}
// salvo i dati
m_nWeight = nWeight ;
m_bItalic = bItalic ;
m_dHeight = dHeight ;
m_dRatio = dRatio ;
m_dAddAdvance = dAddAdvance ;
return true ;
}
//----------------------------------------------------------------------------
bool
OsFont::CalcFontData( void)
{
// verifico esistenza font corrente (non controllo nome)
if ( m_hDC == nullptr || m_hFont == nullptr)
return false ;
// rendo corrente il font
HFONT hPrevFont = (HFONT) SelectObject( m_hDC, m_hFont) ;
if ( hPrevFont == nullptr)
return false ;
// dati generali
TEXTMETRIC tm ;
if ( ! GetTextMetrics( m_hDC, &tm))
return false ;
double dCoeff = (( tm.tmHeight > 0) ? double( OSF_HSTD) / tm.tmHeight : 1) ;
m_dH = ( tm.tmHeight + tm.tmExternalLeading) * dCoeff ;
m_dHCap = ( tm.tmAscent - tm.tmInternalLeading) * dCoeff ;
m_dHx = 0.6667 * m_dHCap ; // approx
m_dAsc = tm.tmAscent * dCoeff ;
m_dDesc = tm.tmDescent * dCoeff ;
m_dAdv = tm.tmMaxCharWidth * dCoeff ;
// cerco dato più preciso su altezza delle maiuscole
GLYPHMETRICS gm ;
MAT2 mat = { {0,1},{0,0},{0,0},{0,1}} ;
// recupero l'altezza della lettera 'H' (72)
DWORD dwSize = GetGlyphOutlineW( m_hDC, 72, GGO_NATIVE, &gm, 0, NULL, &mat) ;
if ( dwSize != GDI_ERROR) {
m_dHCap = gm.gmBlackBoxY ;
m_dHx = 0.6667 * m_dHCap ; // approx
}
// ripristino il font originale
SelectObject( m_hDC, hPrevFont) ;
return true ;
}
//----------------------------------------------------------------------------
bool
OsFont::GetCapHeight( double& dCapH)
{
// verifico esistenza font corrente
if ( m_hDC == nullptr || m_hFont == nullptr || m_sFont.empty())
return false ;
// uso il fattore di scala per Y = m_dHeight / m_dHCap
dCapH = m_dHCap * m_dHeight / m_dHCap ;
return true ;
}
//----------------------------------------------------------------------------
bool
OsFont::GetAscent( double& dAsc)
{
// verifico esistenza font corrente
if ( m_hDC == nullptr || m_hFont == nullptr || m_sFont.empty())
return false ;
// uso il fattore di scala per Y = m_dHeight / m_dHCap
dAsc = m_dAsc * m_dHeight / m_dHCap ;
return true ;
}
//----------------------------------------------------------------------------
bool
OsFont::GetDescent( double& dDesc)
{
// verifico esistenza font corrente
if ( m_hDC == nullptr || m_hFont == nullptr || m_sFont.empty())
return false ;
// uso il fattore di scala per Y = m_dHeight / m_dHCap
dDesc = m_dDesc * m_dHeight / m_dHCap ;
return true ;
}
//----------------------------------------------------------------------------
bool
OsFont::GetLength( const string& sText, double& dLen)
{
// verifico esistenza font corrente
if ( m_hDC == nullptr || m_hFont == nullptr || m_sFont.empty())
return false ;
// rendo corrente il font
HFONT hPrevFont = (HFONT) SelectObject( m_hDC, m_hFont) ;
if ( hPrevFont == nullptr)
return false ;
// calcolo i fattori di scala
double dScaY = m_dHeight / m_dHCap ;
double dScaX = dScaY * m_dRatio ;
// converto i byte della stringa in codici carattere
UINTVECTOR vCode ;
GetCodePoints( sText, vCode) ;
// ciclo sui caratteri
dLen = 0 ;
for ( unsigned int i = 0 ; i < vCode.size() ; ++ i) {
// recupero l'avanzamento di ogni carattere
ABC abc ;
double dAdvance ;
if ( GetCharABCWidths( m_hDC, vCode[i], vCode[i], &abc))
dAdvance = ( abc.abcA + abc.abcB + abc.abcC) ;
else
dAdvance = m_dAdv ;
dLen += dAdvance * dScaX + m_dAddAdvance ;
}
// ripristino il font originale
SelectObject( m_hDC, hPrevFont) ;
return true ;
}
//----------------------------------------------------------------------------
bool
OsFont::GetBBox( const string& sText, BBox3d& b3Box)
{
// recupero le dimensioni
double dAsc ;
if ( ! GetAscent( dAsc))
return false ;
double dDesc ;
if ( ! GetDescent( dDesc))
return false ;
double dLen ;
if ( ! GetLength( sText, dLen))
return false ;
// assegno l'ingombro
b3Box.Set( 0, - dDesc, 0, dLen, dAsc, 0) ;
return true ;
}
//----------------------------------------------------------------------------
bool
OsFont::GetOutline( const string& sText, ICURVEPLIST& lstPC)
{
// verifico esistenza font corrente
if ( m_hDC == nullptr || m_hFont == nullptr || m_sFont.empty())
return false ;
// rendo corrente il font
HFONT hPrevFont = (HFONT) SelectObject( m_hDC, m_hFont) ;
if ( hPrevFont == nullptr)
return false ;
// calcolo i fattori di scala
double dScaY = m_dHeight / m_dHCap ;
double dScaX = dScaY * m_dRatio ;
double dScaZ = 1 ;
// converto i byte della stringa in codici carattere
UINTVECTOR vCode ;
GetCodePoints( sText, vCode) ;
// ciclo sui caratteri
Vector3d vtMove ;
ICURVEPLIST lstTmpPC ;
for ( unsigned int i = 0 ; i < vCode.size() ; ++ i) {
// recupero l'outline
double dAdvance ;
if ( ! GetCharOutline( vCode[i], dAdvance, lstTmpPC))
return false ;
// lo trasformo opportunamente
ICURVEPLIST::iterator iIter ;
for ( iIter = lstTmpPC.begin() ; iIter != lstTmpPC.end() ; ++ iIter) {
// trasformazioni
(*iIter)->Scale( GLOB_FRM, dScaX, dScaY, dScaZ) ;
(*iIter)->Translate( vtMove) ;
// inserimento nella lista principale
lstPC.push_back( (*iIter)) ;
}
vtMove += Vector3d( dAdvance * dScaX + m_dAddAdvance, 0, 0) ;
// ciclo di pulizia ( non devo cancellare le curve, perchè spostate nella lista principale)
lstTmpPC.clear() ;
}
// ripristino il font originale
SelectObject( m_hDC, hPrevFont) ;
return true ;
}
//----------------------------------------------------------------------------
bool
OsFont::ApproxWithLines( const string& sText, double dLinTol, double dAngTolDeg, POLYLINELIST& lstPL)
{
// verifico esistenza font corrente
if ( m_hDC == nullptr || m_hFont == nullptr || m_sFont.empty())
return false ;
// rendo corrente il font
HFONT hPrevFont = (HFONT) SelectObject( m_hDC, m_hFont) ;
if ( hPrevFont == nullptr)
return false ;
// calcolo i fattori di scala
double dScaY = m_dHeight / m_dHCap ;
double dScaX = dScaY * m_dRatio ;
double dScaZ = 1 ;
// converto i byte della stringa in codici carattere
UINTVECTOR vCode ;
GetCodePoints( sText, vCode) ;
// ciclo sui caratteri
Vector3d vtMove ;
ICURVEPLIST lstPC ;
for ( unsigned int i = 0 ; i < vCode.size() ; ++ i) {
// recupero l'outline
double dAdvance ;
if ( ! GetCharOutline( vCode[i], dAdvance, lstPC))
return false ;
// lo approssimo con segmenti di retta
ICURVEPLIST::iterator iIter ;
for ( iIter = lstPC.begin() ; iIter != lstPC.end() ; ++ iIter) {
lstPL.push_back( PolyLine()) ;
if ( (*iIter)->ApproxWithLines( dLinTol, dAngTolDeg, lstPL.back())) {
lstPL.back().Scale( GLOB_FRM, dScaX, dScaY, dScaZ) ;
lstPL.back().Translate( vtMove) ;
}
}
vtMove += Vector3d( dAdvance * dScaX + m_dAddAdvance, 0, 0) ;
// ciclo di pulizia
for ( iIter = lstPC.begin() ; iIter != lstPC.end() ; ++ iIter)
delete (*iIter) ;
lstPC.clear() ;
}
// ripristino il font originale
SelectObject( m_hDC, hPrevFont) ;
return true ;
}
//----------------------------------------------------------------------------
double
DoubleFromFixed( FIXED fx)
{
return ( double( fx.value) + double( fx.fract) * ( 1.0 / 65536.0)) ;
}
//----------------------------------------------------------------------------
FIXED
MediaTwoFixed( FIXED fx1, FIXED fx2)
{
long l = 0L ;
l = (*((long*)&(fx1)) + *((long*)&(fx2))) / 2L ;
return (*(FIXED*)&l) ;
}
//----------------------------------------------------------------------------
bool
OsFont::GetCharOutline( unsigned int nChar, double& dAdvance, ICURVEPLIST& lstPC)
{
GLYPHMETRICS gm ;
MAT2 mat = { {0,1},{0,0},{0,0},{0,1}} ;
// recupero la dimensione necessaria del buffer e lo alloco
DWORD dwSize = GetGlyphOutlineW( m_hDC, nChar, GGO_NATIVE, &gm, 0, NULL, &mat) ;
if ( dwSize == GDI_ERROR)
return false ;
PtrOwner<char> pBuffer( new char[dwSize]) ;
if ( ! ::IsValid( pBuffer))
return false ;
// recupero l'outline
DWORD dErr = GetGlyphOutlineW( m_hDC, nChar, GGO_NATIVE, &gm, dwSize, ::Get( pBuffer), &mat) ;
if ( dErr == GDI_ERROR) {
//delete pBuffer ;
return false ;
}
// assegno l'avanzamento al prossimo carattere
dAdvance = gm.gmCellIncX ;
// mentre esistono nuovi contorni
LPTTPOLYGONHEADER pHeader = (LPTTPOLYGONHEADER) ::Get( pBuffer) ;
while ( (LPSTR) pHeader < ( ::Get( pBuffer) + ptrdiff_t( dwSize))) {
// leggo un nuovo contorno
if ( pHeader->dwType == TT_POLYGON_TYPE) {
// creo curva composita
PtrOwner<ICurveComposite> pCCompo( CreateCurveComposite()) ;
if ( ! ::IsValid( pCCompo))
return false ;
// posizionamento sul punto iniziale
Point3d ptFirst( DoubleFromFixed( pHeader->pfxStart.x),
DoubleFromFixed( pHeader->pfxStart.y),
0) ;
Point3d ptLast = ptFirst ;
// puntatore alla prima polycurva
LPTTPOLYCURVE pCurve = (LPTTPOLYCURVE)( pHeader + 1) ;
// mentre esistono polycurve
while ( (LPSTR) pCurve < ( (LPSTR) pHeader + ptrdiff_t( pHeader->cb))) {
// numero punti
int nPnt = pCurve->cpfx ;
// se rette
if ( pCurve->wType == TT_PRIM_LINE) {
for ( int i = 0 ; i < nPnt ; ++ i ) {
Point3d ptP( DoubleFromFixed( pCurve->apfx[i].x),
DoubleFromFixed( pCurve->apfx[i].y),
0) ;
if ( ! AreSamePointNear( ptLast, ptP)) {
// aggiungo la retta alla curva composita
if ( ! AddLineToCompo( ::Get( pCCompo), ptLast, ptP))
return false ;
// salvo punto come ultimo
ptLast = ptP ;
}
}
}
// se curve di Bezier quadratiche
else if ( pCurve->wType == TT_PRIM_QSPLINE) {
for ( int i = 0 ; i < nPnt - 1 ; ++ i ) {
// punto medio CBezier
Point3d ptM( DoubleFromFixed( pCurve->apfx[i].x),
DoubleFromFixed( pCurve->apfx[i].y),
0) ;
// punto finale CBezier
POINTFX pfEnd ;
// se non è alla fine è il medio dei due punti adiacenti del poligono
if ( i < ( nPnt - 2)) {
pfEnd.x = MediaTwoFixed( pCurve->apfx[i].x, pCurve->apfx[i+1].x) ;
pfEnd.y = MediaTwoFixed( pCurve->apfx[i].y, pCurve->apfx[i+1].y) ;
}
else
pfEnd = pCurve->apfx[i+1] ;
Point3d ptE( DoubleFromFixed( pfEnd.x),
DoubleFromFixed( pfEnd.y),
0) ;
// aggiungo la curva di Bezier quadratica alla curva composita
if ( ! AddCBezierQuadToCompo( ::Get( pCCompo), ptLast, ptM, ptE))
return false ;
// salvo punto finale come ultimo
ptLast = ptE ;
}
}
// altrimenti errore
else {
return false ;
}
// si passa alla polycurva successiva
pCurve = (LPTTPOLYCURVE) &( pCurve->apfx[nPnt]) ;
}
// si aggiunge, se necessario, una linea retta per chiudere la curva
if ( ! AreSamePointNear( ptFirst, ptLast)) {
// aggiungo la retta alla curva composita
if ( ! AddLineToCompo( ::Get( pCCompo), ptLast, ptFirst))
return false ;
// salvo ultimo punto
ptLast = ptFirst ;
}
// se curva composita ok, la inserisco in lista
if ( pCCompo->IsValid())
lstPC.push_back( ::Release( pCCompo)) ;
}
// mi sposto sul prossimo contorno
pHeader = (LPTTPOLYGONHEADER) ( (LPSTR) pHeader + ptrdiff_t( pHeader->cb)) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
OsFont::AddLineToCompo( ICurveComposite* pCCompo, const Point3d& ptStart, const Point3d& ptEnd)
{
// verifico curva composita
if ( pCCompo == nullptr)
return false ;
// creo retta, la imposto e la inserisco nella curva composita
PtrOwner<ICurveLine> pCLine( CreateCurveLine()) ;
if ( ! ::IsValid( pCLine))
return false ;
if ( ! pCLine->Set( ptStart, ptEnd))
return false ;
if ( ! pCCompo->AddCurve( ::Release( pCLine)))
return false ;
return true ;
}
//----------------------------------------------------------------------------
bool
OsFont::AddCBezierQuadToCompo( ICurveComposite* pCCompo,
const Point3d& ptStart, const Point3d& ptMid, const Point3d& ptEnd)
{
// verifico curva composita
if ( pCCompo == nullptr)
return false ;
// creo curva di Bezier quadratica, la imposto e la inserisco nella curva composita
PtrOwner<ICurveBezier> pCBezier( CreateCurveBezier()) ;
if ( ! ::IsValid( pCBezier))
return false ;
if ( ! pCBezier->Init( 2, false) ||
! pCBezier->SetControlPoint( 0, ptStart) ||
! pCBezier->SetControlPoint( 1, ptMid) ||
! pCBezier->SetControlPoint( 2, ptEnd))
return false ;
if ( ! pCCompo->AddCurve( ::Release( pCBezier)))
return false ;
return true ;
}