Fixed crash in winamp reported by Andreas Mohr.
[wine] / dlls / wineps / text.c
1 /*
2  *      PostScript driver text functions
3  *
4  *      Copyright 1998  Huw D M Davies
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <string.h>
21 #include "gdi.h"
22 #include "psdrv.h"
23 #include "wine/debug.h"
24 #include "winspool.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
27
28 static BOOL PSDRV_Text(PSDRV_PDEVICE *physDev, INT x, INT y, UINT flags,
29                        LPCWSTR str, UINT count,
30                        BOOL bDrawBackground, const INT *lpDx);
31
32 /***********************************************************************
33  *           PSDRV_ExtTextOut
34  */
35 BOOL PSDRV_ExtTextOut( PSDRV_PDEVICE *physDev, INT x, INT y, UINT flags,
36                        const RECT *lprect, LPCWSTR str, UINT count,
37                        const INT *lpDx )
38 {
39     BOOL bResult = TRUE;
40     BOOL bClipped = FALSE;
41     BOOL bOpaque = FALSE;
42     RECT rect;
43
44     TRACE("(x=%d, y=%d, flags=0x%08x, str=%s, count=%d, lpDx=%p)\n", x, y,
45           flags, debugstr_wn(str, count), count, lpDx);
46
47     /* write font if not already written */
48     PSDRV_SetFont(physDev);
49
50     /* set clipping and/or draw background */
51     if ((flags & (ETO_CLIPPED | ETO_OPAQUE)) && (lprect != NULL))
52     {
53         rect = *lprect;
54         LPtoDP( physDev->hdc, (POINT *)&rect, 2 );
55         PSDRV_WriteGSave(physDev);
56         PSDRV_WriteRectangle(physDev, rect.left, rect.top, rect.right - rect.left,
57                              rect.bottom - rect.top);
58
59         if (flags & ETO_OPAQUE)
60         {
61             bOpaque = TRUE;
62             PSDRV_WriteGSave(physDev);
63             PSDRV_WriteSetColor(physDev, &physDev->bkColor);
64             PSDRV_WriteFill(physDev);
65             PSDRV_WriteGRestore(physDev);
66         }
67
68         if (flags & ETO_CLIPPED)
69         {
70             bClipped = TRUE;
71             PSDRV_WriteClip(physDev);
72         }
73
74         bResult = PSDRV_Text(physDev, x, y, flags, str, count, !(bClipped && bOpaque), lpDx);
75         PSDRV_WriteGRestore(physDev);
76     }
77     else
78     {
79         bResult = PSDRV_Text(physDev, x, y, flags, str, count, TRUE, lpDx);
80     }
81
82     return bResult;
83 }
84
85 /***********************************************************************
86  *           PSDRV_Text
87  */
88 static BOOL PSDRV_Text(PSDRV_PDEVICE *physDev, INT x, INT y, UINT flags, LPCWSTR str,
89                        UINT count, BOOL bDrawBackground, const INT *lpDx)
90 {
91     SIZE sz;
92     TEXTMETRICW tm;
93     POINT pt;
94     INT ascent, descent;
95     WORD *glyphs = NULL;
96     DC *dc = physDev->dc;
97     UINT align = GetTextAlign( physDev->hdc );
98     INT char_extra;
99     INT *deltas = NULL;
100
101     if (!count)
102         return TRUE;
103
104
105     if(physDev->font.fontloc == Download) {
106         if(flags & ETO_GLYPH_INDEX)
107             glyphs = (LPWORD)str;
108         else {
109             glyphs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WORD));
110             GetGlyphIndicesW(physDev->hdc, str, count, glyphs, 0);
111         }
112     }
113
114     if(align & TA_UPDATECP) {
115         x = dc->CursPosX;
116         y = dc->CursPosY;
117     }
118
119     pt.x = x;
120     pt.y = y;
121     LPtoDP(physDev->hdc, &pt, 1);
122     x = pt.x;
123     y = pt.y;
124
125     if(physDev->font.fontloc == Download)
126         GetTextExtentPointI(physDev->hdc, glyphs, count, &sz);
127     else
128         GetTextExtentPoint32W(physDev->hdc, str, count, &sz);
129
130     if((char_extra = GetTextCharacterExtra(physDev->hdc)) != 0) {
131         INT i;
132         SIZE tmpsz;
133
134         deltas = HeapAlloc(GetProcessHeap(), 0, count * sizeof(INT));
135         for(i = 0; i < count; i++) {
136             deltas[i] = char_extra;
137             if(lpDx)
138                 deltas[i] += lpDx[i];
139             else {
140                 if(physDev->font.fontloc == Download)
141                     GetTextExtentPointI(physDev->hdc, glyphs + i, 1, &tmpsz);
142                 else
143                     GetTextExtentPoint32W(physDev->hdc, str + i, 1, &tmpsz);
144                 deltas[i] += tmpsz.cx;
145             }
146         }
147     } else if(lpDx)
148         deltas = (INT*)lpDx;
149
150     if(deltas) {
151         SIZE tmpsz;
152         INT i;
153         /* Get the width of the last char and add on all the offsets */
154         if(physDev->font.fontloc == Download)
155             GetTextExtentPointI(physDev->hdc, glyphs + count - 1, 1, &tmpsz);
156         else
157             GetTextExtentPoint32W(physDev->hdc, str + count - 1, 1, &tmpsz);
158         for(i = 0; i < count-1; i++)
159             tmpsz.cx += deltas[i];
160         sz.cx = tmpsz.cx; /* sz.cy remains untouched */
161     }
162
163     sz.cx = INTERNAL_XWSTODS(dc, sz.cx);
164     sz.cy = INTERNAL_YWSTODS(dc, sz.cy);
165
166     GetTextMetricsW(physDev->hdc, &tm);
167     ascent = INTERNAL_YWSTODS(dc, tm.tmAscent);
168     descent = INTERNAL_YWSTODS(dc, tm.tmDescent);
169
170     TRACE("textAlign = %x\n", align);
171     switch(align & (TA_LEFT | TA_CENTER | TA_RIGHT) ) {
172     case TA_LEFT:
173         if(align & TA_UPDATECP)
174         {
175             POINT pt;
176             pt.x = x + sz.cx;
177             pt.y = y;
178             DPtoLP( physDev->hdc, &pt, 1 );
179             dc->CursPosX = pt.x;
180         }
181         break;
182
183     case TA_CENTER:
184         x -= sz.cx/2;
185         break;
186
187     case TA_RIGHT:
188         x -= sz.cx;
189         if(align & TA_UPDATECP)
190         {
191             POINT pt;
192             pt.x = x;
193             pt.y = y;
194             DPtoLP( physDev->hdc, &pt, 1 );
195             dc->CursPosX = pt.x;
196         }
197         break;
198     }
199
200     switch(align & (TA_TOP | TA_BASELINE | TA_BOTTOM) ) {
201     case TA_TOP:
202         y += ascent;
203         break;
204
205     case TA_BASELINE:
206         break;
207
208     case TA_BOTTOM:
209         y -= descent;
210         break;
211     }
212
213     if ((GetBkMode( physDev->hdc ) != TRANSPARENT) && bDrawBackground)
214     {
215         PSDRV_WriteGSave(physDev);
216         PSDRV_WriteNewPath(physDev);
217         PSDRV_WriteRectangle(physDev, x, y - ascent, sz.cx,
218                              ascent + descent);
219         PSDRV_WriteSetColor(physDev, &physDev->bkColor);
220         PSDRV_WriteFill(physDev);
221         PSDRV_WriteGRestore(physDev);
222     }
223
224     PSDRV_WriteMoveTo(physDev, x, y);
225
226     if(!deltas) {
227         if(physDev->font.fontloc == Download)
228             PSDRV_WriteDownloadGlyphShow(physDev, glyphs, count);
229         else
230             PSDRV_WriteBuiltinGlyphShow(physDev, str, count);
231     }
232     else {
233         INT i;
234         float dx = 0.0, dy = 0.0;
235         float cos_theta = cos(physDev->font.escapement * M_PI / 1800.0);
236         float sin_theta = sin(physDev->font.escapement * M_PI / 1800.0);
237         for(i = 0; i < count-1; i++) {
238             TRACE("lpDx[%d] = %d\n", i, deltas[i]);
239             if(physDev->font.fontloc == Download)
240                 PSDRV_WriteDownloadGlyphShow(physDev, glyphs + i, 1);
241             else
242                 PSDRV_WriteBuiltinGlyphShow(physDev, str + i, 1);
243             dx += deltas[i] * cos_theta;
244             dy -= deltas[i] * sin_theta;
245             PSDRV_WriteMoveTo(physDev, x + INTERNAL_XWSTODS(dc, dx),
246                               y + INTERNAL_YWSTODS(dc, dy));
247         }
248         if(physDev->font.fontloc == Download)
249             PSDRV_WriteDownloadGlyphShow(physDev, glyphs + i, 1);
250         else
251             PSDRV_WriteBuiltinGlyphShow(physDev, str + i, 1);
252         if(deltas != lpDx)
253             HeapFree(GetProcessHeap(), 0, deltas);
254     }
255
256     /*
257      * Underline and strikeout attributes.
258      */
259     if ((tm.tmUnderlined) || (tm.tmStruckOut)) {
260
261         /* Get the thickness and the position for the underline attribute */
262         /* We'll use the same thickness for the strikeout attribute       */
263
264         INT escapement =  physDev->font.escapement;
265
266         /* Do the underline */
267
268         if (tm.tmUnderlined) {
269             PSDRV_WriteNewPath(physDev); /* will be closed by WriteRectangle */
270             if (escapement != 0)  /* rotated text */
271             {
272                 PSDRV_WriteGSave(physDev);  /* save the graphics state */
273                 PSDRV_WriteMoveTo(physDev, x, y); /* move to the start */
274
275                 /* temporarily rotate the coord system */
276                 PSDRV_WriteRotate(physDev, -escapement/10);
277
278                 /* draw the underline relative to the starting point */
279                 PSDRV_WriteRRectangle(physDev, 0, -physDev->font.underlinePosition,
280                                       sz.cx, physDev->font.underlineThickness);
281             }
282             else
283                 PSDRV_WriteRectangle(physDev, x, y - physDev->font.underlinePosition,
284                                      sz.cx, physDev->font.underlineThickness);
285
286             PSDRV_WriteFill(physDev);
287
288             if (escapement != 0)  /* rotated text */
289                 PSDRV_WriteGRestore(physDev);  /* restore the graphics state */
290         }
291
292         /* Do the strikeout */
293
294         if (tm.tmStruckOut) {
295             PSDRV_WriteNewPath(physDev); /* will be closed by WriteRectangle */
296             if (escapement != 0)  /* rotated text */
297             {
298                 PSDRV_WriteGSave(physDev);  /* save the graphics state */
299                 PSDRV_WriteMoveTo(physDev, x, y); /* move to the start */
300
301                 /* temporarily rotate the coord system */
302                 PSDRV_WriteRotate(physDev, -escapement/10);
303
304                 /* draw the line relative to the starting point */
305                 PSDRV_WriteRRectangle(physDev, 0, -physDev->font.strikeoutPosition,
306                                       sz.cx, physDev->font.strikeoutThickness);
307             }
308             else
309                 PSDRV_WriteRectangle(physDev, x, y - physDev->font.strikeoutPosition,
310                                      sz.cx, physDev->font.strikeoutThickness);
311
312             PSDRV_WriteFill(physDev);
313
314             if (escapement != 0)  /* rotated text */
315                 PSDRV_WriteGRestore(physDev);  /* restore the graphics state */
316         }
317     }
318
319     if(glyphs && glyphs != str) HeapFree(GetProcessHeap(), 0, glyphs);
320     return TRUE;
321 }