Authors: Martin Fuchs <martin-fuchs@gmx.net>, Ge van Geldorp <ge@gse.nl>
[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     PSDRV_SetClip(physDev);
51
52     /* set clipping and/or draw background */
53     if ((flags & (ETO_CLIPPED | ETO_OPAQUE)) && (lprect != NULL))
54     {
55         rect = *lprect;
56         LPtoDP( physDev->hdc, (POINT *)&rect, 2 );
57         PSDRV_WriteGSave(physDev);
58         PSDRV_WriteRectangle(physDev, rect.left, rect.top, rect.right - rect.left,
59                              rect.bottom - rect.top);
60
61         if (flags & ETO_OPAQUE)
62         {
63             bOpaque = TRUE;
64             PSDRV_WriteGSave(physDev);
65             PSDRV_WriteSetColor(physDev, &physDev->bkColor);
66             PSDRV_WriteFill(physDev);
67             PSDRV_WriteGRestore(physDev);
68         }
69
70         if (flags & ETO_CLIPPED)
71         {
72             bClipped = TRUE;
73             PSDRV_WriteClip(physDev);
74         }
75
76         bResult = PSDRV_Text(physDev, x, y, flags, str, count, !(bClipped && bOpaque), lpDx);
77         PSDRV_WriteGRestore(physDev);
78     }
79     else
80     {
81         bResult = PSDRV_Text(physDev, x, y, flags, str, count, TRUE, lpDx);
82     }
83
84     PSDRV_ResetClip(physDev);
85     return bResult;
86 }
87
88 /***********************************************************************
89  *           PSDRV_Text
90  */
91 static BOOL PSDRV_Text(PSDRV_PDEVICE *physDev, INT x, INT y, UINT flags, LPCWSTR str,
92                        UINT count, BOOL bDrawBackground, const INT *lpDx)
93 {
94     SIZE sz;
95     TEXTMETRICW tm;
96     POINT pt;
97     INT ascent, descent;
98     WORD *glyphs = NULL;
99     DC *dc = physDev->dc;
100     UINT align = GetTextAlign( physDev->hdc );
101     INT char_extra;
102     INT *deltas = NULL;
103     double cosEsc, sinEsc;
104     LOGFONTW lf;
105
106     if (!count)
107         return TRUE;
108
109     GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
110     if(lf.lfEscapement != 0) {
111         cosEsc = cos(lf.lfEscapement * M_PI / 1800);
112         sinEsc = sin(lf.lfEscapement * M_PI / 1800);
113     } else {
114         cosEsc = 1;
115         sinEsc = 0;
116     }
117
118     if(physDev->font.fontloc == Download) {
119         if(flags & ETO_GLYPH_INDEX)
120             glyphs = (LPWORD)str;
121         else {
122             glyphs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WORD));
123             GetGlyphIndicesW(physDev->hdc, str, count, glyphs, 0);
124         }
125     }
126
127     if(align & TA_UPDATECP) {
128         x = dc->CursPosX;
129         y = dc->CursPosY;
130     }
131
132     pt.x = x;
133     pt.y = y;
134     LPtoDP(physDev->hdc, &pt, 1);
135     x = pt.x;
136     y = pt.y;
137
138     if(physDev->font.fontloc == Download)
139         GetTextExtentPointI(physDev->hdc, glyphs, count, &sz);
140     else
141         GetTextExtentPoint32W(physDev->hdc, str, count, &sz);
142
143     if((char_extra = GetTextCharacterExtra(physDev->hdc)) != 0) {
144         INT i;
145         SIZE tmpsz;
146
147         deltas = HeapAlloc(GetProcessHeap(), 0, count * sizeof(INT));
148         for(i = 0; i < count; i++) {
149             if(lpDx)
150                 deltas[i] = lpDx[i] + char_extra;
151             else {
152                 if(physDev->font.fontloc == Download)
153                     GetTextExtentPointI(physDev->hdc, glyphs + i, 1, &tmpsz);
154                 else
155                     GetTextExtentPoint32W(physDev->hdc, str + i, 1, &tmpsz);
156                 deltas[i] = tmpsz.cx;
157             }
158         }
159     } else if(lpDx)
160         deltas = (INT*)lpDx;
161
162     if(deltas) {
163         SIZE tmpsz;
164         INT i;
165         /* Get the width of the last char and add on all the offsets */
166         if(physDev->font.fontloc == Download)
167             GetTextExtentPointI(physDev->hdc, glyphs + count - 1, 1, &tmpsz);
168         else
169             GetTextExtentPoint32W(physDev->hdc, str + count - 1, 1, &tmpsz);
170         for(i = 0; i < count-1; i++)
171             tmpsz.cx += deltas[i];
172         sz.cx = tmpsz.cx; /* sz.cy remains untouched */
173     }
174
175     sz.cx = PSDRV_XWStoDS(physDev, sz.cx);
176     sz.cy = PSDRV_YWStoDS(physDev, sz.cy);
177
178     GetTextMetricsW(physDev->hdc, &tm);
179     ascent = PSDRV_YWStoDS(physDev, tm.tmAscent);
180     descent = PSDRV_YWStoDS(physDev, tm.tmDescent);
181
182     TRACE("textAlign = %x\n", align);
183     switch(align & (TA_LEFT | TA_CENTER | TA_RIGHT) ) {
184     case TA_LEFT:
185         if(align & TA_UPDATECP)
186         {
187             POINT pt;
188             pt.x = x + sz.cx * cosEsc;
189             pt.y = y - sz.cx * sinEsc;
190             DPtoLP( physDev->hdc, &pt, 1 );
191             dc->CursPosX = pt.x;
192             dc->CursPosY = pt.y;
193         }
194         break;
195
196     case TA_CENTER:
197         x -= sz.cx * cosEsc / 2;
198         y += sz.cx * sinEsc / 2;
199         break;
200
201     case TA_RIGHT:
202         x -= sz.cx * cosEsc;
203         y += sz.cx * sinEsc;
204         if(align & TA_UPDATECP)
205         {
206             POINT pt;
207             pt.x = x;
208             pt.y = y;
209             DPtoLP( physDev->hdc, &pt, 1 );
210             dc->CursPosX = pt.x;
211             dc->CursPosY = pt.y;
212         }
213         break;
214     }
215
216     switch(align & (TA_TOP | TA_BASELINE | TA_BOTTOM) ) {
217     case TA_TOP:
218         y += ascent * cosEsc;
219         x += ascent * sinEsc;
220         break;
221
222     case TA_BASELINE:
223         break;
224
225     case TA_BOTTOM:
226         y -= descent * cosEsc;
227         x -= descent * sinEsc;
228         break;
229     }
230
231     if ((GetBkMode( physDev->hdc ) != TRANSPARENT) && bDrawBackground)
232     {
233         PSDRV_WriteGSave(physDev);
234         PSDRV_WriteNewPath(physDev);
235         PSDRV_WriteRectangle(physDev, x, y - ascent, sz.cx,
236                              ascent + descent);
237         PSDRV_WriteSetColor(physDev, &physDev->bkColor);
238         PSDRV_WriteFill(physDev);
239         PSDRV_WriteGRestore(physDev);
240     }
241
242     PSDRV_WriteMoveTo(physDev, x, y);
243
244     if(!deltas) {
245         if(physDev->font.fontloc == Download)
246             PSDRV_WriteDownloadGlyphShow(physDev, glyphs, count);
247         else
248             PSDRV_WriteBuiltinGlyphShow(physDev, str, count);
249     }
250     else {
251         INT i;
252         float dx = 0.0, dy = 0.0;
253         float cos_theta = cos(physDev->font.escapement * M_PI / 1800.0);
254         float sin_theta = sin(physDev->font.escapement * M_PI / 1800.0);
255         for(i = 0; i < count-1; i++) {
256             TRACE("lpDx[%d] = %d\n", i, deltas[i]);
257             if(physDev->font.fontloc == Download)
258                 PSDRV_WriteDownloadGlyphShow(physDev, glyphs + i, 1);
259             else
260                 PSDRV_WriteBuiltinGlyphShow(physDev, str + i, 1);
261             dx += deltas[i] * cos_theta;
262             dy -= deltas[i] * sin_theta;
263             PSDRV_WriteMoveTo(physDev, x + PSDRV_XWStoDS(physDev, dx),
264                               y + PSDRV_YWStoDS(physDev, dy));
265         }
266         if(physDev->font.fontloc == Download)
267             PSDRV_WriteDownloadGlyphShow(physDev, glyphs + i, 1);
268         else
269             PSDRV_WriteBuiltinGlyphShow(physDev, str + i, 1);
270         if(deltas != lpDx)
271             HeapFree(GetProcessHeap(), 0, deltas);
272     }
273
274     /*
275      * Underline and strikeout attributes.
276      */
277     if ((tm.tmUnderlined) || (tm.tmStruckOut)) {
278
279         /* Get the thickness and the position for the underline attribute */
280         /* We'll use the same thickness for the strikeout attribute       */
281
282         INT escapement =  physDev->font.escapement;
283
284         /* Do the underline */
285
286         if (tm.tmUnderlined) {
287             PSDRV_WriteNewPath(physDev); /* will be closed by WriteRectangle */
288             if (escapement != 0)  /* rotated text */
289             {
290                 PSDRV_WriteGSave(physDev);  /* save the graphics state */
291                 PSDRV_WriteMoveTo(physDev, x, y); /* move to the start */
292
293                 /* temporarily rotate the coord system */
294                 PSDRV_WriteRotate(physDev, -escapement/10);
295
296                 /* draw the underline relative to the starting point */
297                 PSDRV_WriteRRectangle(physDev, 0, -physDev->font.underlinePosition,
298                                       sz.cx, physDev->font.underlineThickness);
299             }
300             else
301                 PSDRV_WriteRectangle(physDev, x, y - physDev->font.underlinePosition,
302                                      sz.cx, physDev->font.underlineThickness);
303
304             PSDRV_WriteFill(physDev);
305
306             if (escapement != 0)  /* rotated text */
307                 PSDRV_WriteGRestore(physDev);  /* restore the graphics state */
308         }
309
310         /* Do the strikeout */
311
312         if (tm.tmStruckOut) {
313             PSDRV_WriteNewPath(physDev); /* will be closed by WriteRectangle */
314             if (escapement != 0)  /* rotated text */
315             {
316                 PSDRV_WriteGSave(physDev);  /* save the graphics state */
317                 PSDRV_WriteMoveTo(physDev, x, y); /* move to the start */
318
319                 /* temporarily rotate the coord system */
320                 PSDRV_WriteRotate(physDev, -escapement/10);
321
322                 /* draw the line relative to the starting point */
323                 PSDRV_WriteRRectangle(physDev, 0, -physDev->font.strikeoutPosition,
324                                       sz.cx, physDev->font.strikeoutThickness);
325             }
326             else
327                 PSDRV_WriteRectangle(physDev, x, y - physDev->font.strikeoutPosition,
328                                      sz.cx, physDev->font.strikeoutThickness);
329
330             PSDRV_WriteFill(physDev);
331
332             if (escapement != 0)  /* rotated text */
333                 PSDRV_WriteGRestore(physDev);  /* restore the graphics state */
334         }
335     }
336
337     if(glyphs && glyphs != str) HeapFree(GetProcessHeap(), 0, glyphs);
338     return TRUE;
339 }