xref: /reactos/win32ss/user/user32/windows/font.c (revision 44898a4e)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /*
20  * PROJECT:         ReactOS user32.dll
21  * FILE:            win32ss/user/user32/windows/font.c
22  * PURPOSE:         Draw Text
23  * PROGRAMMER:      Casper S. Hornstrup (chorns@users.sourceforge.net)
24  * UPDATE HISTORY:
25  *      09-05-2001  CSH  Created
26  */
27 
28 /* INCLUDES ******************************************************************/
29 
30 #include <user32.h>
31 
32 DWORD WINAPI GdiGetCodePage(HDC hdc);
33 
34 INT WINAPI DrawTextExWorker( HDC hdc, LPWSTR str, INT i_count,
35                         LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp );
36 
37 
38 /* FUNCTIONS *****************************************************************/
39 
40 
41 /***********************************************************************
42  *           TEXT_TabbedTextOut
43  *
44  * Helper function for TabbedTextOut() and GetTabbedTextExtent().
45  * Note: this doesn't work too well for text-alignment modes other
46  *       than TA_LEFT|TA_TOP. But we want bug-for-bug compatibility :-)
47  */
48 /* WINE synced 22-May-2006 */
49 LONG TEXT_TabbedTextOut( HDC hdc,
50                          INT x,
51                          INT y,
52                          LPCWSTR lpstr,
53                          INT count,
54                          INT cTabStops,
55                          const INT *lpTabPos,
56                          INT nTabOrg,
57                          BOOL fDisplayText )
58 {
59     INT defWidth;
60     SIZE extent;
61     int i, j;
62     int start = x;
63     TEXTMETRICW tm;
64 
65     if (!lpTabPos)
66         cTabStops=0;
67 
68     GetTextMetricsW( hdc, &tm );
69 
70     if (cTabStops == 1)
71     {
72         defWidth = *lpTabPos;
73         cTabStops = 0;
74     }
75     else
76     {
77         defWidth = 8 * tm.tmAveCharWidth;
78     }
79 
80     while (count > 0)
81     {
82         RECT r;
83         INT x0;
84         x0 = x;
85         r.left = x0;
86         /* chop the string into substrings of 0 or more <tabs>
87          * possibly followed by 1 or more normal characters */
88         for (i = 0; i < count; i++)
89             if (lpstr[i] != '\t') break;
90         for (j = i; j < count; j++)
91             if (lpstr[j] == '\t') break;
92         /* get the extent of the normal character part */
93         GetTextExtentPointW( hdc, lpstr + i, j - i , &extent );
94         /* and if there is a <tab>, calculate its position */
95         if( i) {
96             /* get x coordinate for the drawing of this string */
97             for (; cTabStops > i; lpTabPos++, cTabStops--)
98             {
99                 if( nTabOrg + abs( *lpTabPos) > x) {
100                     if( lpTabPos[ i - 1] >= 0) {
101                         /* a left aligned tab */
102                         x = nTabOrg + lpTabPos[ i-1] + extent.cx;
103                         break;
104                     }
105                     else
106                     {
107                         /* if tab pos is negative then text is right-aligned
108                          * to tab stop meaning that the string extends to the
109                          * left, so we must subtract the width of the string */
110                         if (nTabOrg - lpTabPos[ i - 1] - extent.cx > x)
111                         {
112                             x = nTabOrg - lpTabPos[ i - 1];
113                             x0 = x - extent.cx;
114                             break;
115                         }
116                     }
117                 }
118             }
119             /* if we have run out of tab stops and we have a valid default tab
120              * stop width then round x up to that width */
121             if ((cTabStops <= i) && (defWidth > 0)) {
122                 x0 = nTabOrg + ((x - nTabOrg) / defWidth + i) * defWidth;
123                 x = x0 + extent.cx;
124             } else if ((cTabStops <= i) && (defWidth < 0)) {
125                 x = nTabOrg + ((x - nTabOrg + extent.cx) / -defWidth + i)
126                     * -defWidth;
127                 x0 = x - extent.cx;
128             }
129         } else
130             x += extent.cx;
131 
132         if (fDisplayText)
133         {
134             r.top    = y;
135             r.right  = x;
136             r.bottom = y + extent.cy;
137 
138             ExtTextOutW( hdc, x0, y, GetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0,
139                          &r, lpstr + i, j - i, NULL );
140         }
141         count -= j;
142         lpstr += j;
143     }
144 
145     if(!extent.cy)
146         extent.cy = tm.tmHeight;
147 
148     return MAKELONG(x - start, extent.cy);
149 }
150 
151 /*
152  * @implemented
153  */
154 LONG
155 WINAPI
156 TabbedTextOutA(
157   HDC hDC,
158   int X,
159   int Y,
160   LPCSTR lpString,
161   int nCount,
162   int nTabPositions,
163   CONST INT *lpnTabStopPositions,
164   int nTabOrigin)
165 {
166   LONG ret;
167   DWORD len;
168   LPWSTR strW;
169   UINT cp = GdiGetCodePage( hDC ); // CP_ACP
170 
171   len = MultiByteToWideChar(cp, 0, lpString, nCount, NULL, 0);
172   if (!len) return 0;
173   strW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
174   if (!strW) return 0;
175   MultiByteToWideChar(cp, 0, lpString, nCount, strW, len);
176   ret = TabbedTextOutW(hDC, X, Y, strW, len, nTabPositions, lpnTabStopPositions, nTabOrigin);
177   HeapFree(GetProcessHeap(), 0, strW);
178   return ret;
179 }
180 
181 /*
182  * @implemented
183  */
184 LONG
185 WINAPI
186 TabbedTextOutW(
187   HDC hDC,
188   int X,
189   int Y,
190   LPCWSTR lpString,
191   int nCount,
192   int nTabPositions,
193   CONST INT *lpnTabStopPositions,
194   int nTabOrigin)
195 {
196   return TEXT_TabbedTextOut(hDC, X, Y, lpString, nCount, nTabPositions, lpnTabStopPositions, nTabOrigin, TRUE);
197 }
198 
199 /* WINE synced 22-May-2006 */
200 /*
201  * @implemented
202  */
203 DWORD
204 WINAPI
205 GetTabbedTextExtentA(
206   HDC hDC,
207   LPCSTR lpString,
208   int nCount,
209   int nTabPositions,
210   CONST INT *lpnTabStopPositions)
211 {
212     LONG ret;
213     UINT cp = GdiGetCodePage( hDC ); // CP_ACP
214     DWORD len;
215     LPWSTR strW;
216 
217     len = MultiByteToWideChar(cp, 0, lpString, nCount, NULL, 0);
218     if (!len) return 0;
219     strW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
220     if (!strW) return 0;
221     MultiByteToWideChar(cp, 0, lpString, nCount, strW, len);
222     ret = GetTabbedTextExtentW(hDC, strW, len, nTabPositions, lpnTabStopPositions);
223     HeapFree(GetProcessHeap(), 0, strW);
224     return ret;
225 }
226 
227 /*
228  * @implemented
229  */
230 DWORD
231 WINAPI
232 GetTabbedTextExtentW(
233   HDC hDC,
234   LPCWSTR lpString,
235   int nCount,
236   int nTabPositions,
237   CONST INT *lpnTabStopPositions)
238 {
239    return TEXT_TabbedTextOut(hDC, 0, 0, lpString, nCount, nTabPositions, lpnTabStopPositions, 0, FALSE);
240 }
241 
242 
243 /***********************************************************************
244  *           DrawTextExW    (USER32.@)
245  *
246  * The documentation on the extra space required for DT_MODIFYSTRING at MSDN
247  * is not quite complete, especially with regard to \0.  We will assume that
248  * the returned string could have a length of up to i_count+3 and also have
249  * a trailing \0 (which would be 4 more than a not-null-terminated string but
250  * 3 more than a null-terminated string).  If this is not so then increase
251  * the allowance in DrawTextExA.
252  */
253 #define MAX_BUFFER 1024
254 /*
255  * @implemented
256  *
257  * Synced with Wine Staging 1.7.37
258  */
259 INT WINAPI DrawTextExW( HDC hdc, LPWSTR str, INT i_count,
260                         LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp )
261 {
262    return DrawTextExWorker( hdc, str, i_count, rect, flags, dtp );
263 }
264 
265 /***********************************************************************
266  *           DrawTextExA    (USER32.@)
267  *
268  * If DT_MODIFYSTRING is specified then there must be room for up to
269  * 4 extra characters.  We take great care about just how much modified
270  * string we return.
271  *
272  * @implemented
273  *
274  * Synced with Wine Staging 1.7.37
275  */
276 INT WINAPI DrawTextExA( HDC hdc, LPSTR str, INT count,
277                         LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp )
278 {
279    WCHAR *wstr;
280    WCHAR *p;
281    INT ret = 0;
282    int i;
283    DWORD wcount;
284    DWORD wmax;
285    DWORD amax;
286    UINT cp;
287 
288    if (!count) return 0;
289    if (!str && count > 0) return 0;
290    if( !str || ((count == -1) && !(count = strlen(str))))
291    {
292         int lh;
293         TEXTMETRICA tm;
294 
295         if (dtp && dtp->cbSize != sizeof(DRAWTEXTPARAMS))
296             return 0;
297 
298         GetTextMetricsA(hdc, &tm);
299         if (flags & DT_EXTERNALLEADING)
300             lh = tm.tmHeight + tm.tmExternalLeading;
301         else
302             lh = tm.tmHeight;
303 
304         if( flags & DT_CALCRECT)
305         {
306             rect->right = rect->left;
307             if( flags & DT_SINGLELINE)
308                 rect->bottom = rect->top + lh;
309             else
310                 rect->bottom = rect->top;
311         }
312         return lh;
313    }
314    cp = GdiGetCodePage( hdc );
315    wcount = MultiByteToWideChar( cp, 0, str, count, NULL, 0 );
316    wmax = wcount;
317    amax = count;
318    if (flags & DT_MODIFYSTRING)
319    {
320         wmax += 4;
321         amax += 4;
322    }
323    wstr = HeapAlloc(GetProcessHeap(), 0, wmax * sizeof(WCHAR));
324    if (wstr)
325    {
326        MultiByteToWideChar( cp, 0, str, count, wstr, wcount );
327        if (flags & DT_MODIFYSTRING)
328            for (i=4, p=wstr+wcount; i--; p++) *p=0xFFFE;
329            /* Initialise the extra characters so that we can see which ones
330             * change.  U+FFFE is guaranteed to be not a unicode character and
331             * so will not be generated by DrawTextEx itself.
332             */
333        ret = DrawTextExW( hdc, wstr, wcount, rect, flags, dtp );
334        if (flags & DT_MODIFYSTRING)
335        {
336             /* Unfortunately the returned string may contain multiple \0s
337              * and so we need to measure it ourselves.
338              */
339             for (i=4, p=wstr+wcount; i-- && *p != 0xFFFE; p++) wcount++;
340             WideCharToMultiByte( cp, 0, wstr, wcount, str, amax, NULL, NULL );
341        }
342        HeapFree(GetProcessHeap(), 0, wstr);
343    }
344    return ret;
345 }
346 
347 /***********************************************************************
348  *           DrawTextW    (USER32.@)
349  *
350  * @implemented
351  * Synced with Wine Staging 1.7.37
352  */
353 INT WINAPI DrawTextW( HDC hdc, LPCWSTR str, INT count, LPRECT rect, UINT flags )
354 {
355     DRAWTEXTPARAMS dtp;
356 
357     memset (&dtp, 0, sizeof(dtp));
358     dtp.cbSize = sizeof(dtp);
359     if (flags & DT_TABSTOP)
360     {
361         dtp.iTabLength = (flags >> 8) & 0xff;
362         flags &= 0xffff00ff;
363     }
364     return DrawTextExW(hdc, (LPWSTR)str, count, rect, flags, &dtp);
365 }
366 
367 /***********************************************************************
368  *           DrawTextA    (USER32.@)
369  *
370  * @implemented
371  * Synced with Wine Staging 1.7.37
372  */
373 INT WINAPI DrawTextA( HDC hdc, LPCSTR str, INT count, LPRECT rect, UINT flags )
374 {
375     DRAWTEXTPARAMS dtp;
376 
377     memset (&dtp, 0, sizeof(dtp));
378     dtp.cbSize = sizeof(dtp);
379     if (flags & DT_TABSTOP)
380     {
381         dtp.iTabLength = (flags >> 8) & 0xff;
382         flags &= 0xffff00ff;
383     }
384     return DrawTextExA( hdc, (LPSTR)str, count, rect, flags, &dtp );
385 }
386 
387 /***************************************************************************
388  *       UserLpkPSMTextOut
389  *
390  * Stripped down version of DrawText, can only draw single line text and
391  * Prefix underscore (only on the last found amperstand)
392  * only flags to be found to be of use in testing:
393  *
394  * DT_NOPREFIX   - Draw the string as is it without any changes
395  * DT_HIDEPREFIX - Draw the string without underscore
396  * DT_PREFIXONLY - Draw only the underscore
397  *
398  * without any of these flags the behavior is the string being drawn without the amperstands and
399  * with the underscore.
400  *
401  * lpk has an equivalent function - LpkPSMTextOut
402  * Notes by testing:
403  * This function in windows doesn't check if lpString is NULL, which results a crash,
404  * returns seemingly random values without any logic, and ignores the DT_NOPREFIX value.
405  * All of these issues don't exist in the LPK version.
406  *
407  * Note: lpString does not need to be null terminated
408  */
409 #define PREFIX 38
410 #define ALPHA_PREFIX 30 /* Win16: Alphabet prefix */
411 #define KANA_PREFIX  31 /* Win16: Katakana prefix */
412 
413 INT WINAPI UserLpkPSMTextOut(HDC hdc, int x, int y, LPCWSTR lpString, int cString, DWORD dwFlags)
414 {
415     SIZE size;
416     TEXTMETRICW tm;
417     int len, i = 0, j = 0;
418     int prefix_count = 0, prefix_offset = -1;
419     LPWSTR display_str = NULL;
420     int prefix_x, prefix_end;
421     HPEN hpen;
422     HPEN oldPen;
423 
424     if (!lpString || cString <= 0)
425         return 0;
426 
427     if (dwFlags & DT_NOPREFIX) /* Windows ignores this */
428     {
429         TextOutW(hdc, x, y, lpString, cString);
430         GetTextExtentPointW(hdc, lpString, cString, &size);
431         return size.cx;
432     }
433 
434     display_str = HeapAlloc(GetProcessHeap(), 0, (cString + 1) * sizeof(WCHAR));
435 
436     if (!display_str)
437         return 0;
438 
439     while (i < cString)
440     {
441         if (lpString[i] == PREFIX || (iswspace(lpString[i]) && lpString[i] != L' '))
442         {
443             if (i < cString - 1 && lpString[i + 1] == PREFIX)
444                 display_str[j++] = lpString[i++];
445             else
446                 i++;
447         }
448         else
449         {
450             display_str[j++] = lpString[i++];
451         }
452     }
453 
454     display_str[j] = L'\0';
455     len = wcslen(display_str);
456 
457     if (!(dwFlags & DT_PREFIXONLY))
458         TextOutW(hdc, x, y, display_str, len);
459 
460     if (!(dwFlags & DT_HIDEPREFIX))
461     {
462 
463         for (i = 0; i < cString - 1; i++)
464         {
465             if (lpString[i] == PREFIX && lpString[i + 1] != PREFIX)
466             {
467                 prefix_offset = i - prefix_count;
468                 prefix_count++;
469             }
470             else if (lpString[i] == PREFIX && lpString[i + 1] == PREFIX)
471             {
472                 i++;
473             }
474         }
475 
476         GetTextMetricsW(hdc, &tm);
477 
478         if (prefix_offset != -1)
479         {
480             GetTextExtentPointW(hdc, display_str, prefix_offset, &size);
481             prefix_x = x + size.cx;
482             GetTextExtentPointW(hdc, display_str, prefix_offset + 1, &size);
483             prefix_end = x + size.cx - 1;
484             hpen = CreatePen(PS_SOLID, 1, GetTextColor(hdc));
485             oldPen = SelectObject(hdc, hpen);
486             MoveToEx(hdc, prefix_x, y + tm.tmAscent + 1, NULL);
487             LineTo(hdc, prefix_end, y + tm.tmAscent + 1);
488             SelectObject(hdc, oldPen);
489             DeleteObject(hpen);
490         }
491     }
492 
493     GetTextExtentPointW(hdc, display_str, len + 1, &size);
494     HeapFree(GetProcessHeap(), 0, display_str);
495 
496     return size.cx;
497 }
498