41a38fef3b
- 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.
518 lines
17 KiB
C++
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 ;
|
|
}
|