Keep track of per-column information inside the listview.
[wine] / dlls / gdi / enhmfdrv / graphics.c
1 /*
2  * Enhanced MetaFile driver graphics functions
3  *
4  * Copyright 1999 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
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "gdi.h"
28 #include "enhmfdrv/enhmetafiledrv.h"
29 #include "wine/debug.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(enhmetafile);
32
33 /**********************************************************************
34  *           EMFDRV_MoveTo
35  */
36 BOOL
37 EMFDRV_MoveTo(PHYSDEV dev, INT x, INT y)
38 {
39     EMRMOVETOEX emr;
40
41     emr.emr.iType = EMR_MOVETOEX;
42     emr.emr.nSize = sizeof(emr);
43     emr.ptl.x = x;
44     emr.ptl.y = y;
45
46     return EMFDRV_WriteRecord( dev, &emr.emr );
47 }
48
49 /***********************************************************************
50  *           EMFDRV_LineTo
51  */
52 BOOL
53 EMFDRV_LineTo( PHYSDEV dev, INT x, INT y )
54 {
55     EMRLINETO emr;
56     RECTL bounds;
57     EMFDRV_PDEVICE *physDev = (EMFDRV_PDEVICE *)dev;
58     DC *dc = physDev->dc;
59
60     emr.emr.iType = EMR_LINETO;
61     emr.emr.nSize = sizeof(emr);
62     emr.ptl.x = x;
63     emr.ptl.y = y;
64
65     if(!EMFDRV_WriteRecord( dev, &emr.emr ))
66         return FALSE;
67
68     bounds.left   = min(x, dc->CursPosX);
69     bounds.top    = min(y, dc->CursPosY);
70     bounds.right  = max(x, dc->CursPosX);
71     bounds.bottom = max(y, dc->CursPosY);
72
73     EMFDRV_UpdateBBox( dev, &bounds );
74
75     return TRUE;
76 }
77
78
79 /***********************************************************************
80  *           EMFDRV_ArcChordPie
81  */
82 static BOOL
83 EMFDRV_ArcChordPie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
84                     INT xstart, INT ystart, INT xend, INT yend, DWORD iType )
85 {
86     INT temp, xCentre, yCentre, i;
87     double angleStart, angleEnd;
88     double xinterStart, yinterStart, xinterEnd, yinterEnd;
89     EMRARC emr;
90     RECTL bounds;
91     EMFDRV_PDEVICE *physDev = (EMFDRV_PDEVICE *)dev;
92     DC *dc = physDev->dc;
93
94     if(left == right || top == bottom) return FALSE;
95
96     if(left > right) {temp = left; left = right; right = temp;}
97     if(top > bottom) {temp = top; top = bottom; bottom = temp;}
98
99     if(dc->GraphicsMode == GM_COMPATIBLE) {
100         right--;
101         bottom--;
102     }
103
104     emr.emr.iType     = iType;
105     emr.emr.nSize     = sizeof(emr);
106     emr.rclBox.left   = left;
107     emr.rclBox.top    = top;
108     emr.rclBox.right  = right;
109     emr.rclBox.bottom = bottom;
110     emr.ptlStart.x    = xstart;
111     emr.ptlStart.y    = ystart;
112     emr.ptlEnd.x      = xend;
113     emr.ptlEnd.x      = yend;
114
115
116     /* Now calculate the BBox */
117     xCentre = (left + right + 1) / 2;
118     yCentre = (top + bottom + 1) / 2;
119
120     xstart -= xCentre;
121     ystart -= yCentre;
122     xend   -= xCentre;
123     yend   -= yCentre;
124
125     /* invert y co-ords to get angle anti-clockwise from x-axis */
126     angleStart = atan2( -(double)ystart, (double)xstart);
127     angleEnd   = atan2( -(double)yend, (double)xend);
128
129     /* These are the intercepts of the start/end lines with the arc */
130
131     xinterStart = (right - left + 1)/2 * cos(angleStart) + xCentre;
132     yinterStart = -(bottom - top + 1)/2 * sin(angleStart) + yCentre;
133     xinterEnd   = (right - left + 1)/2 * cos(angleEnd) + xCentre;
134     yinterEnd   = -(bottom - top + 1)/2 * sin(angleEnd) + yCentre;
135
136     if(angleStart < 0) angleStart += 2 * M_PI;
137     if(angleEnd < 0) angleEnd += 2 * M_PI;
138     if(angleEnd < angleStart) angleEnd += 2 * M_PI;
139
140     bounds.left   = min(xinterStart, xinterEnd);
141     bounds.top    = min(yinterStart, yinterEnd);
142     bounds.right  = max(xinterStart, xinterEnd);
143     bounds.bottom = max(yinterStart, yinterEnd);
144
145     for(i = 0; i <= 8; i++) {
146         if(i * M_PI / 2 < angleStart) /* loop until we're past start */
147             continue;
148         if(i * M_PI / 2 > angleEnd)   /* if we're past end we're finished */
149             break;
150
151         /* the arc touches the rectangle at the start of quadrant i, so adjust
152            BBox to reflect this. */
153
154         switch(i % 4) {
155         case 0:
156             bounds.right = right;
157             break;
158         case 1:
159             bounds.top = top;
160             break;
161         case 2:
162             bounds.left = left;
163             break;
164         case 3:
165             bounds.bottom = bottom;
166             break;
167         }
168     }
169
170     /* If we're drawing a pie then make sure we include the centre */
171     if(iType == EMR_PIE) {
172         if(bounds.left > xCentre) bounds.left = xCentre;
173         else if(bounds.right < xCentre) bounds.right = xCentre;
174         if(bounds.top > yCentre) bounds.top = yCentre;
175         else if(bounds.bottom < yCentre) bounds.right = yCentre;
176     }
177     if(!EMFDRV_WriteRecord( dev, &emr.emr ))
178         return FALSE;
179     EMFDRV_UpdateBBox( dev, &bounds );
180     return TRUE;
181 }
182
183
184 /***********************************************************************
185  *           EMFDRV_Arc
186  */
187 BOOL
188 EMFDRV_Arc( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
189             INT xstart, INT ystart, INT xend, INT yend )
190 {
191     return EMFDRV_ArcChordPie( dev, left, top, right, bottom, xstart, ystart,
192                                xend, yend, EMR_ARC );
193 }
194
195 /***********************************************************************
196  *           EMFDRV_Pie
197  */
198 BOOL
199 EMFDRV_Pie( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
200             INT xstart, INT ystart, INT xend, INT yend )
201 {
202     return EMFDRV_ArcChordPie( dev, left, top, right, bottom, xstart, ystart,
203                                xend, yend, EMR_PIE );
204 }
205
206
207 /***********************************************************************
208  *           EMFDRV_Chord
209  */
210 BOOL
211 EMFDRV_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom,
212              INT xstart, INT ystart, INT xend, INT yend )
213 {
214     return EMFDRV_ArcChordPie( dev, left, top, right, bottom, xstart, ystart,
215                                xend, yend, EMR_CHORD );
216 }
217
218 /***********************************************************************
219  *           EMFDRV_Ellipse
220  */
221 BOOL
222 EMFDRV_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom )
223 {
224     EMRELLIPSE emr;
225     INT temp;
226     EMFDRV_PDEVICE *physDev = (EMFDRV_PDEVICE *)dev;
227     DC *dc = physDev->dc;
228
229     TRACE("%d,%d - %d,%d\n", left, top, right, bottom);
230
231     if(left == right || top == bottom) return FALSE;
232
233     if(left > right) {temp = left; left = right; right = temp;}
234     if(top > bottom) {temp = top; top = bottom; bottom = temp;}
235
236     if(dc->GraphicsMode == GM_COMPATIBLE) {
237         right--;
238         bottom--;
239     }
240
241     emr.emr.iType     = EMR_ELLIPSE;
242     emr.emr.nSize     = sizeof(emr);
243     emr.rclBox.left   = left;
244     emr.rclBox.top    = top;
245     emr.rclBox.right  = right;
246     emr.rclBox.bottom = bottom;
247
248     EMFDRV_UpdateBBox( dev, &emr.rclBox );
249     return EMFDRV_WriteRecord( dev, &emr.emr );
250 }
251
252 /***********************************************************************
253  *           EMFDRV_Rectangle
254  */
255 BOOL
256 EMFDRV_Rectangle(PHYSDEV dev, INT left, INT top, INT right, INT bottom)
257 {
258     EMRRECTANGLE emr;
259     INT temp;
260     EMFDRV_PDEVICE *physDev = (EMFDRV_PDEVICE *)dev;
261     DC *dc = physDev->dc;
262
263     TRACE("%d,%d - %d,%d\n", left, top, right, bottom);
264
265     if(left == right || top == bottom) return FALSE;
266
267     if(left > right) {temp = left; left = right; right = temp;}
268     if(top > bottom) {temp = top; top = bottom; bottom = temp;}
269
270     if(dc->GraphicsMode == GM_COMPATIBLE) {
271         right--;
272         bottom--;
273     }
274
275     emr.emr.iType     = EMR_RECTANGLE;
276     emr.emr.nSize     = sizeof(emr);
277     emr.rclBox.left   = left;
278     emr.rclBox.top    = top;
279     emr.rclBox.right  = right;
280     emr.rclBox.bottom = bottom;
281
282     EMFDRV_UpdateBBox( dev, &emr.rclBox );
283     return EMFDRV_WriteRecord( dev, &emr.emr );
284 }
285
286 /***********************************************************************
287  *           EMFDRV_RoundRect
288  */
289 BOOL
290 EMFDRV_RoundRect( PHYSDEV dev, INT left, INT top, INT right,
291                   INT bottom, INT ell_width, INT ell_height )
292 {
293     EMRROUNDRECT emr;
294     INT temp;
295     EMFDRV_PDEVICE *physDev = (EMFDRV_PDEVICE *)dev;
296     DC *dc = physDev->dc;
297
298     if(left == right || top == bottom) return FALSE;
299
300     if(left > right) {temp = left; left = right; right = temp;}
301     if(top > bottom) {temp = top; top = bottom; bottom = temp;}
302
303     if(dc->GraphicsMode == GM_COMPATIBLE) {
304         right--;
305         bottom--;
306     }
307
308     emr.emr.iType     = EMR_ROUNDRECT;
309     emr.emr.nSize     = sizeof(emr);
310     emr.rclBox.left   = left;
311     emr.rclBox.top    = top;
312     emr.rclBox.right  = right;
313     emr.rclBox.bottom = bottom;
314     emr.szlCorner.cx  = ell_width;
315     emr.szlCorner.cy  = ell_height;
316
317     EMFDRV_UpdateBBox( dev, &emr.rclBox );
318     return EMFDRV_WriteRecord( dev, &emr.emr );
319 }
320
321 /***********************************************************************
322  *           EMFDRV_SetPixel
323  */
324 COLORREF
325 EMFDRV_SetPixel( PHYSDEV dev, INT x, INT y, COLORREF color )
326 {
327   return TRUE;
328 }
329
330
331 /**********************************************************************
332  *          EMFDRV_Polylinegon
333  *
334  * Helper for EMFDRV_Poly{line|gon}
335  */
336 static BOOL
337 EMFDRV_Polylinegon( PHYSDEV dev, const POINT* pt, INT count, DWORD iType )
338 {
339     EMRPOLYLINE *emr;
340     DWORD size;
341     INT i;
342     BOOL ret;
343
344     size = sizeof(EMRPOLYLINE) + sizeof(POINTL) * (count - 1);
345
346     emr = HeapAlloc( GetProcessHeap(), 0, size );
347     emr->emr.iType = iType;
348     emr->emr.nSize = size;
349
350     emr->rclBounds.left = emr->rclBounds.right = pt[0].x;
351     emr->rclBounds.top = emr->rclBounds.bottom = pt[0].y;
352
353     for(i = 1; i < count; i++) {
354         if(pt[i].x < emr->rclBounds.left)
355             emr->rclBounds.left = pt[i].x;
356         else if(pt[i].x > emr->rclBounds.right)
357             emr->rclBounds.right = pt[i].x;
358         if(pt[i].y < emr->rclBounds.top)
359             emr->rclBounds.top = pt[i].y;
360         else if(pt[i].y > emr->rclBounds.bottom)
361             emr->rclBounds.bottom = pt[i].y;
362     }
363
364     emr->cptl = count;
365     memcpy(emr->aptl, pt, count * sizeof(POINTL));
366
367     ret = EMFDRV_WriteRecord( dev, &emr->emr );
368     if(ret)
369         EMFDRV_UpdateBBox( dev, &emr->rclBounds );
370     HeapFree( GetProcessHeap(), 0, emr );
371     return ret;
372 }
373
374
375 /**********************************************************************
376  *          EMFDRV_Polyline
377  */
378 BOOL
379 EMFDRV_Polyline( PHYSDEV dev, const POINT* pt, INT count )
380 {
381     return EMFDRV_Polylinegon( dev, pt, count, EMR_POLYLINE );
382 }
383
384 /**********************************************************************
385  *          EMFDRV_Polygon
386  */
387 BOOL
388 EMFDRV_Polygon( PHYSDEV dev, const POINT* pt, INT count )
389 {
390     if(count < 2) return FALSE;
391     return EMFDRV_Polylinegon( dev, pt, count, EMR_POLYGON );
392 }
393
394
395 /**********************************************************************
396  *          EMFDRV_PolyPolylinegon
397  *
398  * Helper for EMFDRV_PolyPoly{line|gon}
399  */
400 static BOOL
401 EMFDRV_PolyPolylinegon( PHYSDEV dev, const POINT* pt, const INT* counts, UINT polys,
402                         DWORD iType)
403 {
404     EMRPOLYPOLYLINE *emr;
405     DWORD cptl = 0, poly, size, point;
406     RECTL bounds;
407     const POINT *pts;
408     BOOL ret;
409
410     bounds.left = bounds.right = pt[0].x;
411     bounds.top = bounds.bottom = pt[0].y;
412
413     pts = pt;
414     for(poly = 0; poly < polys; poly++) {
415         cptl += counts[poly];
416         for(point = 0; point < counts[poly]; point++) {
417             if(bounds.left > pts->x) bounds.left = pts->x;
418             else if(bounds.right < pts->x) bounds.right = pts->x;
419             if(bounds.top > pts->y) bounds.top = pts->y;
420             else if(bounds.bottom < pts->y) bounds.bottom = pts->y;
421             pts++;
422         }
423     }
424
425     size = sizeof(EMRPOLYPOLYLINE) + (polys - 1) * sizeof(DWORD) +
426       (cptl - 1) * sizeof(POINTL);
427
428     emr = HeapAlloc( GetProcessHeap(), 0, size );
429
430     emr->emr.iType = iType;
431     emr->emr.nSize = size;
432     emr->rclBounds = bounds;
433     emr->nPolys = polys;
434     emr->cptl = cptl;
435     memcpy(emr->aPolyCounts, counts, polys * sizeof(DWORD));
436     memcpy(emr->aPolyCounts + polys, pt, cptl * sizeof(POINTL));
437     ret = EMFDRV_WriteRecord( dev, &emr->emr );
438     if(ret)
439         EMFDRV_UpdateBBox( dev, &emr->rclBounds );
440     HeapFree( GetProcessHeap(), 0, emr );
441     return ret;
442 }
443
444 /**********************************************************************
445  *          EMFDRV_PolyPolyline
446  */
447 BOOL
448 EMFDRV_PolyPolyline(PHYSDEV dev, const POINT* pt, const DWORD* counts, DWORD polys)
449 {
450     return EMFDRV_PolyPolylinegon( dev, pt, (INT *)counts, polys,
451                                    EMR_POLYPOLYLINE );
452 }
453
454 /**********************************************************************
455  *          EMFDRV_PolyPolygon
456  */
457 BOOL
458 EMFDRV_PolyPolygon( PHYSDEV dev, const POINT* pt, const INT* counts, UINT polys )
459 {
460     return EMFDRV_PolyPolylinegon( dev, pt, counts, polys, EMR_POLYPOLYGON );
461 }
462
463
464 /**********************************************************************
465  *          EMFDRV_ExtFloodFill
466  */
467 BOOL
468 EMFDRV_ExtFloodFill( PHYSDEV dev, INT x, INT y, COLORREF color, UINT fillType )
469 {
470     EMREXTFLOODFILL emr;
471
472     emr.emr.iType = EMR_EXTFLOODFILL;
473     emr.emr.nSize = sizeof(emr);
474     emr.ptlStart.x = x;
475     emr.ptlStart.y = y;
476     emr.crColor = color;
477     emr.iMode = fillType;
478
479     return EMFDRV_WriteRecord( dev, &emr.emr );
480 }
481
482
483 /*********************************************************************
484  *          EMFDRV_FillRgn
485  */
486 BOOL EMFDRV_FillRgn( PHYSDEV dev, HRGN hrgn, HBRUSH hbrush )
487 {
488     EMRFILLRGN *emr;
489     DWORD size, rgnsize, index;
490     BOOL ret;
491
492     index = EMFDRV_CreateBrushIndirect( dev, hbrush );
493     if(!index) return FALSE;
494
495     rgnsize = GetRegionData( hrgn, 0, NULL );
496     size = rgnsize + offsetof(EMRFILLRGN,RgnData);
497     emr = HeapAlloc( GetProcessHeap(), 0, size );
498
499     GetRegionData( hrgn, rgnsize, (RGNDATA *)&emr->RgnData );
500
501     emr->emr.iType = EMR_FILLRGN;
502     emr->emr.nSize = size;
503     emr->rclBounds.left   = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.left;
504     emr->rclBounds.top    = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.top;
505     emr->rclBounds.right  = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.right - 1;
506     emr->rclBounds.bottom = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.bottom - 1;
507     emr->cbRgnData = rgnsize;
508     emr->ihBrush = index;
509
510     ret = EMFDRV_WriteRecord( dev, &emr->emr );
511     if(ret)
512         EMFDRV_UpdateBBox( dev, &emr->rclBounds );
513     HeapFree( GetProcessHeap(), 0, emr );
514     return ret;
515 }
516 /*********************************************************************
517  *          EMFDRV_FrameRgn
518  */
519 BOOL EMFDRV_FrameRgn( PHYSDEV dev, HRGN hrgn, HBRUSH hbrush, INT width, INT height )
520 {
521     EMRFRAMERGN *emr;
522     DWORD size, rgnsize, index;
523     BOOL ret;
524
525     index = EMFDRV_CreateBrushIndirect( dev, hbrush );
526     if(!index) return FALSE;
527
528     rgnsize = GetRegionData( hrgn, 0, NULL );
529     size = rgnsize + offsetof(EMRFRAMERGN,RgnData);
530     emr = HeapAlloc( GetProcessHeap(), 0, size );
531
532     GetRegionData( hrgn, rgnsize, (RGNDATA *)&emr->RgnData );
533
534     emr->emr.iType = EMR_FRAMERGN;
535     emr->emr.nSize = size;
536     emr->rclBounds.left   = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.left;
537     emr->rclBounds.top    = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.top;
538     emr->rclBounds.right  = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.right - 1;
539     emr->rclBounds.bottom = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.bottom - 1;
540     emr->cbRgnData = rgnsize;
541     emr->ihBrush = index;
542     emr->szlStroke.cx = width;
543     emr->szlStroke.cy = height;
544
545     ret = EMFDRV_WriteRecord( dev, &emr->emr );
546     if(ret)
547         EMFDRV_UpdateBBox( dev, &emr->rclBounds );
548     HeapFree( GetProcessHeap(), 0, emr );
549     return ret;
550 }
551
552 /*********************************************************************
553  *          EMFDRV_PaintInvertRgn
554  *
555  * Helper for EMFDRV_{Paint|Invert}Rgn
556  */
557 static BOOL EMFDRV_PaintInvertRgn( PHYSDEV dev, HRGN hrgn, DWORD iType )
558 {
559     EMRINVERTRGN *emr;
560     DWORD size, rgnsize;
561     BOOL ret;
562
563
564     rgnsize = GetRegionData( hrgn, 0, NULL );
565     size = rgnsize + offsetof(EMRINVERTRGN,RgnData);
566     emr = HeapAlloc( GetProcessHeap(), 0, size );
567
568     GetRegionData( hrgn, rgnsize, (RGNDATA *)&emr->RgnData );
569
570     emr->emr.iType = iType;
571     emr->emr.nSize = size;
572     emr->rclBounds.left   = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.left;
573     emr->rclBounds.top    = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.top;
574     emr->rclBounds.right  = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.right - 1;
575     emr->rclBounds.bottom = ((RGNDATA *)&emr->RgnData)->rdh.rcBound.bottom - 1;
576     emr->cbRgnData = rgnsize;
577
578     ret = EMFDRV_WriteRecord( dev, &emr->emr );
579     if(ret)
580         EMFDRV_UpdateBBox( dev, &emr->rclBounds );
581     HeapFree( GetProcessHeap(), 0, emr );
582     return ret;
583 }
584
585 /**********************************************************************
586  *          EMFDRV_PaintRgn
587  */
588 BOOL
589 EMFDRV_PaintRgn( PHYSDEV dev, HRGN hrgn )
590 {
591     return EMFDRV_PaintInvertRgn( dev, hrgn, EMR_PAINTRGN );
592 }
593
594 /**********************************************************************
595  *          EMFDRV_InvertRgn
596  */
597 BOOL
598 EMFDRV_InvertRgn( PHYSDEV dev, HRGN hrgn )
599 {
600     return EMFDRV_PaintInvertRgn( dev, hrgn, EMR_INVERTRGN );
601 }
602
603 /**********************************************************************
604  *          EMFDRV_SetBkColor
605  */
606 COLORREF
607 EMFDRV_SetBkColor( PHYSDEV dev, COLORREF color )
608 {
609     EMRSETBKCOLOR emr;
610
611     emr.emr.iType = EMR_SETBKCOLOR;
612     emr.emr.nSize = sizeof(emr);
613     emr.crColor = color;
614
615     return EMFDRV_WriteRecord( dev, &emr.emr ) ? color : CLR_INVALID;
616 }
617
618
619 /**********************************************************************
620  *          EMFDRV_SetTextColor
621  */
622 COLORREF
623 EMFDRV_SetTextColor( PHYSDEV dev, COLORREF color )
624 {
625     EMRSETTEXTCOLOR emr;
626
627     emr.emr.iType = EMR_SETTEXTCOLOR;
628     emr.emr.nSize = sizeof(emr);
629     emr.crColor = color;
630
631     return EMFDRV_WriteRecord( dev, &emr.emr ) ? color : CLR_INVALID;
632 }
633
634 /**********************************************************************
635  *          EMFDRV_ExtTextOut
636  */
637 BOOL EMFDRV_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags,
638                         const RECT *lprect, LPCWSTR str, UINT count,
639                         const INT *lpDx )
640 {
641     EMREXTTEXTOUTW *pemr;
642     DWORD nSize;
643     BOOL ret;
644     EMFDRV_PDEVICE *physDev = (EMFDRV_PDEVICE*) dev;
645
646     nSize = sizeof(*pemr) + (count+1)/2 * 2 * sizeof(WCHAR) +
647       (lpDx ? count * sizeof(INT) : 0);
648
649     TRACE("%s count %d nSize = %ld\n", debugstr_wn(str, count), count, nSize);
650     pemr = HeapAlloc(GetProcessHeap(), 0, nSize);
651
652     pemr->emr.iType = EMR_EXTTEXTOUTW;
653     pemr->emr.nSize = nSize;
654
655     /* FIXME: Calculation of these requires honouring alignment mode, escapement etc. */
656     pemr->rclBounds.left = pemr->rclBounds.right = x;
657     pemr->rclBounds.top = pemr->rclBounds.bottom = y;
658
659     pemr->iGraphicsMode = GetGraphicsMode(physDev->hdc);
660     pemr->exScale = pemr->eyScale = 1.0; /* FIXME */
661
662     pemr->emrtext.ptlReference.x = x;
663     pemr->emrtext.ptlReference.y = y;
664     pemr->emrtext.nChars = count;
665     pemr->emrtext.offString = sizeof(*pemr);
666     memcpy((char*)pemr + pemr->emrtext.offString, str, count * sizeof(WCHAR));
667     pemr->emrtext.fOptions = flags;
668     if(!lprect) {
669         pemr->emrtext.rcl.left = pemr->emrtext.rcl.top = 0;
670         pemr->emrtext.rcl.right = pemr->emrtext.rcl.bottom = -1;
671     } else {
672         pemr->emrtext.rcl.left = lprect->left;
673         pemr->emrtext.rcl.top = lprect->top;
674         pemr->emrtext.rcl.right = lprect->right;
675         pemr->emrtext.rcl.bottom = lprect->bottom;
676     }
677
678     if(lpDx) {
679         pemr->emrtext.offDx = sizeof(*pemr) + (count+1)/2 * 2 * sizeof(WCHAR);
680         memcpy((char*)pemr + pemr->emrtext.offDx, lpDx, count * sizeof(INT));
681     } else
682         pemr->emrtext.offDx = 0; /* FIXME: actually Windows fills out the array
683                                      using GetCharWidth in this case */
684
685
686     ret = EMFDRV_WriteRecord( dev, &pemr->emr );
687     if(ret)
688         EMFDRV_UpdateBBox( dev, &pemr->rclBounds );
689     HeapFree( GetProcessHeap(), 0, pemr );
690     return ret;
691 }