Moved DC_SetupGC functions to graphics/x11drv/graphics.c.
[wine] / graphics / x11drv / text.c
1 /*
2  * X11 graphics driver text functions
3  *
4  * Copyright 1993,1994 Alexandre Julliard
5  */
6
7 #include <stdlib.h>
8 #include "ts_xlib.h"
9 #include <X11/Xatom.h>
10 #include "windows.h"
11 #include <math.h>
12 #include "dc.h"
13 #include "gdi.h"
14 /*#include "callback.h"*/
15 #include "heap.h"
16 #include "x11font.h"
17 #include "debugstr.h"
18 #include "debug.h"
19
20 #define SWAP_INT(a,b)  { int t = a; a = b; b = t; }
21 #define IROUND(x) (int)((x)>0? (x)+0.5 : (x) - 0.5)
22
23 /***********************************************************************
24  *           X11DRV_ExtTextOut
25  */
26 BOOL32
27 X11DRV_ExtTextOut( DC *dc, INT32 x, INT32 y, UINT32 flags,
28                    const RECT32 *lprect, LPCSTR str, UINT32 count,
29                    const INT32 *lpDx )
30 {
31     int                 i;
32     fontObject*         pfo;
33     INT32               width, ascent, descent, xwidth, ywidth;
34     XFontStruct*        font;
35     RECT32              rect;
36     char                dfBreakChar, lfUnderline, lfStrikeOut;
37     BOOL32              rotated = FALSE;
38
39     if (!X11DRV_SetupGCForText( dc )) return TRUE;
40
41     pfo = XFONT_GetFontObject( dc->u.x.font );
42     font = pfo->fs;
43      
44     if (pfo->lf.lfEscapement && pfo->lpX11Trans)
45         rotated = TRUE;
46     dfBreakChar = (char)pfo->fi->df.dfBreakChar;
47     lfUnderline = (pfo->fo_flags & FO_SYNTH_UNDERLINE) ? 1 : 0;
48     lfStrikeOut = (pfo->fo_flags & FO_SYNTH_STRIKEOUT) ? 1 : 0;
49
50     TRACE(text,"hdc=%04x df=%04x %d,%d %s, %d  flags=%d lpDx=%p\n",
51           dc->hSelf, (UINT16)(dc->u.x.font), x, y,
52           debugstr_an (str, count), count, flags, lpDx);
53
54     /* some strings sent here end in a newline for whatever reason.  I have no
55        clue what the right treatment should be in general, but ignoring
56        terminating newlines seems ok.  MW, April 1998.  */
57     if (count > 0 && str[count - 1] == '\n') count--;
58
59     if (lprect != NULL) TRACE(text, "\trect=(%d,%d - %d,%d)\n",
60                                      lprect->left, lprect->top,
61                                      lprect->right, lprect->bottom );
62       /* Setup coordinates */
63
64     if (dc->w.textAlign & TA_UPDATECP)
65     {
66         x = dc->w.CursPosX;
67         y = dc->w.CursPosY;
68     }
69
70     if (flags & (ETO_OPAQUE | ETO_CLIPPED))  /* there's a rectangle */
71     {
72         if (!lprect)  /* not always */
73         {
74             SIZE32 sz;
75             if (flags & ETO_CLIPPED)  /* Can't clip with no rectangle */
76               return FALSE;
77             if (!X11DRV_GetTextExtentPoint( dc, str, count, &sz ))
78               return FALSE;
79             rect.left   = XLPTODP( dc, x );
80             rect.right  = XLPTODP( dc, x+sz.cx );
81             rect.top    = YLPTODP( dc, y );
82             rect.bottom = YLPTODP( dc, y+sz.cy );
83         }
84         else
85         {
86             rect.left   = XLPTODP( dc, lprect->left );
87             rect.right  = XLPTODP( dc, lprect->right );
88             rect.top    = YLPTODP( dc, lprect->top );
89             rect.bottom = YLPTODP( dc, lprect->bottom );
90         }
91         if (rect.right < rect.left) SWAP_INT( rect.left, rect.right );
92         if (rect.bottom < rect.top) SWAP_INT( rect.top, rect.bottom );
93     }
94
95     x = XLPTODP( dc, x );
96     y = YLPTODP( dc, y );
97
98     TRACE(text,"\treal coord: x=%i, y=%i, rect=(%d,%d - %d,%d)\n",
99                           x, y, rect.left, rect.top, rect.right, rect.bottom);
100
101       /* Draw the rectangle */
102
103     if (flags & ETO_OPAQUE)
104     {
105         TSXSetForeground( display, dc->u.x.gc, dc->u.x.backgroundPixel );
106         TSXFillRectangle( display, dc->u.x.drawable, dc->u.x.gc,
107                         dc->w.DCOrgX + rect.left, dc->w.DCOrgY + rect.top,
108                         rect.right-rect.left, rect.bottom-rect.top );
109     }
110     if (!count) return TRUE;  /* Nothing more to do */
111
112       /* Compute text starting position */
113
114     if (lpDx) /* have explicit character cell x offsets in logical coordinates */
115     {
116         int extra = dc->wndExtX / 2;
117         for (i = width = 0; i < count; i++) width += lpDx[i];
118         width = (width * dc->vportExtX + extra ) / dc->wndExtX;
119     }
120     else
121     {
122         SIZE32 sz;
123         if (!X11DRV_GetTextExtentPoint( dc, str, count, &sz ))
124             return FALSE;
125         width = XLSTODS(dc, sz.cx);
126     }
127     ascent = pfo->lpX11Trans ? pfo->lpX11Trans->ascent : font->ascent;
128     descent = pfo->lpX11Trans ? pfo->lpX11Trans->descent : font->descent;
129     xwidth = pfo->lpX11Trans ? width * pfo->lpX11Trans->a /
130       pfo->lpX11Trans->pixelsize : width;
131     ywidth = pfo->lpX11Trans ? width * pfo->lpX11Trans->b /
132       pfo->lpX11Trans->pixelsize : 0;
133
134     switch( dc->w.textAlign & (TA_LEFT | TA_RIGHT | TA_CENTER) )
135     {
136       case TA_LEFT:
137           if (dc->w.textAlign & TA_UPDATECP) {
138               dc->w.CursPosX = XDPTOLP( dc, x + xwidth );
139               dc->w.CursPosY = YDPTOLP( dc, y - ywidth );
140           }
141           break;
142       case TA_RIGHT:
143           x -= xwidth;
144           y += ywidth;
145           if (dc->w.textAlign & TA_UPDATECP) {
146               dc->w.CursPosX = XDPTOLP( dc, x );
147               dc->w.CursPosY = YDPTOLP( dc, y );
148           }
149           break;
150       case TA_CENTER:
151           x -= xwidth / 2;
152           y += ywidth / 2;
153           break;
154     }
155
156     switch( dc->w.textAlign & (TA_TOP | TA_BOTTOM | TA_BASELINE) )
157     {
158       case TA_TOP:
159           x -= pfo->lpX11Trans ? ascent * pfo->lpX11Trans->c /
160             pfo->lpX11Trans->pixelsize : 0;
161           y += pfo->lpX11Trans ? ascent * pfo->lpX11Trans->d /
162             pfo->lpX11Trans->pixelsize : ascent;
163           break;
164       case TA_BOTTOM:
165           x += pfo->lpX11Trans ? descent * pfo->lpX11Trans->c /
166             pfo->lpX11Trans->pixelsize : 0;
167           y -= pfo->lpX11Trans ? descent * pfo->lpX11Trans->d /
168             pfo->lpX11Trans->pixelsize : descent;
169           break;
170       case TA_BASELINE:
171           break;
172     }
173
174       /* Set the clip region */
175
176     if (flags & ETO_CLIPPED)
177     {
178         SaveVisRgn( dc->hSelf );
179         CLIPPING_IntersectVisRect( dc, rect.left, rect.top, rect.right,
180                                    rect.bottom, FALSE );
181     }
182
183       /* Draw the text background if necessary */
184
185     if (dc->w.backgroundMode != TRANSPARENT)
186     {
187           /* If rectangle is opaque and clipped, do nothing */
188         if (!(flags & ETO_CLIPPED) || !(flags & ETO_OPAQUE))
189         {
190               /* Only draw if rectangle is not opaque or if some */
191               /* text is outside the rectangle */
192             if (!(flags & ETO_OPAQUE) ||
193                 (x < rect.left) ||
194                 (x + width >= rect.right) ||
195                 (y - ascent < rect.top) ||
196                 (y + descent >= rect.bottom))
197             {
198                 TSXSetForeground( display, dc->u.x.gc, dc->u.x.backgroundPixel );
199                 TSXFillRectangle( display, dc->u.x.drawable, dc->u.x.gc,
200                                 dc->w.DCOrgX + x,
201                                 dc->w.DCOrgY + y - ascent,
202                                 width,
203                                 ascent + descent );
204             }
205         }
206     }
207     
208     /* Draw the text (count > 0 verified) */
209
210     TSXSetForeground( display, dc->u.x.gc, dc->u.x.textPixel );
211     if (!dc->w.charExtra && !dc->w.breakExtra && !lpDx)
212     {
213       if(!rotated)
214       {
215         TSXDrawString( display, dc->u.x.drawable, dc->u.x.gc, 
216                      dc->w.DCOrgX + x, dc->w.DCOrgY + y, str, count );
217       }
218       else 
219       {  
220         /* have to render character by character. */
221         double offset = 0.0;
222         int i;
223
224         for(i=0; i<count; i++) {
225           int char_metric_offset = (unsigned char) str[i] 
226             - font->min_char_or_byte2;
227           int x_i = IROUND((double) (dc->w.DCOrgX + x) + offset *
228                            pfo->lpX11Trans->a / 1000.0 );
229           int y_i = IROUND((double) (dc->w.DCOrgY + y) - offset *
230                            pfo->lpX11Trans->b / 1000.0 );
231
232           TSXDrawString( display, dc->u.x.drawable, dc->u.x.gc,
233                          x_i, y_i, &str[i], 1);
234           offset += (double) (font->per_char ?
235                      font->per_char[char_metric_offset].attributes:
236                      font->min_bounds.attributes);
237         }
238       }
239     }
240     else  /* Now the fun begins... */
241     {
242         XTextItem *items, *pitem;
243         int delta;
244
245         /* allocate max items */
246
247         pitem = items = HEAP_xalloc( GetProcessHeap(), 0,
248                                      count * sizeof(XTextItem) );
249         delta = i = 0;
250         if( lpDx ) /* explicit character widths */
251         {
252             int extra = dc->wndExtX / 2;
253
254             while (i < count)
255             {
256                 /* initialize text item with accumulated delta */
257
258                 pitem->chars  = (char *)str + i;
259                 pitem->delta  = delta;
260                 pitem->nchars = 0;
261                 pitem->font   = None;
262                 delta = 0;
263
264                 /* add characters  to the same XTextItem until new delta
265                  * becomes  non-zero */
266
267                 do
268                 {
269                     delta += (lpDx[i] * dc->vportExtX + extra) / dc->wndExtX
270                                             - TSXTextWidth( font, str + i, 1);
271                     pitem->nchars++;
272                 } while ((++i < count) && !delta);
273                 pitem++;
274            }
275         }
276         else /* charExtra or breakExtra */
277         {
278             while (i < count)
279             {
280                 pitem->chars  = (char *)str + i;
281                 pitem->delta  = delta;
282                 pitem->nchars = 0;
283                 pitem->font   = None;
284                 delta = 0;
285
286                 do
287                 {
288                     delta += dc->w.charExtra;
289                     if (str[i] == (char)dfBreakChar) delta += dc->w.breakExtra;
290                     pitem->nchars++;
291                 } while ((++i < count) && !delta);
292                 pitem++;
293             } 
294         }
295
296         TSXDrawText( display, dc->u.x.drawable, dc->u.x.gc,
297                    dc->w.DCOrgX + x, dc->w.DCOrgY + y, items, pitem - items );
298         HeapFree( GetProcessHeap(), 0, items );
299     }
300
301       /* Draw underline and strike-out if needed */
302
303     if (lfUnderline)
304     {
305         long linePos, lineWidth;       
306
307         if (!TSXGetFontProperty( font, XA_UNDERLINE_POSITION, &linePos ))
308             linePos = descent - 1;
309         if (!TSXGetFontProperty( font, XA_UNDERLINE_THICKNESS, &lineWidth ))
310             lineWidth = 0;
311         else if (lineWidth == 1) lineWidth = 0;
312         TSXSetLineAttributes( display, dc->u.x.gc, lineWidth,
313                             LineSolid, CapRound, JoinBevel ); 
314         TSXDrawLine( display, dc->u.x.drawable, dc->u.x.gc,
315                    dc->w.DCOrgX + x, dc->w.DCOrgY + y + linePos,
316                    dc->w.DCOrgX + x + width, dc->w.DCOrgY + y + linePos );
317     }
318     if (lfStrikeOut)
319     {
320         long lineAscent, lineDescent;
321         if (!TSXGetFontProperty( font, XA_STRIKEOUT_ASCENT, &lineAscent ))
322             lineAscent = ascent / 2;
323         if (!TSXGetFontProperty( font, XA_STRIKEOUT_DESCENT, &lineDescent ))
324             lineDescent = -lineAscent * 2 / 3;
325         TSXSetLineAttributes( display, dc->u.x.gc, lineAscent + lineDescent,
326                             LineSolid, CapRound, JoinBevel ); 
327         TSXDrawLine( display, dc->u.x.drawable, dc->u.x.gc,
328                    dc->w.DCOrgX + x, dc->w.DCOrgY + y - lineAscent,
329                    dc->w.DCOrgX + x + width, dc->w.DCOrgY + y - lineAscent );
330     }
331
332     if (flags & ETO_CLIPPED) 
333         RestoreVisRgn( dc->hSelf );
334
335     return TRUE;
336 }
337