mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-07-11 07:23:34 +08:00
408 lines
11 KiB
C++
408 lines
11 KiB
C++
|
|
//#include "stdafx.h"
|
|
#include "ap_Font.h"
|
|
|
|
namespace Aggplus
|
|
{
|
|
|
|
Font::Font()
|
|
{
|
|
Construct(NULL, 0.00, 0, UnitPixel);
|
|
}
|
|
|
|
Font::Font(const WCHAR *familyName, REAL emSize, INT style, Unit unit)
|
|
{
|
|
Construct(familyName, emSize, style, unit);//, Unit unit
|
|
}
|
|
|
|
|
|
void Font::Construct(const WCHAR* familyName, float emSize, int style, Unit unit)//
|
|
{
|
|
m_agg_pFeng=NULL;
|
|
|
|
m_femSize=0.00;
|
|
|
|
m_hDC=::GetDC(NULL);
|
|
|
|
if(emSize<1.0f)
|
|
{
|
|
/*assert(FALSE);*/ return;
|
|
} //Not implemented
|
|
if(!familyName || !(*familyName))
|
|
{
|
|
/*assert(FALSE);*/ return;
|
|
} //finished
|
|
m_Unit=unit;
|
|
|
|
m_agg_pFeng=new font_engine_type(m_hDC);
|
|
|
|
m_agg_pFeng->hinting(true);
|
|
m_agg_pFeng->flip_y(true);
|
|
m_agg_pFeng->height(emSize);
|
|
|
|
float ffSize=emSize;
|
|
|
|
switch(m_Unit)
|
|
{
|
|
case UnitDisplay : delete(m_agg_pFeng);m_agg_pFeng=NULL; return; break; //INVALID! 1 -- Variable -- for PageTransform only
|
|
case UnitWorld : // 0 -- World coordinate (non-physical unit)
|
|
case UnitPixel : break; // 2 -- Each unit is one device pixel.
|
|
default:
|
|
case UnitPoint : m_agg_pFeng->resolution(96);break; //ffSize=(emSize*96.0)/72.0; break; //100 // 3 -- Each unit is a printer's point, or 1/72 inch.
|
|
|
|
}
|
|
|
|
m_agg_pFeng->width(0.00);
|
|
//m_agg_pFeng->transform(agg::trans_affine_rotation(agg::deg2rad(00.0)));
|
|
|
|
agg::glyph_rendering gren = agg::glyph_ren_outline;
|
|
|
|
//z_Convert_wcharp_to_charp cFamName(familyName);
|
|
const WCHAR* pSour = familyName;
|
|
int n_Length=WideCharToMultiByte(CP_ACP, 0, pSour, -1, NULL, NULL, NULL, NULL);
|
|
char *pcStr=NULL;
|
|
if(n_Length)
|
|
{
|
|
pcStr=new char[n_Length];
|
|
WideCharToMultiByte(CP_ACP, 0, pSour, -1, pcStr, n_Length, NULL, NULL);
|
|
}
|
|
|
|
int nweight_=FW_REGULAR;
|
|
if(style&FontStyleBold) nweight_=FW_BOLD;
|
|
//lstrlen
|
|
BOOL bSucc=m_agg_pFeng->create_font(pcStr /*familyName*/ /*cFamName.m_pcStr*/, gren, ffSize, 0.00, nweight_,
|
|
(style&FontStyleItalic)!=0,
|
|
//(style&FontStyleUnderline)!=0,
|
|
//(style&FontStyleStrikeout)!=0,
|
|
DEFAULT_CHARSET, FF_DONTCARE);
|
|
if(pcStr) { delete(pcStr); }
|
|
//RUSSIAN_CHARSET, ANSI_CHARSET
|
|
//assert(bSucc);
|
|
//if(!bSucc) bSucc=m_agg_pFeng->create_font(cuStr.GetString(), gren, emSize, 0.00, FW_DONTCARE, false, false, false, DEFAULT_CHARSET, FF_DONTCARE);
|
|
if(bSucc)
|
|
{
|
|
int nSave_dc=SaveDC(m_hDC);
|
|
SetMapMode(m_hDC, MM_ISOTROPIC);
|
|
//SetWindowExtEx(m_hDC, 65500, 65500, NULL); //SetViewportExtEx(m_hDC, 100, 100, NULL);
|
|
SetWindowExtEx(m_hDC, 20480, 20480, NULL);
|
|
SetViewportExtEx(m_hDC, 1200, -1200, NULL);
|
|
|
|
//Default Values for Arial
|
|
int otmMacAscent=31;
|
|
int otmMacDescent=-7;
|
|
UINT otmMacLineGap=1;
|
|
UINT otmEMSquare=2048;
|
|
LONG tmAscent=34;
|
|
{
|
|
OUTLINETEXTMETRIC Otm;memset(&Otm, 0, sizeof(OUTLINETEXTMETRIC));
|
|
Otm.otmSize=sizeof(OUTLINETEXTMETRIC);
|
|
if(!GetOutlineTextMetrics(m_hDC, sizeof(OUTLINETEXTMETRIC), &Otm))
|
|
{
|
|
/*assert(FALSE);*/
|
|
} //FAILED
|
|
else
|
|
{
|
|
otmMacAscent=Otm.otmMacAscent;
|
|
otmMacDescent=Otm.otmMacDescent;
|
|
otmMacLineGap=Otm.otmMacLineGap;
|
|
otmEMSquare=Otm.otmEMSquare;
|
|
tmAscent=Otm.otmTextMetrics.tmAscent;
|
|
}
|
|
}
|
|
|
|
|
|
int n_mac_Ascn=otmMacAscent;
|
|
int n_mac_Desc=abs(otmMacDescent);
|
|
int n_mac_LGap=otmMacLineGap;
|
|
|
|
m_nEmHeight=(unsigned short)otmEMSquare;
|
|
int ntxtAscent=tmAscent;
|
|
m_nCellAscent=(n_mac_Ascn*m_nEmHeight)/ntxtAscent;
|
|
m_nCellDescent=(n_mac_Desc*m_nEmHeight)/ntxtAscent;
|
|
m_nLineSpacing=((n_mac_Ascn+n_mac_Desc+n_mac_LGap)*m_nEmHeight)/ntxtAscent;
|
|
|
|
m_femSize=emSize;
|
|
RestoreDC(m_hDC, nSave_dc);
|
|
}
|
|
else
|
|
{
|
|
delete(m_agg_pFeng);m_agg_pFeng=NULL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
Font::~Font()
|
|
{
|
|
if(m_agg_pFeng)
|
|
delete(m_agg_pFeng);
|
|
::ReleaseDC(NULL, m_hDC); m_hDC=0;
|
|
}
|
|
|
|
BOOL Font::IsAvailable() const
|
|
{
|
|
return(m_agg_pFeng!=NULL);
|
|
}
|
|
|
|
REAL Font::GetHeight(REAL dpi) const
|
|
{
|
|
double dblLineSpacing=GetLineSpacing();
|
|
double dblEmHeight=GetEmHeight();
|
|
//double dblResolution=m_agg_pFeng->resolution();
|
|
double dblMult=1.00;
|
|
if(m_Unit!=UnitPixel)
|
|
dblMult=dpi/72.00;
|
|
|
|
return REAL(dblLineSpacing*(m_femSize/dblEmHeight)*dblMult);
|
|
}
|
|
|
|
float Font::get_res_unit_to_pixel() const
|
|
{
|
|
switch(m_Unit)
|
|
{
|
|
case UnitPixel:return(1.0f);
|
|
default:
|
|
case UnitPoint:return(float(96.0/72.0));
|
|
}
|
|
}
|
|
|
|
void Font::MeasureChars2(const WCHAR* string, int length, const PointF &origin, MeasureCharsRectF *rcArray) const
|
|
{
|
|
if(!IsAvailable())
|
|
{ /*assert(FALSE);*/return; }
|
|
|
|
if(length<0)
|
|
length=(long)wcslen(string);
|
|
|
|
const WCHAR* p = string;
|
|
|
|
unsigned num_glyphs = 0;
|
|
|
|
typedef agg::font_cache_manager<font_engine_type> font_manager_type;
|
|
|
|
font_manager_type m_fman(*m_agg_pFeng);
|
|
|
|
//double m_weight_value=0.00;
|
|
double f_em_height = GetEmHeight();
|
|
double f_em_ascent = GetCellAscent();
|
|
double f_font_size=get_res_unit_to_pixel()*GetSize();
|
|
|
|
double m_height_value=f_font_size*f_em_ascent/f_em_height;
|
|
{
|
|
double x = origin.X;//+z_get_1_6_em();
|
|
double y0 = origin.Y+m_height_value;// - m_height_value - 10.0;
|
|
double y = y0;
|
|
|
|
//bool bFirst=false;
|
|
|
|
while(*p && length)
|
|
{
|
|
const agg::glyph_cache* glyph = m_fman.glyph(*p);
|
|
if(*p!='\n' && glyph)
|
|
{
|
|
m_fman.add_kerning(&x, &y);
|
|
//assert(glyph->data_type==agg::glyph_data_outline);
|
|
|
|
//if(!bFirst) { x-=glyph->bounds.x1; bFirst=true; }
|
|
rcArray[num_glyphs].X=float(x+glyph->bounds.x1);//+glyph->bounds.x1);
|
|
//rcArray[num_glyphs].X=float(x+glyph->bounds.x1);//+glyph->bounds.x1);
|
|
rcArray[num_glyphs].Y=float(y+glyph->bounds.y1);
|
|
rcArray[num_glyphs].Width=float(glyph->bounds.x2-glyph->bounds.x1);
|
|
rcArray[num_glyphs].Height=float(glyph->bounds.y2-glyph->bounds.y1);
|
|
rcArray[num_glyphs].StartX=float(x);
|
|
rcArray[num_glyphs].Advance=float(glyph->advance_x);
|
|
// increment pen position
|
|
x += glyph->advance_x;
|
|
y += glyph->advance_y;
|
|
++num_glyphs;
|
|
}
|
|
++p;
|
|
length--;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//ZTODO: Test new function
|
|
void aggplus_flat_measure_fit_chars(const Font *font, const WCHAR* string, int length, float fMaxWidth, float *pfLinStart, float *pfLinWidth, int *pnLinChars, int *pnAdvanceChars, bool *break_by_newline)
|
|
{
|
|
if(!font->IsAvailable())
|
|
{
|
|
/*assert(FALSE);*/return;
|
|
}
|
|
|
|
if(length<0) length=(long)wcslen(string);
|
|
|
|
if(!length) { *pfLinStart=*pfLinWidth=0.00;*pnLinChars=0; return; }
|
|
|
|
*pfLinStart=0.0f;
|
|
*break_by_newline=false;
|
|
|
|
const WCHAR* p = string;
|
|
|
|
typedef agg::font_cache_manager<font_engine_type> font_manager_type;
|
|
font_manager_type m_fman(*font->get_agg_font_engine());
|
|
|
|
int n_last_fit_nonspace=0;
|
|
double f_last_fit_nonspace_advance_width=0.00;
|
|
double f_last_fit_nonspace_blckbox_width=0.00;
|
|
int n_last_fit_allchars=0;
|
|
double f_last_fit_allchars_advance_width=0.00;
|
|
double f_last_fit_allchars_blckbox_width=0.00;
|
|
int n_plus_advance=0;
|
|
|
|
double f_prev_fit_advance_width=0.00;
|
|
double f_prev_fit_blckbox_width=0.00;
|
|
double f_next_fit_advance_width=0.00;
|
|
double f_next_fit_blckbox_width=0.00;
|
|
|
|
double y = 0.00;
|
|
WCHAR prev_char=0;//' ';
|
|
bool prev_char_is_white=false;
|
|
bool prev_char_is_begin_white=false;
|
|
int q=0;
|
|
bool bStop=false;
|
|
while(*p)
|
|
{
|
|
bool char_is_white=false;
|
|
bool char_is_begin_white=false;
|
|
|
|
const agg::glyph_cache* glyph = m_fman.glyph(*p);
|
|
if(!glyph) break;
|
|
//assert(glyph->data_type==agg::glyph_data_outline);
|
|
|
|
if(*p=='\n')
|
|
{
|
|
n_last_fit_nonspace=q;
|
|
f_last_fit_nonspace_blckbox_width=f_next_fit_blckbox_width;
|
|
n_plus_advance++;
|
|
*break_by_newline=true;
|
|
bStop=true;
|
|
}
|
|
else if(*p=='\r')
|
|
{
|
|
//Ignore it
|
|
n_plus_advance++;
|
|
}
|
|
else
|
|
{
|
|
if(!q)
|
|
*pfLinStart=(float)glyph->bounds.x1;
|
|
//else?
|
|
m_fman.add_kerning(&f_next_fit_advance_width, &y);
|
|
|
|
//***
|
|
f_next_fit_blckbox_width=f_next_fit_advance_width;
|
|
f_next_fit_advance_width+=glyph->advance_x;
|
|
f_next_fit_blckbox_width+=glyph->bounds.x2;
|
|
q++;
|
|
//***
|
|
|
|
switch(*p)
|
|
{
|
|
case ' ':
|
|
//char_is_white=true;
|
|
char_is_begin_white=true;
|
|
if(n_last_fit_allchars==0)
|
|
{
|
|
n_last_fit_nonspace=q;
|
|
f_last_fit_nonspace_advance_width=f_next_fit_advance_width;
|
|
f_last_fit_nonspace_blckbox_width=f_next_fit_blckbox_width;
|
|
//Check this:
|
|
n_last_fit_allchars=q;
|
|
f_last_fit_allchars_advance_width=f_next_fit_advance_width;
|
|
f_last_fit_allchars_blckbox_width=f_next_fit_blckbox_width;
|
|
}
|
|
else
|
|
{
|
|
if(!prev_char_is_begin_white)
|
|
{
|
|
n_last_fit_nonspace=n_last_fit_allchars;
|
|
f_last_fit_nonspace_advance_width=f_last_fit_allchars_advance_width;
|
|
f_last_fit_nonspace_blckbox_width=f_last_fit_allchars_blckbox_width;
|
|
}
|
|
|
|
}
|
|
break;
|
|
//ZTODO
|
|
case '(':case '[':case '{': //ZTODO: spec case
|
|
char_is_begin_white=true;
|
|
case '-':case '!':case '?':case '%':case ')':case ']':case '}':
|
|
char_is_white=true;
|
|
//break;
|
|
default:
|
|
if(f_next_fit_blckbox_width>=fMaxWidth)
|
|
{
|
|
//case when single word don't fit
|
|
if(n_last_fit_nonspace==0)
|
|
{
|
|
n_last_fit_nonspace=n_last_fit_allchars;
|
|
f_last_fit_nonspace_blckbox_width=f_last_fit_allchars_blckbox_width;
|
|
}
|
|
bStop=true;
|
|
}
|
|
else
|
|
{
|
|
if(char_is_begin_white)
|
|
{
|
|
if(!prev_char_is_begin_white)
|
|
{
|
|
n_last_fit_nonspace=n_last_fit_allchars;
|
|
f_last_fit_nonspace_advance_width=f_last_fit_allchars_advance_width;
|
|
f_last_fit_nonspace_blckbox_width=f_last_fit_allchars_blckbox_width;
|
|
}
|
|
char_is_white=false;
|
|
}
|
|
else if(prev_char_is_white)
|
|
{
|
|
n_last_fit_nonspace=n_last_fit_allchars;
|
|
f_last_fit_nonspace_advance_width=f_last_fit_allchars_advance_width;
|
|
f_last_fit_nonspace_blckbox_width=f_last_fit_allchars_blckbox_width;
|
|
}
|
|
|
|
|
|
n_last_fit_allchars=q;
|
|
f_last_fit_allchars_advance_width=f_next_fit_advance_width;
|
|
f_last_fit_allchars_blckbox_width=f_next_fit_blckbox_width;
|
|
}
|
|
|
|
break;
|
|
}
|
|
f_prev_fit_blckbox_width=f_prev_fit_advance_width;
|
|
f_prev_fit_advance_width+=glyph->advance_x;
|
|
f_prev_fit_blckbox_width+=glyph->bounds.x2;
|
|
}
|
|
if(bStop)
|
|
break;
|
|
|
|
length--;
|
|
|
|
if(!length)
|
|
{
|
|
n_last_fit_nonspace=n_last_fit_allchars;
|
|
f_last_fit_nonspace_blckbox_width=f_last_fit_allchars_blckbox_width;
|
|
break;
|
|
}
|
|
prev_char=*p;
|
|
prev_char_is_white=char_is_white;
|
|
prev_char_is_begin_white=char_is_begin_white;
|
|
|
|
++p;
|
|
y += glyph->advance_y;
|
|
}
|
|
|
|
*pnLinChars=n_last_fit_nonspace;
|
|
*pnAdvanceChars=n_last_fit_nonspace+n_plus_advance;
|
|
*pfLinWidth=(float)f_last_fit_nonspace_blckbox_width;
|
|
}
|
|
|
|
double Font::get_1_6_em() const
|
|
{
|
|
return (GetSize()*get_res_unit_to_pixel())/6.00;
|
|
|
|
}
|
|
|
|
}
|
|
|