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