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