Release 980712
[wine.git] / graphics / x11drv / text.c
blob09c78b0d27b19ad373c18b6c6b4d8001983c257d
1 /*
2 * X11 graphics driver text functions
4 * Copyright 1993,1994 Alexandre Julliard
5 */
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"
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)
23 /***********************************************************************
24 * X11DRV_ExtTextOut
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 )
31 HRGN32 hRgnClip = 0;
32 int i;
33 fontObject* pfo;
34 INT32 width, ascent, descent, xwidth, ywidth;
35 XFontStruct* font;
36 RECT32 rect;
37 char dfBreakChar, lfUnderline, lfStrikeOut;
38 BOOL32 rotated = FALSE;
40 if (!DC_SetupGCForText( dc )) return TRUE;
42 pfo = XFONT_GetFontObject( dc->u.x.font );
43 font = pfo->fs;
45 if (pfo->lf.lfEscapement && pfo->lpX11Trans)
46 rotated = TRUE;
47 dfBreakChar = (char)pfo->fi->df.dfBreakChar;
48 lfUnderline = (pfo->fo_flags & FO_SYNTH_UNDERLINE) ? 1 : 0;
49 lfStrikeOut = (pfo->fo_flags & FO_SYNTH_STRIKEOUT) ? 1 : 0;
51 TRACE(text,"hdc=%04x df=%04x %d,%d %s, %d flags=%d lpDx=%p\n",
52 dc->hSelf, (UINT16)(dc->u.x.font), x, y,
53 debugstr_an (str, count), count, flags, lpDx);
55 /* some strings sent here end in a newline for whatever reason. I have no
56 clue what the right treatment should be in general, but ignoring
57 terminating newlines seems ok. MW, April 1998. */
58 if (count > 0 && str[count - 1] == '\n') count--;
60 if (lprect != NULL) TRACE(text, "\trect=(%d,%d - %d,%d)\n",
61 lprect->left, lprect->top,
62 lprect->right, lprect->bottom );
63 /* Setup coordinates */
65 if (dc->w.textAlign & TA_UPDATECP)
67 x = dc->w.CursPosX;
68 y = dc->w.CursPosY;
71 if (flags & (ETO_OPAQUE | ETO_CLIPPED)) /* there's a rectangle */
73 if (!lprect) /* not always */
75 SIZE32 sz;
76 if (flags & ETO_CLIPPED) /* Can't clip with no rectangle */
77 return FALSE;
78 if (!X11DRV_GetTextExtentPoint( dc, str, count, &sz ))
79 return FALSE;
80 rect.left = XLPTODP( dc, x );
81 rect.right = XLPTODP( dc, x+sz.cx );
82 rect.top = YLPTODP( dc, y );
83 rect.bottom = YLPTODP( dc, y+sz.cy );
85 else
87 rect.left = XLPTODP( dc, lprect->left );
88 rect.right = XLPTODP( dc, lprect->right );
89 rect.top = YLPTODP( dc, lprect->top );
90 rect.bottom = YLPTODP( dc, lprect->bottom );
92 if (rect.right < rect.left) SWAP_INT( rect.left, rect.right );
93 if (rect.bottom < rect.top) SWAP_INT( rect.top, rect.bottom );
96 x = XLPTODP( dc, x );
97 y = YLPTODP( dc, y );
99 TRACE(text,"\treal coord: x=%i, y=%i, rect=(%d,%d - %d,%d)\n",
100 x, y, rect.left, rect.top, rect.right, rect.bottom);
102 /* Draw the rectangle */
104 if (flags & ETO_OPAQUE)
106 TSXSetForeground( display, dc->u.x.gc, dc->w.backgroundPixel );
107 TSXFillRectangle( display, dc->u.x.drawable, dc->u.x.gc,
108 dc->w.DCOrgX + rect.left, dc->w.DCOrgY + rect.top,
109 rect.right-rect.left, rect.bottom-rect.top );
111 if (!count) return TRUE; /* Nothing more to do */
113 /* Compute text starting position */
115 if (lpDx) /* have explicit character cell x offsets in logical coordinates */
117 int extra = dc->wndExtX / 2;
118 for (i = width = 0; i < count; i++) width += lpDx[i];
119 width = (width * dc->vportExtX + extra ) / dc->wndExtX;
121 else
123 SIZE32 sz;
124 if (!X11DRV_GetTextExtentPoint( dc, str, count, &sz ))
125 return FALSE;
126 width = XLSTODS(dc, sz.cx);
128 ascent = pfo->lpX11Trans ? pfo->lpX11Trans->ascent : font->ascent;
129 descent = pfo->lpX11Trans ? pfo->lpX11Trans->descent : font->descent;
130 xwidth = pfo->lpX11Trans ? width * pfo->lpX11Trans->a /
131 pfo->lpX11Trans->pixelsize : width;
132 ywidth = pfo->lpX11Trans ? width * pfo->lpX11Trans->b /
133 pfo->lpX11Trans->pixelsize : 0;
135 switch( dc->w.textAlign & (TA_LEFT | TA_RIGHT | TA_CENTER) )
137 case TA_LEFT:
138 if (dc->w.textAlign & TA_UPDATECP) {
139 dc->w.CursPosX = XDPTOLP( dc, x + xwidth );
140 dc->w.CursPosY = YDPTOLP( dc, y - ywidth );
142 break;
143 case TA_RIGHT:
144 x -= xwidth;
145 y += ywidth;
146 if (dc->w.textAlign & TA_UPDATECP) {
147 dc->w.CursPosX = XDPTOLP( dc, x );
148 dc->w.CursPosY = YDPTOLP( dc, y );
150 break;
151 case TA_CENTER:
152 x -= xwidth / 2;
153 y += ywidth / 2;
154 break;
157 switch( dc->w.textAlign & (TA_TOP | TA_BOTTOM | TA_BASELINE) )
159 case TA_TOP:
160 x -= pfo->lpX11Trans ? ascent * pfo->lpX11Trans->c /
161 pfo->lpX11Trans->pixelsize : 0;
162 y += pfo->lpX11Trans ? ascent * pfo->lpX11Trans->d /
163 pfo->lpX11Trans->pixelsize : ascent;
164 break;
165 case TA_BOTTOM:
166 x += pfo->lpX11Trans ? descent * pfo->lpX11Trans->c /
167 pfo->lpX11Trans->pixelsize : 0;
168 y -= pfo->lpX11Trans ? descent * pfo->lpX11Trans->d /
169 pfo->lpX11Trans->pixelsize : descent;
170 break;
171 case TA_BASELINE:
172 break;
175 /* Set the clip region */
177 if (flags & ETO_CLIPPED)
179 hRgnClip = dc->w.hClipRgn;
180 CLIPPING_IntersectClipRect( dc, rect.left, rect.top, rect.right,
181 rect.bottom, CLIP_INTERSECT|CLIP_KEEPRGN );
184 /* Draw the text background if necessary */
186 if (dc->w.backgroundMode != TRANSPARENT)
188 /* If rectangle is opaque and clipped, do nothing */
189 if (!(flags & ETO_CLIPPED) || !(flags & ETO_OPAQUE))
191 /* Only draw if rectangle is not opaque or if some */
192 /* text is outside the rectangle */
193 if (!(flags & ETO_OPAQUE) ||
194 (x < rect.left) ||
195 (x + width >= rect.right) ||
196 (y - ascent < rect.top) ||
197 (y + descent >= rect.bottom))
199 TSXSetForeground( display, dc->u.x.gc, dc->w.backgroundPixel );
200 TSXFillRectangle( display, dc->u.x.drawable, dc->u.x.gc,
201 dc->w.DCOrgX + x,
202 dc->w.DCOrgY + y - ascent,
203 width,
204 ascent + descent );
209 /* Draw the text (count > 0 verified) */
211 TSXSetForeground( display, dc->u.x.gc, dc->w.textPixel );
212 if (!dc->w.charExtra && !dc->w.breakExtra && !lpDx)
214 if(!rotated)
216 TSXDrawString( display, dc->u.x.drawable, dc->u.x.gc,
217 dc->w.DCOrgX + x, dc->w.DCOrgY + y, str, count );
219 else
221 /* have to render character by character. */
222 double offset = 0.0;
223 int i;
225 for(i=0; i<count; i++) {
226 int char_metric_offset = (unsigned char) str[i]
227 - font->min_char_or_byte2;
228 int x_i = IROUND((double) (dc->w.DCOrgX + x) + offset *
229 pfo->lpX11Trans->a / 1000.0 );
230 int y_i = IROUND((double) (dc->w.DCOrgY + y) - offset *
231 pfo->lpX11Trans->b / 1000.0 );
233 TSXDrawString( display, dc->u.x.drawable, dc->u.x.gc,
234 x_i, y_i, &str[i], 1);
235 offset += (double) (font->per_char ?
236 font->per_char[char_metric_offset].attributes:
237 font->min_bounds.attributes);
241 else /* Now the fun begins... */
243 XTextItem *items, *pitem;
244 int delta;
246 /* allocate max items */
248 pitem = items = HEAP_xalloc( GetProcessHeap(), 0,
249 count * sizeof(XTextItem) );
250 delta = i = 0;
251 if( lpDx ) /* explicit character widths */
253 int extra = dc->wndExtX / 2;
255 while (i < count)
257 /* initialize text item with accumulated delta */
259 pitem->chars = (char *)str + i;
260 pitem->delta = delta;
261 pitem->nchars = 0;
262 pitem->font = None;
263 delta = 0;
265 /* add characters to the same XTextItem until new delta
266 * becomes non-zero */
270 delta += (lpDx[i] * dc->vportExtX + extra) / dc->wndExtX
271 - TSXTextWidth( font, str + i, 1);
272 pitem->nchars++;
273 } while ((++i < count) && !delta);
274 pitem++;
277 else /* charExtra or breakExtra */
279 while (i < count)
281 pitem->chars = (char *)str + i;
282 pitem->delta = delta;
283 pitem->nchars = 0;
284 pitem->font = None;
285 delta = 0;
289 delta += dc->w.charExtra;
290 if (str[i] == (char)dfBreakChar) delta += dc->w.breakExtra;
291 pitem->nchars++;
292 } while ((++i < count) && !delta);
293 pitem++;
297 TSXDrawText( display, dc->u.x.drawable, dc->u.x.gc,
298 dc->w.DCOrgX + x, dc->w.DCOrgY + y, items, pitem - items );
299 HeapFree( GetProcessHeap(), 0, items );
302 /* Draw underline and strike-out if needed */
304 if (lfUnderline)
306 long linePos, lineWidth;
308 if (!TSXGetFontProperty( font, XA_UNDERLINE_POSITION, &linePos ))
309 linePos = descent - 1;
310 if (!TSXGetFontProperty( font, XA_UNDERLINE_THICKNESS, &lineWidth ))
311 lineWidth = 0;
312 else if (lineWidth == 1) lineWidth = 0;
313 TSXSetLineAttributes( display, dc->u.x.gc, lineWidth,
314 LineSolid, CapRound, JoinBevel );
315 TSXDrawLine( display, dc->u.x.drawable, dc->u.x.gc,
316 dc->w.DCOrgX + x, dc->w.DCOrgY + y + linePos,
317 dc->w.DCOrgX + x + width, dc->w.DCOrgY + y + linePos );
319 if (lfStrikeOut)
321 long lineAscent, lineDescent;
322 if (!TSXGetFontProperty( font, XA_STRIKEOUT_ASCENT, &lineAscent ))
323 lineAscent = ascent / 2;
324 if (!TSXGetFontProperty( font, XA_STRIKEOUT_DESCENT, &lineDescent ))
325 lineDescent = -lineAscent * 2 / 3;
326 TSXSetLineAttributes( display, dc->u.x.gc, lineAscent + lineDescent,
327 LineSolid, CapRound, JoinBevel );
328 TSXDrawLine( display, dc->u.x.drawable, dc->u.x.gc,
329 dc->w.DCOrgX + x, dc->w.DCOrgY + y - lineAscent,
330 dc->w.DCOrgX + x + width, dc->w.DCOrgY + y - lineAscent );
333 if (flags & ETO_CLIPPED)
335 SelectClipRgn32( dc->hSelf, hRgnClip );
336 DeleteObject32( hRgnClip );
338 return TRUE;