#include <config.h>
#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
#if !defined(WINVER) || (WINVER < 0x0500)
# ifdef WINVER
# undef WINVER
# endif
# define WINVER 0x0500
#endif
#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
# ifdef _WIN32_WINNT
# undef _WIN32_WINNT
# endif
# define _WIN32_WINNT 0x0500
#endif
#include "Fl_GDI_Graphics_Driver.H"
#include "../../flstring.h"
#include <FL/Fl.H>
#include <FL/fl_draw.H>
#include <FL/platform.H>
#include "Fl_Font.H"
#include <stdio.h>
#include <stdlib.h>
#include <FL/fl_string_functions.h>
#include <FL/fl_utf8.h>
#ifdef __CYGWIN__
# include <wchar.h>
#endif
#define ENDOFBUFFER 127
const char* Fl_GDI_Graphics_Driver::get_font_name(Fl_Font fnum, int* ap) {
Fl_Fontdesc *f = fl_fonts + fnum;
if (!f->fontname[0]) {
const char* p = f->name;
if (!p || !*p) {if (ap) *ap = 0; return "";}
int type;
switch (*p) {
case 'B': type = FL_BOLD; break;
case 'I': type = FL_ITALIC; break;
case 'P': type = FL_BOLD | FL_ITALIC; break;
default: type = 0; break;
}
strlcpy(f->fontname, p+1, ENDOFBUFFER);
if (type & FL_BOLD) strlcat(f->fontname, " bold", ENDOFBUFFER);
if (type & FL_ITALIC) strlcat(f->fontname, " italic", ENDOFBUFFER);
f->fontname[ENDOFBUFFER] = (char)type;
}
if (ap) *ap = f->fontname[ENDOFBUFFER];
return f->fontname;
}
static int fl_free_font = FL_FREE_FONT;
static void set_font_name(const char ft, char *fn) {
fn[0] = ft;
Fl::set_font((Fl_Font)(fl_free_font++), fl_strdup(fn));
}
static int CALLBACK
enumcbw(CONST LOGFONTW *lpelf,
CONST TEXTMETRICW * ,
DWORD ,
LPARAM p) {
if (!p && lpelf->lfCharSet != ANSI_CHARSET) return 1;
char *fn = NULL; unsigned lw = (unsigned)wcslen(lpelf->lfFaceName);
unsigned dstlen = fl_utf8fromwc(fn, 0, (wchar_t*)lpelf->lfFaceName, lw); fn = (char*)malloc((size_t)dstlen + 2); if (!fn) return 1;
fn[0] = ' ';
dstlen = fl_utf8fromwc(fn+1, dstlen+1, (wchar_t*)lpelf->lfFaceName, lw); fn[dstlen + 1] = 0;
for (int i = 0; i < FL_FREE_FONT; i++) {
if (!strcmp(Fl::get_font_name((Fl_Font)i), fn+1)) {
free(fn);
return 1;
}
}
set_font_name(' ', fn);
if (lpelf->lfWeight <= 400)
set_font_name('B', fn);
set_font_name('I', fn);
if (lpelf->lfWeight <= 400)
set_font_name('P', fn);
free(fn);
return 1;
}
Fl_Font Fl_GDI_Graphics_Driver::set_fonts(const char* xstarname) {
HDC gc = (HDC)fl_graphics_driver->gc();
if (fl_free_font == FL_FREE_FONT) { if (!gc) gc = fl_GetDC(0);
EnumFontFamiliesW(gc, NULL, (FONTENUMPROCW)enumcbw, xstarname != 0);
}
return (Fl_Font)fl_free_font;
}
static int nbSize;
static int cyPerInch;
static int sizes[128];
static int CALLBACK
EnumSizeCbW(CONST LOGFONTW * ,
CONST TEXTMETRICW *lpntm,
DWORD fontType,
LPARAM ) {
if ((fontType & RASTER_FONTTYPE) == 0) {
sizes[0] = 0;
nbSize = 1;
return 0;
}
int add = lpntm->tmHeight - lpntm->tmInternalLeading;
add = MulDiv(add, 72, cyPerInch);
int start = 0;
while ((start < nbSize) && (sizes[start] < add)) {
start++;
}
if ((start < nbSize) && (sizes[start] == add)) {
return 1;
}
for (int i=nbSize; i>start; i--) sizes[i] = sizes[i - 1];
sizes[start] = add;
nbSize++;
return nbSize < 128;
}
int Fl_GDI_Graphics_Driver::get_font_sizes(Fl_Font fnum, int*& sizep) {
nbSize = 0;
Fl_Fontdesc *s = fl_fonts+fnum;
if (!s->name) s = fl_fonts;
HDC gc = (HDC)fl_graphics_driver->gc();
if (!gc) gc = fl_GetDC(0);
cyPerInch = GetDeviceCaps(gc, LOGPIXELSY);
if (cyPerInch < 1) cyPerInch = 1;
const char *nm = (const char*)s->name+1;
size_t len = strlen(s->name+1);
unsigned l = fl_utf8toUtf16(nm, (unsigned) len, NULL, 0); unsigned short *b = (unsigned short*) malloc((l + 1) * sizeof(short));
l = fl_utf8toUtf16(nm, (unsigned) len, b, (l+1)); b[l] = 0;
EnumFontFamiliesW(gc, (WCHAR*)b, (FONTENUMPROCW)EnumSizeCbW, 0);
free(b);
sizep = sizes;
return nbSize;
}
const char *Fl_GDI_Graphics_Driver::font_name(int num) {
return fl_fonts[num].name;
}
void Fl_GDI_Graphics_Driver::font_name(int num, const char *name) {
Fl_Fontdesc *s = fl_fonts + num;
if (s->name) {
if (!strcmp(s->name, name)) {s->name = name; return;}
for (Fl_Font_Descriptor* f = s->first; f;) {
Fl_Font_Descriptor* n = f->next; delete f; f = n;
}
s->first = 0;
}
s->name = name;
s->fontname[0] = 0;
s->first = 0;
}
static int fl_angle_ = 0;
static unsigned short *wstr = NULL;
static int wstr_len = 0;
#ifndef FL_DOXYGEN
Fl_GDI_Font_Descriptor::Fl_GDI_Font_Descriptor(const char* name, Fl_Fontsize fsize) : Fl_Font_Descriptor(name,fsize) {
int weight = FW_NORMAL;
int italic = 0;
switch (*name++) {
case 'I': italic = 1; break;
case 'P': italic = 1;
case 'B': weight = FW_BOLD; break;
case ' ': break;
default: name--;
}
int wn = fl_utf8toUtf16(name, (unsigned int)strlen(name), wstr, wstr_len);
if (wn >= wstr_len) {
wstr = (unsigned short*) realloc(wstr, sizeof(unsigned short) * (wn + 1));
wstr_len = wn + 1;
wn = fl_utf8toUtf16(name, (unsigned int)strlen(name), wstr, wstr_len);
}
fid = CreateFontW(
-fsize, 0, fl_angle_*10, fl_angle_*10, weight,
italic,
FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, (LPCWSTR)wstr );
angle = fl_angle_;
HDC gc = (HDC)fl_graphics_driver->gc();
if (!gc) gc = fl_GetDC(0);
SelectObject(gc, fid);
GetTextMetrics(gc, &metr);
int i;
memset(width, 0, 64 * sizeof(int*));
#if HAVE_GL
for (i = 0; i < 64; i++) glok[i] = 0;
#endif
size = fsize;
}
Fl_GDI_Font_Descriptor::~Fl_GDI_Font_Descriptor() {
#if HAVE_GL
#endif
if (this == fl_graphics_driver->font_descriptor()) fl_graphics_driver->font_descriptor(NULL);
DeleteObject(fid);
for (int i = 0; i < 64; i++) {
if ( width[i] ) free(width[i]);
}
}
static Fl_Fontdesc built_in_table[] = {
{" Microsoft Sans Serif"},
{"BMicrosoft Sans Serif"},
{"IMicrosoft Sans Serif"},
{"PMicrosoft Sans Serif"},
{" Courier New"},
{"BCourier New"},
{"ICourier New"},
{"PCourier New"},
{" Times New Roman"},
{"BTimes New Roman"},
{"ITimes New Roman"},
{"PTimes New Roman"},
{" Symbol"},
{" Terminal"},
{"BTerminal"},
{" Wingdings"},
};
Fl_Fontdesc* fl_fonts = built_in_table;
static Fl_GDI_Font_Descriptor* find(Fl_Font fnum, Fl_Fontsize size, int angle) {
Fl_Fontdesc* s = fl_fonts+fnum;
if (!s->name) s = fl_fonts; Fl_GDI_Font_Descriptor* f;
for (f = (Fl_GDI_Font_Descriptor*)s->first; f; f = (Fl_GDI_Font_Descriptor*)f->next)
if (f->size == size && f->angle == angle) return f;
f = new Fl_GDI_Font_Descriptor(s->name, size);
f->next = s->first;
s->first = f;
return f;
}
static void fl_font(Fl_Graphics_Driver *driver, Fl_Font fnum, Fl_Fontsize size, int angle) {
if (fnum==-1) { fl_angle_ = 0;
driver->Fl_Graphics_Driver::font(0, 0);
return;
}
if (fnum == driver->Fl_Graphics_Driver::font() && size == ((Fl_GDI_Graphics_Driver*)driver)->size_unscaled() && angle == fl_angle_) return;
fl_angle_ = angle;
driver->Fl_Graphics_Driver::font(fnum, size);
driver->font_descriptor( find(fnum, size, angle) );
}
void Fl_GDI_Graphics_Driver::font_unscaled(Fl_Font fnum, Fl_Fontsize size) {
fl_font(this, fnum, size, 0);
}
int Fl_GDI_Graphics_Driver::height_unscaled() {
Fl_GDI_Font_Descriptor *fl_fontsize = (Fl_GDI_Font_Descriptor*)font_descriptor();
if (fl_fontsize) return (fl_fontsize->metr.tmAscent + fl_fontsize->metr.tmDescent);
else return -1;
}
int Fl_GDI_Graphics_Driver::descent_unscaled() {
Fl_GDI_Font_Descriptor *fl_fontsize = (Fl_GDI_Font_Descriptor*)font_descriptor();
if (fl_fontsize) return fl_fontsize->metr.tmDescent;
else return -1;
}
Fl_Fontsize Fl_GDI_Graphics_Driver::size_unscaled() {
if (font_descriptor()) return size_;
return -1;
}
double Fl_GDI_Graphics_Driver::width_unscaled(const char* c, int n) {
int i = 0;
if (!font_descriptor()) return -1.0;
double w = 0.0;
char *end = (char *)&c[n];
while (i < n) {
unsigned int ucs;
int l;
ucs = fl_utf8decode((const char*)(c + i), end, &l);
i += l;
if (!fl_nonspacing(ucs)) {
w += width_unscaled(ucs);
}
}
return w;
}
double Fl_GDI_Graphics_Driver::width_unscaled(unsigned int c) {
Fl_GDI_Font_Descriptor *fl_fontsize = (Fl_GDI_Font_Descriptor*)font_descriptor();
unsigned int r;
SIZE s;
if(c > 0x0000FFFF) { if (!gc_) { return 0.0;
}
int cc; unsigned short u16[4]; cc = fl_ucs_to_Utf16(c, u16, 4);
SelectObject(gc_, fl_fontsize->fid);
GetTextExtentPoint32W(gc_, (WCHAR*)u16, cc, &s);
return (double)s.cx;
}
r = (c & 0xFC00) >> 10;
if (!fl_fontsize->width[r]) {
fl_fontsize->width[r] = (int*) malloc(sizeof(int) * 0x0400);
for (int i = 0; i < 0x0400; i++) fl_fontsize->width[r][i] = -1;
} else {
if ( fl_fontsize->width[r][c&0x03FF] >= 0 ) { return (double) fl_fontsize->width[r][c & 0x03FF];
}
}
unsigned short ii = r * 0x400;
HDC gc2 = gc_;
HWND hWnd = 0;
if (!gc2) { hWnd = Fl::first_window() ? fl_xid(Fl::first_window()) : NULL;
gc2 = GetDC(hWnd);
}
if (!gc2) Fl::fatal("Invalid graphic context: fl_width() failed because no valid HDC was found!");
SelectObject(gc2, fl_fontsize->fid);
ii += c &0x03FF;
GetTextExtentPoint32W(gc2, (WCHAR*)&ii, 1, &s);
fl_fontsize->width[r][c&0x03FF] = s.cx;
if (gc2 && gc2 != gc_) ReleaseDC(hWnd, gc2);
return (double) fl_fontsize->width[r][c & 0x03FF];
}
typedef DWORD (WINAPI* fl_GetGlyphIndices_func)(HDC,LPCWSTR,int,LPWORD,DWORD);
static fl_GetGlyphIndices_func fl_GetGlyphIndices = NULL; static int have_loaded_GetGlyphIndices = 0;
static void GetGlyphIndices_init() {
HMODULE hMod = GetModuleHandle("GDI32.DLL");
if (hMod) {
fl_GetGlyphIndices = (fl_GetGlyphIndices_func)GetProcAddress(hMod, "GetGlyphIndicesW");
}
have_loaded_GetGlyphIndices = -1; }
static void on_printer_extents_update(int &dx, int &dy, int &w, int &h, HDC gc)
{
POINT pt[3] = { {0, 0}, {dx, dy}, {dx+w, dy+h} };
DPtoLP(gc, pt, 3);
w = pt[2].x - pt[1].x;
h = pt[2].y - pt[1].y;
dx = pt[1].x - pt[0].x;
dy = pt[1].y - pt[0].y;
}
#define EXTENTS_UPDATE(x,y,w,h,gc) \
if (Fl_Surface_Device::surface() != Fl_Display_Device::display_device()) { \
on_printer_extents_update(x,y,w,h,gc); \
}
void Fl_GDI_Graphics_Driver::text_extents_unscaled(const char *c, int n, int &dx, int &dy, int &w, int &h) {
Fl_GDI_Font_Descriptor *fl_fontsize = (Fl_GDI_Font_Descriptor*)font_descriptor();
if (!fl_fontsize) { w = 0; h = 0;
dx = dy = 0;
return;
}
static unsigned short *ext_buff = NULL; static WORD *w_buff = NULL; static unsigned wc_len = 0; static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; GLYPHMETRICS metrics;
int maxw = 0, maxh = 0, dh;
int minx = 0, miny = -999999;
unsigned len = 0, idx = 0;
HWND hWnd = 0;
HDC gc2 = gc_; int has_surrogates;
if (have_loaded_GetGlyphIndices == 0) {
GetGlyphIndices_init();
}
if(!fl_GetGlyphIndices) goto exit_error;
if (!gc2) { hWnd = Fl::first_window() ? fl_xid(Fl::first_window()) : NULL;
gc2 = GetDC(hWnd);
}
if (!gc2) goto exit_error;
len = fl_utf8toUtf16(c, n, ext_buff, wc_len);
if(len >= wc_len) {
if(ext_buff) {delete [] ext_buff;}
if(w_buff) {delete [] w_buff;}
wc_len = len + 64;
ext_buff = new unsigned short[wc_len];
w_buff = new WORD[wc_len];
len = fl_utf8toUtf16(c, n, ext_buff, wc_len);
}
SelectObject(gc2, fl_fontsize->fid);
has_surrogates = 0;
for(unsigned ll = 0; ll < len; ll++) {
if((ext_buff[ll] >= 0xD800) && (ext_buff[ll] < 0xE000)) {
has_surrogates = -1;
break;
}
}
if (has_surrogates) {
GCP_RESULTSW gcp_res;
memset(w_buff, 0, (sizeof(WORD) * wc_len));
memset(&gcp_res, 0, sizeof(GCP_RESULTSW));
gcp_res.lpGlyphs = (LPWSTR)w_buff;
gcp_res.nGlyphs = wc_len;
gcp_res.lStructSize = sizeof(gcp_res);
DWORD dr = GetCharacterPlacementW(gc2, (WCHAR*)ext_buff, len, 0, &gcp_res, GCP_GLYPHSHAPE);
if(dr) {
len = gcp_res.nGlyphs;
} else goto exit_error;
} else {
if (fl_GetGlyphIndices(gc_, (WCHAR*)ext_buff, len, w_buff, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR) {
goto exit_error;
}
}
for(idx = 0; idx < len; idx++){
if (GetGlyphOutlineW (gc2, w_buff[idx], GGO_METRICS | GGO_GLYPH_INDEX,
&metrics, 0, NULL, &matrix) == GDI_ERROR) {
goto exit_error;
}
maxw += metrics.gmCellIncX;
if(idx == 0) minx = metrics.gmptGlyphOrigin.x;
dh = metrics.gmBlackBoxY - metrics.gmptGlyphOrigin.y;
if(dh > maxh) maxh = dh;
if(miny < metrics.gmptGlyphOrigin.y) miny = metrics.gmptGlyphOrigin.y;
}
maxw = maxw - metrics.gmCellIncX + metrics.gmBlackBoxX + metrics.gmptGlyphOrigin.x;
w = maxw - minx;
h = maxh + miny;
dx = minx;
dy = -miny;
EXTENTS_UPDATE(dx, dy, w, h, gc_);
return;
exit_error:
w = (int)width(c, n);
h = height_unscaled();
dx = 0;
dy = descent_unscaled() - h;
EXTENTS_UPDATE(dx, dy, w, h, gc_);
return;
}
void Fl_GDI_Graphics_Driver::draw_unscaled(const char* str, int n, int x, int y) {
COLORREF oldColor = SetTextColor(gc_, fl_RGB());
if (!font_descriptor()) this->font(FL_HELVETICA, FL_NORMAL_SIZE);
SelectObject(gc_, ((Fl_GDI_Font_Descriptor*)font_descriptor())->fid);
int wn = fl_utf8toUtf16(str, n, wstr, wstr_len);
if(wn >= wstr_len) {
wstr = (unsigned short*) realloc(wstr, sizeof(unsigned short) * (wn + 1));
wstr_len = wn + 1;
wn = fl_utf8toUtf16(str, n, wstr, wstr_len);
}
TextOutW(gc_, x, y, (WCHAR*)wstr, wn);
SetTextColor(gc_, oldColor); }
void Fl_GDI_Graphics_Driver::draw_unscaled(int angle, const char* str, int n, int x, int y) {
fl_font(this, Fl_Graphics_Driver::font(), size_unscaled(), angle);
int wn = 0; COLORREF oldColor = SetTextColor(gc_, fl_RGB());
SelectObject(gc_, ((Fl_GDI_Font_Descriptor*)font_descriptor())->fid);
wn = fl_utf8toUtf16(str, n, wstr, wstr_len);
if(wn >= wstr_len) { wstr = (unsigned short*) realloc(wstr, sizeof(unsigned short) * (wn + 1));
wstr_len = wn + 1;
wn = fl_utf8toUtf16(str, n, wstr, wstr_len); }
TextOutW(gc_, x, y, (WCHAR*)wstr, wn);
SetTextColor(gc_, oldColor);
fl_font(this, Fl_Graphics_Driver::font(), size_unscaled(), 0);
}
void Fl_GDI_Graphics_Driver::rtl_draw_unscaled(const char* c, int n, int x, int y) {
int wn;
wn = fl_utf8toUtf16(c, n, wstr, wstr_len);
if(wn >= wstr_len) {
wstr = (unsigned short*) realloc(wstr, sizeof(unsigned short) * (wn + 1));
wstr_len = wn + 1;
wn = fl_utf8toUtf16(c, n, wstr, wstr_len);
}
COLORREF oldColor = SetTextColor(gc_, fl_RGB());
SelectObject(gc_, ((Fl_GDI_Font_Descriptor*)font_descriptor())->fid);
#ifdef RTL_CHAR_BY_CHAR
int i = 0;
int lx = 0;
while (i < wn) { lx = (int) width(wstr[i]);
x -= lx;
TextOutW(gc_, x, y, (WCHAR*)wstr + i, 1);
if (fl_nonspacing(wstr[i])) {
x += lx;
}
i++;
}
#else
UINT old_align = SetTextAlign(gc_, TA_RIGHT | TA_RTLREADING);
TextOutW(gc_, x, y - height_unscaled() + descent_unscaled(), (WCHAR*)wstr, wn);
SetTextAlign(gc_, old_align);
#endif
SetTextColor(gc_, oldColor);
}
#endif