#include "cairo_font.h" cairo_font::cairo_font(IMLangFontLink2* fl, HFONT hFont, int size ) { init(); m_font_link = fl; if(m_font_link) { m_font_link->AddRef(); } m_size = size; set_font(hFont); } cairo_font::cairo_font(IMLangFontLink2* fl, LPCWSTR facename, int size, int weight, BOOL italic, BOOL strikeout, BOOL underline ) { init(); m_size = size; m_font_link = fl; if(m_font_link) { m_font_link->AddRef(); } LOGFONT lf; ZeroMemory(&lf, sizeof(lf)); if(!lstrcmpi(facename, L"monospace")) { wcscpy_s(lf.lfFaceName, LF_FACESIZE, L"Courier New"); } else if(!lstrcmpi(facename, L"serif")) { wcscpy_s(lf.lfFaceName, LF_FACESIZE, L"Times New Roman"); } else if(!lstrcmpi(facename, L"sans-serif")) { wcscpy_s(lf.lfFaceName, LF_FACESIZE, L"Arial"); } else if(!lstrcmpi(facename, L"fantasy")) { wcscpy_s(lf.lfFaceName, LF_FACESIZE, L"Impact"); } else if(!lstrcmpi(facename, L"cursive")) { wcscpy_s(lf.lfFaceName, LF_FACESIZE, L"Comic Sans MS"); } else { wcscpy_s(lf.lfFaceName, LF_FACESIZE, facename); } lf.lfHeight = -size; lf.lfWeight = weight; lf.lfItalic = italic; lf.lfCharSet = DEFAULT_CHARSET; lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = DEFAULT_QUALITY; lf.lfStrikeOut = strikeout; lf.lfUnderline = underline; HFONT fnt = CreateFontIndirect(&lf); set_font(fnt); } cairo_font::~cairo_font() { if(m_font_face) { cairo_font_face_destroy(m_font_face); } for(size_t i = 0; i < m_linked_fonts.size(); i++) { if(m_linked_fonts[i]->hFont) { m_font_link->ReleaseFont(m_linked_fonts[i]->hFont); } if(m_linked_fonts[i]->font_face) { cairo_font_face_destroy(m_linked_fonts[i]->font_face); } } m_linked_fonts.clear(); if(m_font_link) { m_font_link->AddRef(); } if(m_hFont) { DeleteObject(m_hFont); } } void cairo_font::show_text( cairo_t* cr, int x, int y, const char* str ) { lock(); text_chunk::vector chunks; split_text(str, chunks); cairo_set_font_size(cr, m_size); cairo_move_to(cr, x, y); for(size_t i = 0; i < chunks.size(); i++) { if(chunks[i]->font) { cairo_set_font_face(cr, chunks[i]->font->font_face); } else { cairo_set_font_face(cr, m_font_face); } cairo_show_text(cr, chunks[i]->text); } unlock(); if(m_bUnderline) { int tw = text_width(cr, chunks); lock(); cairo_set_line_width(cr, 1); cairo_move_to(cr, x, y + 1.5); cairo_line_to(cr, x + tw, y + 1.5); cairo_stroke(cr); unlock(); } if(m_bStrikeOut) { int tw = text_width(cr, chunks); cairo_font_metrics fm; get_metrics(cr, &fm); int ln_y = y - fm.x_height / 2; lock(); cairo_set_line_width(cr, 1); cairo_move_to(cr, x, (double) ln_y - 0.5); cairo_line_to(cr, x + tw, (double) ln_y - 0.5); cairo_stroke(cr); unlock(); } free_text_chunks(chunks); } void cairo_font::split_text( const char* src, text_chunk::vector& chunks ) { wchar_t* str = cairo_font::utf8_to_wchar(src); wchar_t* str_start = str; int cch = lstrlen(str); HDC hdc = GetDC(NULL); SelectObject(hdc, m_hFont); HRESULT hr = S_OK; while(cch > 0) { DWORD dwActualCodePages; long cchActual; if(m_font_link) { hr = m_font_link->GetStrCodePages(str, cch, m_font_code_pages, &dwActualCodePages, &cchActual); } else { hr = S_FALSE; } if(hr != S_OK) { break; } text_chunk* chk = new text_chunk; int sz = WideCharToMultiByte(CP_UTF8, 0, str, cchActual, chk->text, 0, NULL, NULL) + 1; chk->text = new CHAR[sz]; sz = WideCharToMultiByte(CP_UTF8, 0, str, cchActual, chk->text, sz, NULL, NULL); chk->text[sz] = 0; chk->font = NULL; if(!(dwActualCodePages & m_font_code_pages)) { for(linked_font::vector::iterator i = m_linked_fonts.begin(); i != m_linked_fonts.end(); i++) { if((*i)->code_pages == dwActualCodePages) { chk->font = (*i); break; } } if(!chk->font) { linked_font* lkf = new linked_font; lkf->code_pages = dwActualCodePages; lkf->hFont = NULL; m_font_link->MapFont(hdc, dwActualCodePages, 0, &lkf->hFont); if (lkf->hFont) { lkf->font_face = create_font_face(lkf->hFont); m_linked_fonts.push_back(lkf); } else { delete lkf; lkf = NULL; } chk->font = lkf; } } chunks.push_back(chk); cch -= cchActual; str += cchActual; } if(hr != S_OK) { text_chunk* chk = new text_chunk; int sz = WideCharToMultiByte(CP_UTF8, 0, str, -1, chk->text, 0, NULL, NULL) + 1; chk->text = new CHAR[sz]; sz = WideCharToMultiByte(CP_UTF8, 0, str, -1, chk->text, sz, NULL, NULL); chk->text[sz] = 0; chk->font = NULL; chunks.push_back(chk); } ReleaseDC(NULL, hdc); delete str_start; } void cairo_font::free_text_chunks( text_chunk::vector& chunks ) { for(size_t i = 0; i < chunks.size(); i++) { delete chunks[i]; } chunks.clear(); } cairo_font_face_t* cairo_font::create_font_face( HFONT fnt ) { LOGFONT lf; GetObject(fnt, sizeof(LOGFONT), &lf); return cairo_win32_font_face_create_for_logfontw(&lf); } int cairo_font::text_width( cairo_t* cr, const char* str ) { text_chunk::vector chunks; split_text(str, chunks); int ret = text_width(cr, chunks); free_text_chunks(chunks); return (int) ret; } int cairo_font::text_width( cairo_t* cr, text_chunk::vector& chunks ) { lock(); cairo_set_font_size(cr, m_size); double ret = 0; for(size_t i = 0; i < chunks.size(); i++) { if(chunks[i]->font) { cairo_set_font_face(cr, chunks[i]->font->font_face); } else { cairo_set_font_face(cr, m_font_face); } cairo_text_extents_t ext; cairo_text_extents(cr, chunks[i]->text, &ext); ret += ext.x_advance; } unlock(); return (int) ret; } void cairo_font::get_metrics(cairo_t* cr, cairo_font_metrics* fm ) { lock(); cairo_set_font_face(cr, m_font_face); cairo_set_font_size(cr, m_size); cairo_font_extents_t ext; cairo_font_extents(cr, &ext); cairo_text_extents_t tex; cairo_text_extents(cr, "x", &tex); fm->ascent = (int) ext.ascent; fm->descent = (int) ext.descent; fm->height = (int) (ext.ascent + ext.descent); fm->x_height = (int) tex.height; unlock(); } void cairo_font::set_font( HFONT hFont ) { clear(); m_hFont = hFont; m_font_face = create_font_face(m_hFont); m_font_code_pages = 0; if(m_font_link) { HDC hdc = GetDC(NULL); SelectObject(hdc, m_hFont); m_font_link->GetFontCodePages(hdc, m_hFont, &m_font_code_pages); ReleaseDC(NULL, hdc); } LOGFONT lf; GetObject(m_hFont, sizeof(LOGFONT), &lf); m_bUnderline = lf.lfUnderline; m_bStrikeOut = lf.lfStrikeOut; } void cairo_font::clear() { if(m_font_face) { cairo_font_face_destroy(m_font_face); m_font_face = NULL; } for(size_t i = 0; i < m_linked_fonts.size(); i++) { if(m_linked_fonts[i]->hFont && m_font_link) { m_font_link->ReleaseFont(m_linked_fonts[i]->hFont); } if(m_linked_fonts[i]->font_face) { cairo_font_face_destroy(m_linked_fonts[i]->font_face); } } m_linked_fonts.clear(); if(m_hFont) { DeleteObject(m_hFont); m_hFont = NULL; } } void cairo_font::init() { m_hFont = NULL; m_font_face = NULL; m_font_link = NULL; m_font_code_pages = 0; m_size = 0; m_bUnderline = FALSE; m_bStrikeOut = FALSE; } wchar_t* cairo_font::utf8_to_wchar( const char* src ) { if(!src) return NULL; int len = (int) strlen(src); wchar_t* ret = new wchar_t[len + 1]; MultiByteToWideChar(CP_UTF8, 0, src, -1, ret, len + 1); return ret; } char* cairo_font::wchar_to_utf8( const wchar_t* src ) { if(!src) return NULL; int len = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL); char* ret = new char[len]; WideCharToMultiByte(CP_UTF8, 0, src, -1, ret, len, NULL, NULL); return ret; }