2 // "$Id: fl_font_win32.cxx 8644 2011-05-10 15:37:05Z ianmacarthur $"
4 // WIN32 font selection routines for the Fast Light Tool Kit (FLTK).
6 // Copyright 1998-2011 by Bill Spitzak and others.
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
28 #include <FL/Fl_Printer.H>
30 static int fl_angle_
= 0;
33 Fl_Font_Descriptor::Fl_Font_Descriptor(const char* name
, Fl_Fontsize fsize
) {
34 int weight
= FW_NORMAL
;
37 case 'I': italic
= 1; break;
39 case 'B': weight
= FW_BOLD
; break;
44 -fsize
, // negative makes it use "char size"
45 0, // logical average character width
46 fl_angle_
*10, // angle of escapement
47 fl_angle_
*10, // base-line orientation angle
50 FALSE
, // underline attribute flag
51 FALSE
, // strikeout attribute flag
52 DEFAULT_CHARSET
, // character set identifier
53 OUT_DEFAULT_PRECIS
, // output precision
54 CLIP_DEFAULT_PRECIS
,// clipping precision
55 DEFAULT_QUALITY
, // output quality
56 DEFAULT_PITCH
, // pitch and family
57 name
// pointer to typeface name string
60 if (!fl_gc
) fl_GetDC(0);
61 SelectObject(fl_gc
, fid
);
62 GetTextMetrics(fl_gc
, &metr
);
63 // BOOL ret = GetCharWidthFloat(fl_gc, metr.tmFirstChar, metr.tmLastChar, font->width+metr.tmFirstChar);
64 // ...would be the right call, but is not implemented into Window95! (WinNT?)
65 //GetCharWidth(fl_gc, 0, 255, width);
67 for (i
= 0; i
< 64; i
++) width
[i
] = NULL
;
70 for (i
= 0; i
< 64; i
++) glok
[i
] = 0;
75 Fl_Font_Descriptor::~Fl_Font_Descriptor() {
77 // Delete list created by gl_draw(). This is not done by this code
78 // as it will link in GL unnecessarily. There should be some kind
79 // of "free" routine pointer, or a subclass?
81 // int base = font->min_char_or_byte2;
82 // int size = font->max_char_or_byte2-base+1;
83 // int base = 0; int size = 256;
84 // glDeleteLists(listbase+base,size);
87 if (this == fl_graphics_driver
->font_descriptor()) fl_graphics_driver
->font_descriptor(NULL
);
90 for (i
= 0; i
< 64; i
++) free(width
[i
]);
93 ////////////////////////////////////////////////////////////////
95 // WARNING: if you add to this table, you must redefine FL_FREE_FONT
96 // in Enumerations.H & recompile!!
97 static Fl_Fontdesc built_in_table
[] = {
106 {" Times New Roman"},
107 {"BTimes New Roman"},
108 {"ITimes New Roman"},
109 {"PTimes New Roman"},
116 Fl_Fontdesc
* fl_fonts
= built_in_table
;
118 static Fl_Font_Descriptor
* find(Fl_Font fnum
, Fl_Fontsize size
, int angle
) {
119 Fl_Fontdesc
* s
= fl_fonts
+fnum
;
120 if (!s
->name
) s
= fl_fonts
; // use 0 if fnum undefined
121 Fl_Font_Descriptor
* f
;
122 for (f
= s
->first
; f
; f
= f
->next
)
123 if (f
->size
== size
&& f
->angle
== angle
) return f
;
124 f
= new Fl_Font_Descriptor(s
->name
, size
);
130 ////////////////////////////////////////////////////////////////
133 static void fl_font(Fl_Graphics_Driver
*driver
, Fl_Font fnum
, Fl_Fontsize size
, int angle
) {
134 if (fnum
==-1) { // just make sure that we will load a new font next time
136 driver
->Fl_Graphics_Driver::font(0, 0);
139 if (fnum
== driver
->Fl_Graphics_Driver::font() && size
== driver
->size() && angle
== fl_angle_
) return;
141 driver
->Fl_Graphics_Driver::font(fnum
, size
);
142 driver
->font_descriptor( find(fnum
, size
, angle
) );
145 void Fl_GDI_Graphics_Driver::font(Fl_Font fnum
, Fl_Fontsize size
) {
146 fl_font(this, fnum
, size
, 0);
149 int Fl_GDI_Graphics_Driver::height() {
150 Fl_Font_Descriptor
*fl_fontsize
= font_descriptor();
151 if (fl_fontsize
) return (fl_fontsize
->metr
.tmAscent
+ fl_fontsize
->metr
.tmDescent
);
155 int Fl_GDI_Graphics_Driver::descent() {
156 Fl_Font_Descriptor
*fl_fontsize
= font_descriptor();
157 if (fl_fontsize
) return fl_fontsize
->metr
.tmDescent
;
161 // Unicode string buffer
162 static unsigned short *wstr
= NULL
;
163 static int wstr_len
= 0;
166 double Fl_GDI_Graphics_Driver::width(const char* c
, int n
) {
168 if (!font_descriptor()) return -1.0;
170 char *end
= (char *)&c
[n
];
174 ucs
= fl_utf8decode((const char*)(c
+ i
), end
, &l
);
177 if (!fl_nonspacing(ucs
)) {
184 double Fl_GDI_Graphics_Driver::width(unsigned int c
) {
185 Fl_Font_Descriptor
*fl_fontsize
= font_descriptor();
188 // Special Case Handling of Unicode points over U+FFFF.
189 // The logic (below) computes a lookup table for char widths
190 // on-the-fly, but the table only covers codepoints up to
191 // U+FFFF, which covers the basic multilingual plane, but
192 // not any higher plane, or glyphs that require surrogate-pairs
193 // to encode them in WinXX, which is UTF16.
194 // This code assumes that these glyphs are rarely used and simply
195 // measures them explicitly if they occur - This will be slow...
196 if(c
> 0x0000FFFF) { // UTF16 surrogate pair is needed
197 if (!fl_gc
) { // We have no valid gc, so nothing to measure - bail out
200 int cc
; // cell count
201 unsigned short u16
[4]; // Array for UTF16 representation of c
202 // Creates a UTF16 string from a UCS code point.
203 cc
= fl_ucs_to_Utf16(c
, u16
, 4);
204 // Make sure the current font is selected before we make the measurement
205 SelectObject(fl_gc
, fl_fontsize
->fid
);
206 // measure the glyph width
207 GetTextExtentPoint32W(fl_gc
, (WCHAR
*)u16
, cc
, &s
);
210 // else - this falls through to the lookup-table for glyph widths
211 // in the basic multilingual plane
212 r
= (c
& 0xFC00) >> 10;
213 if (!fl_fontsize
->width
[r
]) {
214 fl_fontsize
->width
[r
] = (int*) malloc(sizeof(int) * 0x0400);
215 unsigned short i
= 0, ii
= r
* 0x400;
216 // The following code makes a best effort attempt to obtain a valid fl_gc.
217 // If no fl_gc is available at the time we call fl_width(), then we first
218 // try to obtain a gc from the first fltk window.
219 // If that is null then we attempt to obtain the gc from the current screen
220 // using (GetDC(NULL)).
221 // This should resolve STR #2086
224 if (!gc
) { // We have no valid gc, try and obtain one
225 // Use our first fltk window, or fallback to using the screen via GetDC(NULL)
226 hWnd
= Fl::first_window() ? fl_xid(Fl::first_window()) : NULL
;
230 Fl::fatal("Invalid graphic context: fl_width() failed because no valid HDC was found!");
231 SelectObject(gc
, fl_fontsize
->fid
);
232 for (; i
< 0x400; i
++) {
233 GetTextExtentPoint32W(gc
, (WCHAR
*)&ii
, 1, &s
);
234 fl_fontsize
->width
[r
][i
] = s
.cx
;
237 if (gc
&& gc
!=fl_gc
) ReleaseDC(hWnd
, gc
);
239 return (double) fl_fontsize
->width
[r
][c
& 0x03FF];
242 /* Add function pointer to allow us to access GetGlyphIndicesW on systems that have it,
243 * without crashing on systems that do not. */
244 /* DWORD WINAPI GetGlyphIndicesW(HDC,LPCWSTR,int,LPWORD,DWORD) */
245 typedef DWORD (WINAPI
* fl_GetGlyphIndices_func
)(HDC
,LPCWSTR
,int,LPWORD
,DWORD
);
247 static fl_GetGlyphIndices_func fl_GetGlyphIndices
= NULL
; // used to hold a proc pointer for GetGlyphIndicesW
248 static int have_loaded_GetGlyphIndices
= 0; // Set this non-zero once we have tried to load GetGlyphIndices
250 // Function that tries to dynamically load GetGlyphIndicesW at runtime
251 static void GetGlyphIndices_init() {
252 // Since not all versions of Windows include GetGlyphIndicesW support,
253 // we do a run-time check for the required function.
254 HMODULE hMod
= GetModuleHandle("GDI32.DLL");
256 // check that GetGlyphIndicesW is available
257 fl_GetGlyphIndices
= (fl_GetGlyphIndices_func
)GetProcAddress(hMod
, "GetGlyphIndicesW");
259 have_loaded_GetGlyphIndices
= -1; // set this non-zero when we have attempted to load GetGlyphIndicesW
260 } // GetGlyphIndices_init function
262 static void on_printer_extents_update(int &dx
, int &dy
, int &w
, int &h
)
263 // converts text extents from device coords to logical coords
265 POINT pt
[3] = { {0, 0}, {dx
, dy
}, {dx
+w
, dy
+h
} };
266 DPtoLP(fl_gc
, pt
, 3);
267 w
= pt
[2].x
- pt
[1].x
;
268 h
= pt
[2].y
- pt
[1].y
;
269 dx
= pt
[1].x
- pt
[0].x
;
270 dy
= pt
[1].y
- pt
[0].y
;
273 // if printer context, extents shd be converted to logical coords
274 #define EXTENTS_UPDATE(x,y,w,h) \
275 if (Fl_Surface_Device::surface()->class_name() == Fl_Printer::class_id) { on_printer_extents_update(x,y,w,h); }
277 // Function to determine the extent of the "inked" area of the glyphs in a string
278 void Fl_GDI_Graphics_Driver::text_extents(const char *c
, int n
, int &dx
, int &dy
, int &w
, int &h
) {
280 Fl_Font_Descriptor
*fl_fontsize
= font_descriptor();
281 if (!fl_fontsize
) { // no valid font, nothing to measure
287 static unsigned short *ext_buff
= NULL
; // UTF-16 converted version of input UTF-8 string
288 static WORD
*w_buff
= NULL
; // glyph indices array
289 static unsigned wc_len
= 0; // current string buffer dimensions
290 static const MAT2 matrix
= { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; // identity mat for GetGlyphOutlineW
291 GLYPHMETRICS metrics
;
292 int maxw
= 0, maxh
= 0, dh
;
293 int minx
= 0, miny
= -999999;
294 unsigned len
= 0, idx
= 0;
296 HDC gc
= fl_gc
; // local copy of current gc - make a copy in case we change it...
297 int has_surrogates
; // will be set if the string contains surrogate pairs
299 // Have we loaded the GetGlyphIndicesW function yet?
300 if (have_loaded_GetGlyphIndices
== 0) {
301 GetGlyphIndices_init();
303 // Do we have a usable GetGlyphIndices function?
304 if(!fl_GetGlyphIndices
) goto exit_error
; // No GetGlyphIndices function, use fallback mechanism instead
306 // The following code makes a best effort attempt to obtain a valid fl_gc.
307 // See description in fl_width() above for an explanation.
308 if (!gc
) { // We have no valid gc, try and obtain one
309 // Use our first fltk window, or fallback to using the screen via GetDC(NULL)
310 hWnd
= Fl::first_window() ? fl_xid(Fl::first_window()) : NULL
;
313 if (!gc
) goto exit_error
; // no valid gc, attempt to use fallback measure
315 // now convert the string to WCHAR and measure it
316 len
= fl_utf8toUtf16(c
, n
, ext_buff
, wc_len
);
318 if(ext_buff
) {delete [] ext_buff
;}
319 if(w_buff
) {delete [] w_buff
;}
321 ext_buff
= new unsigned short[wc_len
];
322 w_buff
= new WORD
[wc_len
];
323 len
= fl_utf8toUtf16(c
, n
, ext_buff
, wc_len
);
325 SelectObject(gc
, fl_fontsize
->fid
);
327 // Are there surrogate-pairs in this string? If so GetGlyphIndicesW will fail
328 // since it can only handle the BMP range.
329 // We ideally want to use GetGlyphIndicesW, as it is the Right Thing, but it
330 // only works for the BMP, so we leverage GetCharacterPlacementW instead, which
331 // is not ideal, but works adequately well, and does handle surrogate pairs.
333 for(unsigned ll
= 0; ll
< len
; ll
++) {
334 if((ext_buff
[ll
] >= 0xD800) && (ext_buff
[ll
] < 0xE000)) {
339 if (has_surrogates
) {
340 // GetGlyphIndices will not work - use GetCharacterPlacementW() instead
341 GCP_RESULTSW gcp_res
;
342 memset(w_buff
, 0, (sizeof(WORD
) * wc_len
));
343 memset(&gcp_res
, 0, sizeof(GCP_RESULTSW
));
344 gcp_res
.lpGlyphs
= (LPWSTR
)w_buff
;
345 gcp_res
.nGlyphs
= wc_len
;
346 gcp_res
.lStructSize
= sizeof(gcp_res
);
348 DWORD dr
= GetCharacterPlacementW(gc
, (WCHAR
*)ext_buff
, len
, 0, &gcp_res
, GCP_GLYPHSHAPE
);
350 len
= gcp_res
.nGlyphs
;
351 } else goto exit_error
;
353 if (fl_GetGlyphIndices(gc
, (WCHAR
*)ext_buff
, len
, w_buff
, GGI_MARK_NONEXISTING_GLYPHS
) == GDI_ERROR
) {
354 // some error occured here - just return fl_measure values
359 // now we have the glyph array we measure each glyph in turn...
360 for(idx
= 0; idx
< len
; idx
++){
361 if (GetGlyphOutlineW (gc
, w_buff
[idx
], GGO_METRICS
| GGO_GLYPH_INDEX
,
362 &metrics
, 0, NULL
, &matrix
) == GDI_ERROR
) {
365 maxw
+= metrics
.gmCellIncX
;
366 if(idx
== 0) minx
= metrics
.gmptGlyphOrigin
.x
;
367 dh
= metrics
.gmBlackBoxY
- metrics
.gmptGlyphOrigin
.y
;
368 if(dh
> maxh
) maxh
= dh
;
369 if(miny
< metrics
.gmptGlyphOrigin
.y
) miny
= metrics
.gmptGlyphOrigin
.y
;
371 // for the last cell, we only want the bounding X-extent, not the glyphs increment step
372 maxw
= maxw
- metrics
.gmCellIncX
+ metrics
.gmBlackBoxX
+ metrics
.gmptGlyphOrigin
.x
;
377 EXTENTS_UPDATE(dx
, dy
, w
, h
);
378 return; // normal exit
381 // some error here - just return fl_measure values
382 w
= (int)width(c
, n
);
386 EXTENTS_UPDATE(dx
, dy
, w
, h
);
390 void Fl_GDI_Graphics_Driver::draw(const char* str
, int n
, int x
, int y
) {
391 COLORREF oldColor
= SetTextColor(fl_gc
, fl_RGB());
392 SelectObject(fl_gc
, font_descriptor()->fid
);
393 int wn
= fl_utf8toUtf16(str
, n
, wstr
, wstr_len
);
395 wstr
= (unsigned short*) realloc(wstr
, sizeof(unsigned short) * (wn
+ 1));
397 wn
= fl_utf8toUtf16(str
, n
, wstr
, wstr_len
);
399 TextOutW(fl_gc
, x
, y
, (WCHAR
*)wstr
, wn
);
400 SetTextColor(fl_gc
, oldColor
); // restore initial state
403 void Fl_GDI_Graphics_Driver::draw(int angle
, const char* str
, int n
, int x
, int y
) {
404 fl_font(this, Fl_Graphics_Driver::font(), size(), angle
);
405 int wn
= 0; // count of UTF16 cells to render full string
406 COLORREF oldColor
= SetTextColor(fl_gc
, fl_RGB());
407 SelectObject(fl_gc
, font_descriptor()->fid
);
408 wn
= fl_utf8toUtf16(str
, n
, wstr
, wstr_len
);
409 if(wn
>= wstr_len
) { // Array too small
410 wstr
= (unsigned short*) realloc(wstr
, sizeof(unsigned short) * (wn
+ 1));
412 wn
= fl_utf8toUtf16(str
, n
, wstr
, wstr_len
); // respin the translation
414 TextOutW(fl_gc
, x
, y
, (WCHAR
*)wstr
, wn
);
415 SetTextColor(fl_gc
, oldColor
);
416 fl_font(this, Fl_Graphics_Driver::font(), size(), 0);
419 void Fl_GDI_Graphics_Driver::rtl_draw(const char* c
, int n
, int x
, int y
) {
421 wn
= fl_utf8toUtf16(c
, n
, wstr
, wstr_len
);
423 wstr
= (unsigned short*) realloc(wstr
, sizeof(unsigned short) * (wn
+ 1));
425 wn
= fl_utf8toUtf16(c
, n
, wstr
, wstr_len
);
428 COLORREF oldColor
= SetTextColor(fl_gc
, fl_RGB());
429 SelectObject(fl_gc
, font_descriptor()->fid
);
430 #ifdef RTL_CHAR_BY_CHAR
433 while (i
< wn
) { // output char by char is very bad for Arabic but coherent with fl_width()
434 lx
= (int) width(wstr
[i
]);
436 TextOutW(fl_gc
, x
, y
, (WCHAR
*)wstr
+ i
, 1);
437 if (fl_nonspacing(wstr
[i
])) {
443 UINT old_align
= SetTextAlign(fl_gc
, TA_RIGHT
| TA_RTLREADING
);
444 TextOutW(fl_gc
, x
, y
- height() + descent(), (WCHAR
*)wstr
, wn
);
445 SetTextAlign(fl_gc
, old_align
);
447 SetTextColor(fl_gc
, oldColor
);
451 // End of "$Id: fl_font_win32.cxx 8644 2011-05-10 15:37:05Z ianmacarthur $".