metafile.c 44.6 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3 4
/*
 * Metafile functions
 *
 * Copyright  David W. Metcalfe, 1994
5 6 7
 * Copyright  Niels de Carpentier, 1996
 * Copyright  Albrecht Kleine, 1996
 * Copyright  Huw Davies, 1996
Alexandre Julliard's avatar
Alexandre Julliard committed
8
 *
9 10 11 12 13 14 15 16 17 18 19 20
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
21
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 23 24
 *
 * NOTES
 *
25 26 27 28
 * These functions are primarily involved with metafile playback or anything
 * that touches a HMETAFILE.
 * For recording of metafiles look in graphics/metafiledrv/
 *
29
 * Note that (32 bit) HMETAFILEs are GDI objects, while HMETAFILE16s are
30 31 32 33
 * global memory handles so these cannot be interchanged.
 *
 * Memory-based metafiles are just stored as a continuous block of memory with
 * a METAHEADER at the head with METARECORDs appended to it.  mtType is
Austin English's avatar
Austin English committed
34
 * METAFILE_MEMORY (1).  Note this is identical to the disk image of a
35 36 37 38 39 40 41 42 43 44 45
 * disk-based metafile - even mtType is METAFILE_MEMORY.
 * 16bit HMETAFILE16s are global handles to this block
 * 32bit HMETAFILEs are GDI handles METAFILEOBJs, which contains a ptr to
 * the memory.
 * Disk-based metafiles are rather different. HMETAFILE16s point to a
 * METAHEADER which has mtType equal to METAFILE_DISK (2).  Following the 9
 * WORDs of the METAHEADER there are a further 3 WORDs of 0, 1 of 0x117, 1
 * more 0, then 2 which may be a time stamp of the file and then the path of
 * the file (METAHEADERDISK). I've copied this for 16bit compatibility.
 *
 * HDMD - 14/4/1999
46
 */
47

48
#include "config.h"
49

50
#include <stdarg.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
51
#include <string.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
52
#include <fcntl.h>
53

54 55 56
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
57
#include "winreg.h"
58
#include "winnls.h"
59
#include "winternl.h"
60
#include "gdi_private.h"
61
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
62

63
WINE_DEFAULT_DEBUG_CHANNEL(metafile);
64

65 66 67 68 69 70 71 72 73
#include "pshpack1.h"
typedef struct
{
    DWORD dw1, dw2, dw3;
    WORD w4;
    CHAR filename[0x100];
} METAHEADERDISK;
#include "poppack.h"

74 75 76 77 78 79
typedef struct
{
    GDIOBJHDR   header;
    METAHEADER  *mh;
} METAFILEOBJ;

80

Alexandre Julliard's avatar
Alexandre Julliard committed
81 82 83 84 85
/******************************************************************
 *         MF_AddHandle
 *
 *    Add a handle to an external handle table and return the index
 */
86
static int MF_AddHandle(HANDLETABLE *ht, UINT htlen, HGDIOBJ hobj)
Alexandre Julliard's avatar
Alexandre Julliard committed
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
{
    int i;

    for (i = 0; i < htlen; i++)
    {
	if (*(ht->objectHandle + i) == 0)
	{
	    *(ht->objectHandle + i) = hobj;
	    return i;
	}
    }
    return -1;
}


/******************************************************************
103 104 105 106 107 108 109 110
 *         MF_Create_HMETATFILE
 *
 * Creates a (32 bit) HMETAFILE object from a METAHEADER
 *
 * HMETAFILEs are GDI objects.
 */
HMETAFILE MF_Create_HMETAFILE(METAHEADER *mh)
{
111 112 113 114 115 116 117
    HMETAFILE hmf;
    METAFILEOBJ *metaObj;

    if (!(metaObj = HeapAlloc( GetProcessHeap(), 0, sizeof(*metaObj) ))) return 0;
    metaObj->mh = mh;
    if (!(hmf = alloc_gdi_handle( &metaObj->header, OBJ_METAFILE, NULL )))
        HeapFree( GetProcessHeap(), 0, metaObj );
118 119 120 121 122 123 124 125 126 127
    return hmf;
}

/******************************************************************
 *         MF_GetMetaHeader
 *
 * Returns ptr to METAHEADER associated with HMETAFILE
 */
static METAHEADER *MF_GetMetaHeader( HMETAFILE hmf )
{
128
    METAHEADER *ret = NULL;
129
    METAFILEOBJ * metaObj = GDI_GetObjPtr( hmf, OBJ_METAFILE );
130 131 132 133 134 135
    if (metaObj)
    {
        ret = metaObj->mh;
        GDI_ReleaseObj( hmf );
    }
    return ret;
136 137
}

138 139 140
/******************************************************************
 *         convert_points
 *
141
 * Convert an array of POINTS to an array of POINT.
142 143
 * Result must be freed by caller.
 */
144
static POINT *convert_points( UINT count, const POINTS *pts )
145 146 147 148 149 150 151
{
    UINT i;
    POINT *ret = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ret) );
    if (ret)
    {
        for (i = 0; i < count; i++)
        {
152 153
            ret[i].x = pts[i].x;
            ret[i].y = pts[i].y;
154 155 156 157 158
        }
    }
    return ret;
}

159
/******************************************************************
160
 *          DeleteMetaFile  (GDI32.@)
161 162 163 164 165 166
 *
 *  Delete a memory-based metafile.
 */

BOOL WINAPI DeleteMetaFile( HMETAFILE hmf )
{
167
    METAFILEOBJ * metaObj = free_gdi_handle( hmf );
168 169
    if (!metaObj) return FALSE;
    HeapFree( GetProcessHeap(), 0, metaObj->mh );
170
    return HeapFree( GetProcessHeap(), 0, metaObj );
171 172 173 174 175 176 177 178
}

/******************************************************************
 *         MF_ReadMetaFile
 *
 * Returns a pointer to a memory based METAHEADER read in from file HFILE
 *
 */
179
static METAHEADER *MF_ReadMetaFile(HANDLE hfile)
180 181 182 183 184
{
    METAHEADER *mh;
    DWORD BytesRead, size;

    size = sizeof(METAHEADER);
185
    mh = HeapAlloc( GetProcessHeap(), 0, size );
186 187 188
    if(!mh) return NULL;
    if(ReadFile( hfile, mh, size, &BytesRead, NULL) == 0 ||
       BytesRead != size) {
189
        HeapFree( GetProcessHeap(), 0, mh );
190 191
	return NULL;
    }
192 193 194
    if (mh->mtType != METAFILE_MEMORY || mh->mtVersion != MFVERSION ||
        mh->mtHeaderSize != size / 2)
    {
195 196 197
        HeapFree( GetProcessHeap(), 0, mh );
        return NULL;
    }
198
    size = mh->mtSize * 2;
199
    mh = HeapReAlloc( GetProcessHeap(), 0, mh, size );
200 201 202 203 204
    if(!mh) return NULL;
    size -= sizeof(METAHEADER);
    if(ReadFile( hfile, (char *)mh + sizeof(METAHEADER), size, &BytesRead,
		 NULL) == 0 ||
       BytesRead != size) {
205
        HeapFree( GetProcessHeap(), 0, mh );
206 207 208 209
	return NULL;
    }

    if (mh->mtType != METAFILE_MEMORY) {
210
        WARN("Disk metafile had mtType = %04x\n", mh->mtType);
211 212 213 214 215
	mh->mtType = METAFILE_MEMORY;
    }
    return mh;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
216
/******************************************************************
217
 *         GetMetaFileA   (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
218
 *
219
 *  Read a metafile from a file. Returns handle to a memory-based metafile.
Alexandre Julliard's avatar
Alexandre Julliard committed
220
 */
221
HMETAFILE WINAPI GetMetaFileA( LPCSTR lpFilename )
Alexandre Julliard's avatar
Alexandre Julliard committed
222
{
223
    METAHEADER *mh;
224
    HANDLE hFile;
225

226
    TRACE("%s\n", lpFilename);
227 228 229

    if(!lpFilename)
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
230

231
    if((hFile = CreateFileA(lpFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
232
			    OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE)
233 234 235 236 237 238
        return 0;

    mh = MF_ReadMetaFile(hFile);
    CloseHandle(hFile);
    if(!mh) return 0;
    return MF_Create_HMETAFILE( mh );
Alexandre Julliard's avatar
Alexandre Julliard committed
239 240
}

Alexandre Julliard's avatar
Alexandre Julliard committed
241
/******************************************************************
242
 *         GetMetaFileW   (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
243
 */
244
HMETAFILE WINAPI GetMetaFileW( LPCWSTR lpFilename )
Alexandre Julliard's avatar
Alexandre Julliard committed
245
{
246
    METAHEADER *mh;
247
    HANDLE hFile;
248

249
    TRACE("%s\n", debugstr_w(lpFilename));
250 251 252 253

    if(!lpFilename)
        return 0;

254
    if((hFile = CreateFileW(lpFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
255
			    OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE)
256 257 258 259 260 261
        return 0;

    mh = MF_ReadMetaFile(hFile);
    CloseHandle(hFile);
    if(!mh) return 0;
    return MF_Create_HMETAFILE( mh );
Alexandre Julliard's avatar
Alexandre Julliard committed
262 263 264 265
}


/******************************************************************
266 267 268
 *         MF_LoadDiskBasedMetaFile
 *
 * Creates a new memory-based metafile from a disk-based one.
Alexandre Julliard's avatar
Alexandre Julliard committed
269
 */
270
static METAHEADER *MF_LoadDiskBasedMetaFile(METAHEADER *mh)
271 272
{
    METAHEADERDISK *mhd;
273
    HANDLE hfile;
274
    METAHEADER *mh2;
Alexandre Julliard's avatar
Alexandre Julliard committed
275

276
    if(mh->mtType != METAFILE_DISK) {
277
        ERR("Not a disk based metafile\n");
278 279 280 281
	return NULL;
    }
    mhd = (METAHEADERDISK *)((char *)mh + sizeof(METAHEADER));

282
    if((hfile = CreateFileA(mhd->filename, GENERIC_READ, FILE_SHARE_READ, NULL,
283
			    OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
284
        WARN("Can't open file of disk based metafile\n");
285 286 287 288 289 290 291 292 293 294 295
        return NULL;
    }
    mh2 = MF_ReadMetaFile(hfile);
    CloseHandle(hfile);
    return mh2;
}

/******************************************************************
 *         MF_CreateMetaHeaderDisk
 *
 * Take a memory based METAHEADER and change it to a disk based METAHEADER
296
 * associated with filename.  Note: Trashes contents of old one.
297
 */
298
METAHEADER *MF_CreateMetaHeaderDisk(METAHEADER *mh, LPCVOID filename, BOOL uni )
Alexandre Julliard's avatar
Alexandre Julliard committed
299
{
300 301
    METAHEADERDISK *mhd;

302
    mh = HeapReAlloc( GetProcessHeap(), 0, mh,
303 304 305
		      sizeof(METAHEADER) + sizeof(METAHEADERDISK));
    mh->mtType = METAFILE_DISK;
    mhd = (METAHEADERDISK *)((char *)mh + sizeof(METAHEADER));
306 307 308 309 310 311

    if( uni )
        WideCharToMultiByte(CP_ACP, 0, filename, -1, 
                   mhd->filename, sizeof mhd->filename, NULL, NULL);
    else
        lstrcpynA( mhd->filename, filename, sizeof mhd->filename );
312 313 314
    return mh;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
315
/******************************************************************
316
 *         CopyMetaFileW   (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
317 318 319 320 321
 *
 *  Copies the metafile corresponding to hSrcMetaFile to either
 *  a disk file, if a filename is given, or to a new memory based
 *  metafile, if lpFileName is NULL.
 *
322 323 324
 * PARAMS
 *  hSrcMetaFile [I] handle of metafile to copy
 *  lpFilename   [I] filename if copying to a file
Alexandre Julliard's avatar
Alexandre Julliard committed
325
 *
326
 * RETURNS
Alexandre Julliard's avatar
Alexandre Julliard committed
327 328 329 330
 *  Handle to metafile copy on success, NULL on failure.
 *
 * BUGS
 *  Copying to disk returns NULL even if successful.
Alexandre Julliard's avatar
Alexandre Julliard committed
331
 */
332
HMETAFILE WINAPI CopyMetaFileW( HMETAFILE hSrcMetaFile, LPCWSTR lpFilename )
Jon Griffiths's avatar
Jon Griffiths committed
333
{
334 335
    METAHEADER *mh = MF_GetMetaHeader( hSrcMetaFile );
    METAHEADER *mh2 = NULL;
336
    HANDLE hFile;
337

338
    TRACE("(%p,%s)\n", hSrcMetaFile, debugstr_w(lpFilename));
339

340
    if(!mh) return 0;
341

342 343 344
    if(mh->mtType == METAFILE_DISK)
        mh2 = MF_LoadDiskBasedMetaFile(mh);
    else {
345
        mh2 = HeapAlloc( GetProcessHeap(), 0, mh->mtSize * 2 );
346 347 348 349
        memcpy( mh2, mh, mh->mtSize * 2 );
    }

    if(lpFilename) {         /* disk based metafile */
350
        DWORD w;
351
        if((hFile = CreateFileW(lpFilename, GENERIC_WRITE, 0, NULL,
352
				CREATE_ALWAYS, 0, 0)) == INVALID_HANDLE_VALUE) {
353
	    HeapFree( GetProcessHeap(), 0, mh2 );
Alexandre Julliard's avatar
Alexandre Julliard committed
354
	    return 0;
355
	}
356
	WriteFile(hFile, mh2, mh2->mtSize * 2, &w, NULL);
357 358
	CloseHandle(hFile);
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
359

360
    return MF_Create_HMETAFILE( mh2 );
Alexandre Julliard's avatar
Alexandre Julliard committed
361 362
}

Alexandre Julliard's avatar
Alexandre Julliard committed
363 364

/******************************************************************
365
 *         CopyMetaFileA   (GDI32.@)
366 367
 *
 * See CopyMetaFileW.
Alexandre Julliard's avatar
Alexandre Julliard committed
368
 */
369
HMETAFILE WINAPI CopyMetaFileA( HMETAFILE hSrcMetaFile, LPCSTR lpFilename )
Alexandre Julliard's avatar
Alexandre Julliard committed
370
{
371
    UNICODE_STRING lpFilenameW;
372 373
    HMETAFILE ret = 0;

374 375 376
    if (lpFilename) RtlCreateUnicodeStringFromAsciiz(&lpFilenameW, lpFilename);
    else lpFilenameW.Buffer = NULL;

377 378 379
    ret = CopyMetaFileW( hSrcMetaFile, lpFilenameW.Buffer );
    if (lpFilenameW.Buffer)
        RtlFreeUnicodeString(&lpFilenameW);
Alexandre Julliard's avatar
Alexandre Julliard committed
380 381 382
    return ret;
}

383 384
/*******************************************************************
 *         MF_PlayMetaFile
Alexandre Julliard's avatar
Alexandre Julliard committed
385
 *
386
 * Helper for PlayMetaFile
Alexandre Julliard's avatar
Alexandre Julliard committed
387
 */
388
static BOOL MF_PlayMetaFile( HDC hdc, METAHEADER *mh)
Alexandre Julliard's avatar
Alexandre Julliard committed
389 390
{

Alexandre Julliard's avatar
Alexandre Julliard committed
391
    METARECORD *mr;
392
    HANDLETABLE *ht;
393
    unsigned int offset = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
394
    WORD i;
395 396 397
    HPEN hPen;
    HBRUSH hBrush;
    HFONT hFont;
398 399
    HPALETTE hPal;
    HRGN hRgn;
400 401
    BOOL loaded = FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
402
    if (!mh) return FALSE;
403
    if(mh->mtType == METAFILE_DISK) { /* Create a memory-based copy */
404 405 406 407
        mh = MF_LoadDiskBasedMetaFile(mh);
	if(!mh) return FALSE;
	loaded = TRUE;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
408

409
    /* save DC */
410 411 412
    hPen = GetCurrentObject(hdc, OBJ_PEN);
    hBrush = GetCurrentObject(hdc, OBJ_BRUSH);
    hFont = GetCurrentObject(hdc, OBJ_FONT);
413 414 415 416 417 418 419 420
    hPal = GetCurrentObject(hdc, OBJ_PAL);

    hRgn = CreateRectRgn(0, 0, 0, 0);
    if (!GetClipRgn(hdc, hRgn))
    {
        DeleteObject(hRgn);
        hRgn = 0;
    }
421

Alexandre Julliard's avatar
Alexandre Julliard committed
422
    /* create the handle table */
423
    ht = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
424
		    sizeof(HANDLETABLE) * mh->mtNoObjects);
425
    if(!ht) return FALSE;
426

Alexandre Julliard's avatar
Alexandre Julliard committed
427
    /* loop through metafile playing records */
Alexandre Julliard's avatar
Alexandre Julliard committed
428 429
    offset = mh->mtHeaderSize * 2;
    while (offset < mh->mtSize * 2)
Alexandre Julliard's avatar
Alexandre Julliard committed
430
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
431
        mr = (METARECORD *)((char *)mh + offset);
432
	TRACE("offset=%04x,size=%08x\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
433
            offset, mr->rdSize);
434
	if (mr->rdSize < 3) { /* catch illegal record sizes */
435
            TRACE("Entry got size %d at offset %d, total mf length is %d\n",
436 437
                  mr->rdSize,offset,mh->mtSize*2);
            break;
Alexandre Julliard's avatar
Alexandre Julliard committed
438
	}
439

Alexandre Julliard's avatar
Alexandre Julliard committed
440
	offset += mr->rdSize * 2;
441 442 443 444
	if (mr->rdFunction == META_EOF) {
	    TRACE("Got META_EOF so stopping\n");
	    break;
	}
445
	PlayMetaFileRecord( hdc, ht, mr, mh->mtNoObjects );
Alexandre Julliard's avatar
Alexandre Julliard committed
446 447
    }

448
    /* restore DC */
449
    SelectObject(hdc, hPen);
450 451 452 453
    SelectObject(hdc, hBrush);
    SelectPalette(hdc, hPal, FALSE);
    ExtSelectClipRgn(hdc, hRgn, RGN_COPY);
    DeleteObject(hRgn);
Alexandre Julliard's avatar
Alexandre Julliard committed
454

Alexandre Julliard's avatar
Alexandre Julliard committed
455 456 457
    /* free objects in handle table */
    for(i = 0; i < mh->mtNoObjects; i++)
      if(*(ht->objectHandle + i) != 0)
458
        DeleteObject(*(ht->objectHandle + i));
459

Alexandre Julliard's avatar
Alexandre Julliard committed
460
    /* free handle table */
461
    HeapFree( GetProcessHeap(), 0, ht );
462
    if(loaded)
463
        HeapFree( GetProcessHeap(), 0, mh );
Alexandre Julliard's avatar
Alexandre Julliard committed
464 465 466
    return TRUE;
}

467
/******************************************************************
468
 *         PlayMetaFile   (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
469
 *
470 471
 *  Renders the metafile specified by hmf in the DC specified by
 *  hdc. Returns FALSE on failure, TRUE on success.
472 473 474 475
 *
 * PARAMS
 *  hdc [I] handle of DC to render in
 *  hmf [I] handle of metafile to render
476 477 478 479
 *
 * RETURNS
 *  Success: TRUE
 *  Failure: FALSE
Alexandre Julliard's avatar
Alexandre Julliard committed
480
 */
481
BOOL WINAPI PlayMetaFile( HDC hdc, HMETAFILE hmf )
Alexandre Julliard's avatar
Alexandre Julliard committed
482
{
483
    METAHEADER *mh = MF_GetMetaHeader( hmf );
484
    return MF_PlayMetaFile( hdc, mh );
485 486 487
}

/******************************************************************
488
 *            EnumMetaFile   (GDI32.@)
489 490 491 492
 *
 *  Loop through the metafile records in hmf, calling the user-specified
 *  function for each one, stopping when the user's function returns FALSE
 *  (which is considered to be failure)
493
 *  or when no records are left (which is considered to be success).
494 495 496 497
 *
 * RETURNS
 *  TRUE on success, FALSE on failure.
 */
498
BOOL WINAPI EnumMetaFile(HDC hdc, HMETAFILE hmf, MFENUMPROC lpEnumFunc, LPARAM lpData)
Jon Griffiths's avatar
Jon Griffiths committed
499
{
Alexandre Julliard's avatar
Alexandre Julliard committed
500
    METAHEADER *mhTemp = NULL, *mh = MF_GetMetaHeader(hmf);
Alexandre Julliard's avatar
Alexandre Julliard committed
501
    METARECORD *mr;
502
    HANDLETABLE *ht;
Alexandre Julliard's avatar
Alexandre Julliard committed
503
    BOOL result = TRUE;
504 505
    int i;
    unsigned int offset = 0;
506 507 508
    HPEN hPen;
    HBRUSH hBrush;
    HFONT hFont;
Alexandre Julliard's avatar
Alexandre Julliard committed
509

510
    TRACE("(%p,%p,%p,%p)\n", hdc, hmf, lpEnumFunc, (void*)lpData);
Alexandre Julliard's avatar
Alexandre Julliard committed
511
    if (!mh) return 0;
512
    if(mh->mtType == METAFILE_DISK)
Alexandre Julliard's avatar
Alexandre Julliard committed
513
    {
514 515
        /* Create a memory-based copy */
        if (!(mhTemp = MF_LoadDiskBasedMetaFile(mh))) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
516 517
	mh = mhTemp;
    }
518

Alexandre Julliard's avatar
Alexandre Julliard committed
519
    /* save the current pen, brush and font */
520 521 522
    hPen = GetCurrentObject(hdc, OBJ_PEN);
    hBrush = GetCurrentObject(hdc, OBJ_BRUSH);
    hFont = GetCurrentObject(hdc, OBJ_FONT);
Alexandre Julliard's avatar
Alexandre Julliard committed
523

524
    ht = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
525
			    sizeof(HANDLETABLE) * mh->mtNoObjects);
526

Alexandre Julliard's avatar
Alexandre Julliard committed
527 528
    /* loop through metafile records */
    offset = mh->mtHeaderSize * 2;
529

Alexandre Julliard's avatar
Alexandre Julliard committed
530 531 532
    while (offset < (mh->mtSize * 2))
    {
	mr = (METARECORD *)((char *)mh + offset);
533 534 535 536
	if(mr->rdFunction == META_EOF) {
	    TRACE("Got META_EOF so stopping\n");
	    break;
	}
537
	TRACE("Calling EnumFunc with record type %x\n",
538
	      mr->rdFunction);
Alexandre Julliard's avatar
Alexandre Julliard committed
539 540 541 542 543
        if (!lpEnumFunc( hdc, ht, mr, mh->mtNoObjects, (LONG)lpData ))
	{
	    result = FALSE;
	    break;
	}
544

Alexandre Julliard's avatar
Alexandre Julliard committed
545 546 547 548
	offset += (mr->rdSize * 2);
    }

    /* restore pen, brush and font */
549 550 551
    SelectObject(hdc, hBrush);
    SelectObject(hdc, hPen);
    SelectObject(hdc, hFont);
Alexandre Julliard's avatar
Alexandre Julliard committed
552 553 554 555

    /* free objects in handle table */
    for(i = 0; i < mh->mtNoObjects; i++)
      if(*(ht->objectHandle + i) != 0)
556
        DeleteObject(*(ht->objectHandle + i));
Alexandre Julliard's avatar
Alexandre Julliard committed
557 558

    /* free handle table */
559
    HeapFree( GetProcessHeap(), 0, ht);
Alexandre Julliard's avatar
Alexandre Julliard committed
560
    /* free a copy of metafile */
561
    HeapFree( GetProcessHeap(), 0, mhTemp );
Alexandre Julliard's avatar
Alexandre Julliard committed
562 563 564
    return result;
}

565
static BOOL MF_Play_MetaCreateRegion( METARECORD *mr, HRGN hrgn );
566
static BOOL MF_Play_MetaExtTextOut(HDC hdc, METARECORD *mr);
Alexandre Julliard's avatar
Alexandre Julliard committed
567
/******************************************************************
568
 *         PlayMetaFileRecord   (GDI32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
569 570
 *
 *   Render a single metafile record specified by *mr in the DC hdc, while
571
 *   using the handle table *ht, of length handles,
Alexandre Julliard's avatar
Alexandre Julliard committed
572 573 574 575 576
 *   to store metafile objects.
 *
 * BUGS
 *  The following metafile records are unimplemented:
 *
577
 *  DRAWTEXT, ANIMATEPALETTE, SETPALENTRIES,
Alexandre Julliard's avatar
Alexandre Julliard committed
578 579
 *  RESIZEPALETTE, EXTFLOODFILL, RESETDC, STARTDOC, STARTPAGE, ENDPAGE,
 *  ABORTDOC, ENDDOC, CREATEBRUSH, CREATEBITMAPINDIRECT, and CREATEBITMAP.
Alexandre Julliard's avatar
Alexandre Julliard committed
580
 */
581 582
BOOL WINAPI PlayMetaFileRecord( HDC hdc,  HANDLETABLE *ht, METARECORD *mr, UINT handles )
{
Alexandre Julliard's avatar
Alexandre Julliard committed
583
    short s1;
584
    POINT *pt;
Alexandre Julliard's avatar
Alexandre Julliard committed
585 586
    BITMAPINFOHEADER *infohdr;

587
    TRACE("(%p %p %p %u) function %04x\n", hdc, ht, mr, handles, mr->rdFunction);
588

Alexandre Julliard's avatar
Alexandre Julliard committed
589 590
    switch (mr->rdFunction)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
591
    case META_EOF:
592
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
593 594

    case META_DELETEOBJECT:
595 596 597
        DeleteObject(*(ht->objectHandle + mr->rdParm[0]));
        *(ht->objectHandle + mr->rdParm[0]) = 0;
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
598

Alexandre Julliard's avatar
Alexandre Julliard committed
599
    case META_SETBKCOLOR:
600 601
        SetBkColor(hdc, MAKELONG(mr->rdParm[0], mr->rdParm[1]));
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
602 603

    case META_SETBKMODE:
604 605
        SetBkMode(hdc, mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
606 607

    case META_SETMAPMODE:
608 609
        SetMapMode(hdc, mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
610 611

    case META_SETROP2:
612 613
        SetROP2(hdc, mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
614 615

    case META_SETRELABS:
616 617
        SetRelAbs(hdc, mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
618 619

    case META_SETPOLYFILLMODE:
620 621
        SetPolyFillMode(hdc, mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
622 623

    case META_SETSTRETCHBLTMODE:
624 625
        SetStretchBltMode(hdc, mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
626

Alexandre Julliard's avatar
Alexandre Julliard committed
627
    case META_SETTEXTCOLOR:
628 629
        SetTextColor(hdc, MAKELONG(mr->rdParm[0], mr->rdParm[1]));
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
630 631

    case META_SETWINDOWORG:
632 633
        SetWindowOrgEx(hdc, (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0], NULL);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
634 635

    case META_SETWINDOWEXT:
636 637
        SetWindowExtEx(hdc, (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0], NULL);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
638 639

    case META_SETVIEWPORTORG:
640 641
        SetViewportOrgEx(hdc, (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0], NULL);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
642 643

    case META_SETVIEWPORTEXT:
644 645
        SetViewportExtEx(hdc, (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0], NULL);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
646 647

    case META_OFFSETWINDOWORG:
648 649
        OffsetWindowOrgEx(hdc, (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0], NULL);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
650 651

    case META_SCALEWINDOWEXT:
652 653 654
        ScaleWindowExtEx(hdc, (SHORT)mr->rdParm[3], (SHORT)mr->rdParm[2],
                              (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0], NULL);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
655 656

    case META_OFFSETVIEWPORTORG:
657 658
        OffsetViewportOrgEx(hdc, (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0], NULL);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
659 660

    case META_SCALEVIEWPORTEXT:
661 662 663
        ScaleViewportExtEx(hdc, (SHORT)mr->rdParm[3], (SHORT)mr->rdParm[2],
                                (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0], NULL);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
664 665

    case META_LINETO:
666 667
        LineTo(hdc, (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
668 669

    case META_MOVETO:
670 671
        MoveToEx(hdc, (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0], NULL);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
672 673

    case META_EXCLUDECLIPRECT:
674 675 676
        ExcludeClipRect( hdc, (SHORT)mr->rdParm[3], (SHORT)mr->rdParm[2],
                              (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0] );
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
677 678

    case META_INTERSECTCLIPRECT:
679 680 681
        IntersectClipRect( hdc, (SHORT)mr->rdParm[3], (SHORT)mr->rdParm[2],
                                (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0] );
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
682 683

    case META_ARC:
684 685 686 687 688
        Arc(hdc, (SHORT)mr->rdParm[7], (SHORT)mr->rdParm[6],
                 (SHORT)mr->rdParm[5], (SHORT)mr->rdParm[4],
                 (SHORT)mr->rdParm[3], (SHORT)mr->rdParm[2],
                 (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
689 690

    case META_ELLIPSE:
691 692 693
        Ellipse(hdc, (SHORT)mr->rdParm[3], (SHORT)mr->rdParm[2],
                     (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
694 695

    case META_FLOODFILL:
696 697 698
        FloodFill(hdc, (SHORT)mr->rdParm[3], (SHORT)mr->rdParm[2],
                    MAKELONG(mr->rdParm[0], mr->rdParm[1]));
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
699 700

    case META_PIE:
701 702 703 704 705
        Pie(hdc, (SHORT)mr->rdParm[7], (SHORT)mr->rdParm[6],
                 (SHORT)mr->rdParm[5], (SHORT)mr->rdParm[4],
                 (SHORT)mr->rdParm[3], (SHORT)mr->rdParm[2],
                 (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
706 707

    case META_RECTANGLE:
708 709 710
        Rectangle(hdc, (SHORT)mr->rdParm[3], (SHORT)mr->rdParm[2],
                       (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
711 712

    case META_ROUNDRECT:
713 714 715 716
        RoundRect(hdc, (SHORT)mr->rdParm[5], (SHORT)mr->rdParm[4],
                       (SHORT)mr->rdParm[3], (SHORT)mr->rdParm[2],
                       (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
717 718

    case META_PATBLT:
719 720 721 722
        PatBlt(hdc, (SHORT)mr->rdParm[5], (SHORT)mr->rdParm[4],
                    (SHORT)mr->rdParm[3], (SHORT)mr->rdParm[2],
                    MAKELONG(mr->rdParm[0], mr->rdParm[1]));
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
723 724

    case META_SAVEDC:
725 726
        SaveDC(hdc);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
727 728

    case META_SETPIXEL:
729 730 731
        SetPixel(hdc, (SHORT)mr->rdParm[3], (SHORT)mr->rdParm[2],
                 MAKELONG(mr->rdParm[0], mr->rdParm[1]));
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
732 733

    case META_OFFSETCLIPRGN:
734 735
        OffsetClipRgn( hdc, (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0] );
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
736 737

    case META_TEXTOUT:
738 739 740 741 742
        s1 = mr->rdParm[0];
        TextOutA(hdc, (SHORT)mr->rdParm[((s1 + 1) >> 1) + 2],
                 (SHORT)mr->rdParm[((s1 + 1) >> 1) + 1],
                 (char *)(mr->rdParm + 1), s1);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
743 744

    case META_POLYGON:
745
        if ((pt = convert_points( mr->rdParm[0], (POINTS *)(mr->rdParm + 1))))
746 747 748 749 750
        {
            Polygon(hdc, pt, mr->rdParm[0]);
            HeapFree( GetProcessHeap(), 0, pt );
        }
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
751

Alexandre Julliard's avatar
Alexandre Julliard committed
752
    case META_POLYPOLYGON:
753 754 755 756 757
        {
            UINT i, total;
            SHORT *counts = (SHORT *)(mr->rdParm + 1);

            for (i = total = 0; i < mr->rdParm[0]; i++) total += counts[i];
758
            pt = convert_points( total, (POINTS *)(counts + mr->rdParm[0]) );
759 760 761 762 763 764 765 766 767 768 769 770 771
            if (pt)
            {
                INT *cnt32 = HeapAlloc( GetProcessHeap(), 0, mr->rdParm[0] * sizeof(*cnt32) );
                if (cnt32)
                {
                    for (i = 0; i < mr->rdParm[0]; i++) cnt32[i] = counts[i];
                    PolyPolygon( hdc, pt, cnt32, mr->rdParm[0]);
                    HeapFree( GetProcessHeap(), 0, cnt32 );
                }
            }
            HeapFree( GetProcessHeap(), 0, pt );
        }
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
772

Alexandre Julliard's avatar
Alexandre Julliard committed
773
    case META_POLYLINE:
774
        if ((pt = convert_points( mr->rdParm[0], (POINTS *)(mr->rdParm + 1))))
775 776 777 778 779
        {
            Polyline( hdc, pt, mr->rdParm[0] );
            HeapFree( GetProcessHeap(), 0, pt );
        }
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
780 781

    case META_RESTOREDC:
782 783
        RestoreDC(hdc, (SHORT)mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
784 785

    case META_SELECTOBJECT:
786 787
        SelectObject(hdc, *(ht->objectHandle + mr->rdParm[0]));
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
788 789

    case META_CHORD:
790 791 792 793 794
        Chord(hdc, (SHORT)mr->rdParm[7], (SHORT)mr->rdParm[6],
                   (SHORT)mr->rdParm[5], (SHORT)mr->rdParm[4],
                   (SHORT)mr->rdParm[3], (SHORT)mr->rdParm[2],
                   (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
795 796

    case META_CREATEPATTERNBRUSH:
797 798 799 800 801 802 803 804 805
        switch (mr->rdParm[0])
        {
        case BS_PATTERN:
            infohdr = (BITMAPINFOHEADER *)(mr->rdParm + 2);
            MF_AddHandle(ht, handles,
                         CreatePatternBrush(CreateBitmap(infohdr->biWidth,
                                      infohdr->biHeight,
                                      infohdr->biPlanes,
                                      infohdr->biBitCount,
806 807
                                      mr->rdParm +
                                      (sizeof(BITMAPINFOHEADER) / 2) + 4)));
808 809 810 811 812 813 814 815 816 817 818 819 820
            break;

        case BS_DIBPATTERN:
            infohdr = (BITMAPINFOHEADER *)(mr->rdParm + 2);
            MF_AddHandle(ht, handles, CreateDIBPatternBrushPt( infohdr, mr->rdParm[1] ));
            break;

        default:
            ERR("META_CREATEPATTERNBRUSH: Unknown pattern type %d\n",
                mr->rdParm[0]);
            break;
        }
        break;
821

Alexandre Julliard's avatar
Alexandre Julliard committed
822
    case META_CREATEPENINDIRECT:
823 824 825 826 827 828 829 830 831
        {
            LOGPEN pen;
            pen.lopnStyle = mr->rdParm[0];
            pen.lopnWidth.x = (SHORT)mr->rdParm[1];
            pen.lopnWidth.y = (SHORT)mr->rdParm[2];
            pen.lopnColor = MAKELONG( mr->rdParm[3], mr->rdParm[4] );
            MF_AddHandle(ht, handles, CreatePenIndirect( &pen ));
        }
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
832

Alexandre Julliard's avatar
Alexandre Julliard committed
833
    case META_CREATEFONTINDIRECT:
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
        {
            LOGFONTA font;
            font.lfHeight         = (SHORT)mr->rdParm[0];
            font.lfWidth          = (SHORT)mr->rdParm[1];
            font.lfEscapement     = (SHORT)mr->rdParm[2];
            font.lfOrientation    = (SHORT)mr->rdParm[3];
            font.lfWeight         = (SHORT)mr->rdParm[4];
            font.lfItalic         = LOBYTE(mr->rdParm[5]);
            font.lfUnderline      = HIBYTE(mr->rdParm[5]);
            font.lfStrikeOut      = LOBYTE(mr->rdParm[6]);
            font.lfCharSet        = HIBYTE(mr->rdParm[6]);
            font.lfOutPrecision   = LOBYTE(mr->rdParm[7]);
            font.lfClipPrecision  = HIBYTE(mr->rdParm[7]);
            font.lfQuality        = LOBYTE(mr->rdParm[8]);
            font.lfPitchAndFamily = HIBYTE(mr->rdParm[8]);
            memcpy( font.lfFaceName, mr->rdParm + 9, LF_FACESIZE );
            MF_AddHandle(ht, handles, CreateFontIndirectA( &font ));
        }
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
853

Alexandre Julliard's avatar
Alexandre Julliard committed
854
    case META_CREATEBRUSHINDIRECT:
855 856 857 858 859 860 861 862
        {
            LOGBRUSH brush;
            brush.lbStyle = mr->rdParm[0];
            brush.lbColor = MAKELONG( mr->rdParm[1], mr->rdParm[2] );
            brush.lbHatch = mr->rdParm[3];
            MF_AddHandle(ht, handles, CreateBrushIndirect( &brush ));
        }
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
863

Alexandre Julliard's avatar
Alexandre Julliard committed
864
    case META_CREATEPALETTE:
865 866
        MF_AddHandle(ht, handles, CreatePalette((LPLOGPALETTE)mr->rdParm));
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
867 868

    case META_SETTEXTALIGN:
869 870
        SetTextAlign(hdc, mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
871 872

    case META_SELECTPALETTE:
873 874
        GDISelectPalette(hdc, *(ht->objectHandle + mr->rdParm[1]), mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
875 876

    case META_SETMAPPERFLAGS:
877 878
        SetMapperFlags(hdc, MAKELONG(mr->rdParm[0],mr->rdParm[1]));
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
879 880

    case META_REALIZEPALETTE:
881 882
        GDIRealizePalette(hdc);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
883 884

    case META_ESCAPE:
885 886
        switch (mr->rdParm[0]) {
        case GETSCALINGFACTOR: /* get function ... would just NULL dereference */
887 888
        case GETPHYSPAGESIZE:
        case GETPRINTINGOFFSET:
889 890 891 892 893
             return FALSE;
        case SETABORTPROC:
             FIXME("Filtering Escape(SETABORTPROC), possible virus?\n");
             return FALSE;
        }
894
        Escape(hdc, mr->rdParm[0], mr->rdParm[1], (LPCSTR)&mr->rdParm[2], NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
895 896
        break;

Alexandre Julliard's avatar
Alexandre Julliard committed
897
    case META_EXTTEXTOUT:
898
        MF_Play_MetaExtTextOut( hdc, mr );
899
        break;
900

Alexandre Julliard's avatar
Alexandre Julliard committed
901 902
    case META_STRETCHDIB:
      {
903
        LPBITMAPINFO info = (LPBITMAPINFO) &(mr->rdParm[11]);
904
        LPSTR bits = (LPSTR)info + bitmap_info_size( info, mr->rdParm[2] );
905 906 907
        StretchDIBits( hdc, (SHORT)mr->rdParm[10], (SHORT)mr->rdParm[9], (SHORT)mr->rdParm[8],
                       (SHORT)mr->rdParm[7], (SHORT)mr->rdParm[6], (SHORT)mr->rdParm[5],
                       (SHORT)mr->rdParm[4], (SHORT)mr->rdParm[3], bits, info,
908
                       mr->rdParm[2],MAKELONG(mr->rdParm[0],mr->rdParm[1]));
Alexandre Julliard's avatar
Alexandre Julliard committed
909 910
      }
      break;
Alexandre Julliard's avatar
Alexandre Julliard committed
911 912 913

    case META_DIBSTRETCHBLT:
      {
914
        LPBITMAPINFO info = (LPBITMAPINFO) &(mr->rdParm[10]);
915
        LPSTR bits = (LPSTR)info + bitmap_info_size( info, DIB_RGB_COLORS );
916 917 918
        StretchDIBits( hdc, (SHORT)mr->rdParm[9], (SHORT)mr->rdParm[8], (SHORT)mr->rdParm[7],
                       (SHORT)mr->rdParm[6], (SHORT)mr->rdParm[5], (SHORT)mr->rdParm[4],
                       (SHORT)mr->rdParm[3], (SHORT)mr->rdParm[2], bits, info,
919
                       DIB_RGB_COLORS,MAKELONG(mr->rdParm[0],mr->rdParm[1]));
Alexandre Julliard's avatar
Alexandre Julliard committed
920
      }
921
      break;
Alexandre Julliard's avatar
Alexandre Julliard committed
922 923 924

    case META_STRETCHBLT:
      {
925 926 927 928 929
        HDC hdcSrc = CreateCompatibleDC(hdc);
        HBITMAP hbitmap = CreateBitmap(mr->rdParm[10], /*Width */
                                       mr->rdParm[11], /*Height*/
                                       mr->rdParm[13], /*Planes*/
                                       mr->rdParm[14], /*BitsPixel*/
930
                                       &mr->rdParm[15]); /*bits*/
931 932 933 934 935 936 937
        SelectObject(hdcSrc,hbitmap);
        StretchBlt(hdc, (SHORT)mr->rdParm[9], (SHORT)mr->rdParm[8],
                   (SHORT)mr->rdParm[7], (SHORT)mr->rdParm[6],
                   hdcSrc, (SHORT)mr->rdParm[5], (SHORT)mr->rdParm[4],
                   (SHORT)mr->rdParm[3], (SHORT)mr->rdParm[2],
                   MAKELONG(mr->rdParm[0],mr->rdParm[1]));
        DeleteDC(hdcSrc);
Alexandre Julliard's avatar
Alexandre Julliard committed
938 939 940
      }
      break;

941
    case META_BITBLT:
Alexandre Julliard's avatar
Alexandre Julliard committed
942
      {
943 944
        HDC hdcSrc = CreateCompatibleDC(hdc);
        HBITMAP hbitmap = CreateBitmap(mr->rdParm[7]/*Width */,
945 946 947
                                        mr->rdParm[8]/*Height*/,
                                        mr->rdParm[10]/*Planes*/,
                                        mr->rdParm[11]/*BitsPixel*/,
948
                                        &mr->rdParm[12]/*bits*/);
949 950 951 952
        SelectObject(hdcSrc,hbitmap);
        BitBlt(hdc,(SHORT)mr->rdParm[6],(SHORT)mr->rdParm[5],
                (SHORT)mr->rdParm[4],(SHORT)mr->rdParm[3],
                hdcSrc, (SHORT)mr->rdParm[2],(SHORT)mr->rdParm[1],
953
                MAKELONG(0,mr->rdParm[0]));
954
        DeleteDC(hdcSrc);
Alexandre Julliard's avatar
Alexandre Julliard committed
955 956
      }
      break;
Alexandre Julliard's avatar
Alexandre Julliard committed
957 958

    case META_CREATEREGION:
Alexandre Julliard's avatar
Alexandre Julliard committed
959
      {
960
        HRGN hrgn = CreateRectRgn(0,0,0,0);
961

962 963
        MF_Play_MetaCreateRegion(mr, hrgn);
        MF_AddHandle(ht, handles, hrgn);
Alexandre Julliard's avatar
Alexandre Julliard committed
964 965
      }
      break;
Alexandre Julliard's avatar
Alexandre Julliard committed
966

967
    case META_FILLREGION:
968 969
        FillRgn(hdc, *(ht->objectHandle + mr->rdParm[1]),
                *(ht->objectHandle + mr->rdParm[0]));
970 971 972
        break;

    case META_FRAMEREGION:
973 974 975
        FrameRgn(hdc, *(ht->objectHandle + mr->rdParm[3]),
                 *(ht->objectHandle + mr->rdParm[2]),
                 (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0]);
Alexandre Julliard's avatar
Alexandre Julliard committed
976 977
        break;

978
    case META_INVERTREGION:
979
        InvertRgn(hdc, *(ht->objectHandle + mr->rdParm[0]));
980
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
981

982
    case META_PAINTREGION:
983
        PaintRgn(hdc, *(ht->objectHandle + mr->rdParm[0]));
Alexandre Julliard's avatar
Alexandre Julliard committed
984 985
        break;

986
    case META_SELECTCLIPREGION:
987 988 989 990 991 992
        {
            HRGN hrgn = 0;

            if (mr->rdParm[0]) hrgn = *(ht->objectHandle + mr->rdParm[0]);
            SelectClipRgn(hdc, hrgn);
        }
993
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
994

995
    case META_DIBCREATEPATTERNBRUSH:
996 997 998 999
        /*  mr->rdParm[0] may be BS_PATTERN or BS_DIBPATTERN:
            but there's no difference */
        MF_AddHandle(ht, handles, CreateDIBPatternBrushPt( mr->rdParm + 2, mr->rdParm[1] ));
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1000

1001 1002
    case META_DIBBITBLT:
      /* In practice I've found that there are two layouts for
1003 1004 1005 1006
         META_DIBBITBLT, one (the first here) is the usual one when a src
         dc is actually passed to it, the second occurs when the src dc is
         passed in as NULL to the creating BitBlt. As the second case has
         no dib, a size check will suffice to distinguish.
1007

1008
         Caolan.McNamara@ul.ie */
1009 1010

        if (mr->rdSize > 12) {
1011
            LPBITMAPINFO info = (LPBITMAPINFO) &(mr->rdParm[8]);
1012
            LPSTR bits = (LPSTR)info + bitmap_info_size(info, mr->rdParm[0]);
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023

            StretchDIBits(hdc, (SHORT)mr->rdParm[7], (SHORT)mr->rdParm[6], (SHORT)mr->rdParm[5],
                          (SHORT)mr->rdParm[4], (SHORT)mr->rdParm[3], (SHORT)mr->rdParm[2],
                          (SHORT)mr->rdParm[5], (SHORT)mr->rdParm[4], bits, info,
                          DIB_RGB_COLORS, MAKELONG(mr->rdParm[0], mr->rdParm[1]));
        }
        else /* equivalent to a PatBlt */
            PatBlt(hdc, (SHORT)mr->rdParm[8], (SHORT)mr->rdParm[7],
                   (SHORT)mr->rdParm[6], (SHORT)mr->rdParm[5],
                   MAKELONG(mr->rdParm[0], mr->rdParm[1]));
        break;
1024

1025
    case META_SETTEXTCHAREXTRA:
1026 1027
        SetTextCharacterExtra(hdc, (SHORT)mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1028

1029
    case META_SETTEXTJUSTIFICATION:
1030 1031
        SetTextJustification(hdc, (SHORT)mr->rdParm[1], (SHORT)mr->rdParm[0]);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1032

1033
    case META_EXTFLOODFILL:
1034 1035 1036
        ExtFloodFill(hdc, (SHORT)mr->rdParm[4], (SHORT)mr->rdParm[3],
                     MAKELONG(mr->rdParm[1], mr->rdParm[2]),
                     mr->rdParm[0]);
1037 1038
        break;

1039
    case META_SETDIBTODEV:
1040 1041
        {
            BITMAPINFO *info = (BITMAPINFO *) &(mr->rdParm[9]);
1042
            char *bits = (char *)info + bitmap_info_size( info, mr->rdParm[0] );
1043 1044 1045 1046 1047 1048 1049
            SetDIBitsToDevice(hdc, (SHORT)mr->rdParm[8], (SHORT)mr->rdParm[7],
                              (SHORT)mr->rdParm[6], (SHORT)mr->rdParm[5],
                              (SHORT)mr->rdParm[4], (SHORT)mr->rdParm[3],
                              mr->rdParm[2], mr->rdParm[1], bits, info,
                              mr->rdParm[0]);
            break;
        }
1050 1051

#define META_UNIMP(x) case x: \
1052
FIXME("PlayMetaFileRecord:record type "#x" not implemented.\n"); \
1053
break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
    META_UNIMP(META_DRAWTEXT)
    META_UNIMP(META_ANIMATEPALETTE)
    META_UNIMP(META_SETPALENTRIES)
    META_UNIMP(META_RESIZEPALETTE)
    META_UNIMP(META_RESETDC)
    META_UNIMP(META_STARTDOC)
    META_UNIMP(META_STARTPAGE)
    META_UNIMP(META_ENDPAGE)
    META_UNIMP(META_ABORTDOC)
    META_UNIMP(META_ENDDOC)
    META_UNIMP(META_CREATEBRUSH)
    META_UNIMP(META_CREATEBITMAPINDIRECT)
    META_UNIMP(META_CREATEBITMAP)
#undef META_UNIMP
Alexandre Julliard's avatar
Alexandre Julliard committed
1068

Alexandre Julliard's avatar
Alexandre Julliard committed
1069
    default:
1070 1071
        WARN("PlayMetaFileRecord: Unknown record type %x\n", mr->rdFunction);
        return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1072
    }
1073
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1074 1075 1076
}

/******************************************************************
1077
 *         SetMetaFileBitsEx    (GDI32.@)
1078
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1079
 *  Create a metafile from raw data. No checking of the data is performed.
1080 1081 1082 1083 1084
 *  Use GetMetaFileBitsEx() to get raw data from a metafile.
 *
 * PARAMS
 *  size   [I] size of metafile, in bytes
 *  lpData [I] pointer to metafile data
1085 1086 1087 1088
 *
 * RETURNS
 *  Success: Handle to metafile.
 *  Failure: NULL.
Alexandre Julliard's avatar
Alexandre Julliard committed
1089
 */
1090
HMETAFILE WINAPI SetMetaFileBitsEx( UINT size, const BYTE *lpData )
Alexandre Julliard's avatar
Alexandre Julliard committed
1091
{
1092 1093
    const METAHEADER *mh_in = (const METAHEADER *)lpData;
    METAHEADER *mh_out;
1094 1095 1096

    if (size & 1) return 0;

1097 1098
    if (!size || mh_in->mtType != METAFILE_MEMORY || mh_in->mtVersion != MFVERSION ||
        mh_in->mtHeaderSize != sizeof(METAHEADER) / 2)
1099 1100 1101 1102 1103
    {
        SetLastError(ERROR_INVALID_DATA);
        return 0;
    }

1104 1105
    mh_out = HeapAlloc( GetProcessHeap(), 0, size );
    if (!mh_out)
1106 1107 1108 1109 1110
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return 0;
    }

1111 1112 1113
    memcpy(mh_out, mh_in, size);
    mh_out->mtSize = size / 2;
    return MF_Create_HMETAFILE(mh_out);
Alexandre Julliard's avatar
Alexandre Julliard committed
1114 1115
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1116
/*****************************************************************
Jon Griffiths's avatar
Jon Griffiths committed
1117 1118 1119
 *  GetMetaFileBitsEx     (GDI32.@)
 *
 * Get raw metafile data.
1120
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1121
 *  Copies the data from metafile _hmf_ into the buffer _buf_.
1122 1123 1124 1125 1126
 *
 * PARAMS
 *  hmf   [I] metafile
 *  nSize [I] size of buf
 *  buf   [O] buffer to receive raw metafile data
1127 1128 1129 1130
 *
 * RETURNS
 *  If _buf_ is zero, returns size of buffer required. Otherwise,
 *  returns number of bytes copied.
Alexandre Julliard's avatar
Alexandre Julliard committed
1131
 */
1132
UINT WINAPI GetMetaFileBitsEx( HMETAFILE hmf, UINT nSize, LPVOID buf )
Jon Griffiths's avatar
Jon Griffiths committed
1133
{
1134 1135 1136
    METAHEADER *mh = MF_GetMetaHeader(hmf);
    UINT mfSize;

1137
    TRACE("(%p,%d,%p)\n", hmf, nSize, buf);
1138 1139
    if (!mh) return 0;  /* FIXME: error code */
    if(mh->mtType == METAFILE_DISK)
1140
        FIXME("Disk-based metafile?\n");
1141 1142
    mfSize = mh->mtSize * 2;
    if (!buf) {
1143
	TRACE("returning size %d\n", mfSize);
1144 1145 1146 1147
	return mfSize;
    }
    if(mfSize > nSize) mfSize = nSize;
    memmove(buf, mh, mfSize);
1148
    return mfSize;
Alexandre Julliard's avatar
Alexandre Julliard committed
1149 1150
}

1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225
#include <pshpack2.h>
typedef struct
{
    DWORD magic;   /* WMFC */
    WORD unk04;    /* 1 */
    WORD unk06;    /* 0 */
    WORD unk08;    /* 0 */
    WORD unk0a;    /* 1 */
    WORD checksum;
    DWORD unk0e;   /* 0 */
    DWORD num_chunks;
    DWORD chunk_size;
    DWORD remaining_size;
    DWORD emf_size;
    BYTE *emf_data;
} mf_comment_chunk;
#include <poppack.h>

static const DWORD wmfc_magic = 0x43464d57;

/******************************************************************
 *         add_mf_comment
 *
 * Helper for GetWinMetaFileBits
 *
 * Add the MFCOMMENT record[s] which is essentially a copy
 * of the original emf.
 */
static BOOL add_mf_comment(HDC hdc, HENHMETAFILE emf)
{
    DWORD size = GetEnhMetaFileBits(emf, 0, NULL), i;
    BYTE *bits, *chunk_data;
    mf_comment_chunk *chunk = NULL;
    BOOL ret = FALSE;
    static const DWORD max_chunk_size = 0x2000;

    if(!size) return FALSE;
    chunk_data = bits = HeapAlloc(GetProcessHeap(), 0, size);
    if(!bits) return FALSE;
    if(!GetEnhMetaFileBits(emf, size, bits)) goto end;

    chunk = HeapAlloc(GetProcessHeap(), 0, max_chunk_size + FIELD_OFFSET(mf_comment_chunk, emf_data));
    if(!chunk) goto end;

    chunk->magic = wmfc_magic;
    chunk->unk04 = 1;
    chunk->unk06 = 0;
    chunk->unk08 = 0;
    chunk->unk0a = 1;
    chunk->checksum = 0; /* We fixup the first chunk's checksum before returning from GetWinMetaFileBits */
    chunk->unk0e = 0;
    chunk->num_chunks = (size + max_chunk_size - 1) / max_chunk_size;
    chunk->chunk_size = max_chunk_size;
    chunk->remaining_size = size;
    chunk->emf_size = size;

    for(i = 0; i < chunk->num_chunks; i++)
    {
        if(i == chunk->num_chunks - 1) /* last chunk */
            chunk->chunk_size = chunk->remaining_size;

        chunk->remaining_size -= chunk->chunk_size;
        memcpy(&chunk->emf_data, chunk_data, chunk->chunk_size);
        chunk_data += chunk->chunk_size;

        if(!Escape(hdc, MFCOMMENT, chunk->chunk_size + FIELD_OFFSET(mf_comment_chunk, emf_data), (char*)chunk, NULL))
            goto end;
    }
    ret = TRUE;
end:
    HeapFree(GetProcessHeap(), 0, chunk);
    HeapFree(GetProcessHeap(), 0, bits);
    return ret;
}

1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
/*******************************************************************
 *        muldiv
 *
 * Behaves somewhat differently to MulDiv when the answer is -ve
 * and also rounds n.5 towards zero
 */
static INT muldiv(INT m1, INT m2, INT d)
{
    LONGLONG ret;

    ret = ((LONGLONG)m1 * m2 + d/2) / d; /* Always add d/2 even if ret will be -ve */

    if((LONGLONG)m1 * m2 * 2 == (2 * ret - 1) * d) /* If the answer is exactly n.5 round towards zero */
    {
        if(ret > 0) ret--;
        else ret++;
    }
    return ret;
}

1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270
/******************************************************************
 *         set_window
 *
 * Helper for GetWinMetaFileBits
 *
 * Add the SetWindowOrg and SetWindowExt records
 */
static BOOL set_window(HDC hdc, HENHMETAFILE emf, HDC ref_dc, INT map_mode)
{
    ENHMETAHEADER header;
    INT horz_res, vert_res, horz_size, vert_size;
    POINT pt;

    if(!GetEnhMetaFileHeader(emf, sizeof(header), &header)) return FALSE;

    horz_res = GetDeviceCaps(ref_dc, HORZRES);
    vert_res = GetDeviceCaps(ref_dc, VERTRES);
    horz_size = GetDeviceCaps(ref_dc, HORZSIZE);
    vert_size = GetDeviceCaps(ref_dc, VERTSIZE);

    switch(map_mode)
    {
    case MM_TEXT:
    case MM_ISOTROPIC:
    case MM_ANISOTROPIC:
1271 1272
        pt.y = muldiv(header.rclFrame.top, vert_res, vert_size * 100);
        pt.x = muldiv(header.rclFrame.left, horz_res, horz_size * 100);
1273 1274
        break;
    case MM_LOMETRIC:
1275 1276
        pt.y = muldiv(-header.rclFrame.top, 1, 10) + 1;
        pt.x = muldiv( header.rclFrame.left, 1, 10);
1277 1278 1279
        break;
    case MM_HIMETRIC:
        pt.y = -header.rclFrame.top + 1;
1280
        pt.x = (header.rclFrame.left >= 0) ? header.rclFrame.left : header.rclFrame.left + 1; /* See the tests */
1281 1282
        break;
    case MM_LOENGLISH:
1283 1284
        pt.y = muldiv(-header.rclFrame.top, 10, 254) + 1;
        pt.x = muldiv( header.rclFrame.left, 10, 254);
1285 1286
        break;
    case MM_HIENGLISH:
1287 1288
        pt.y = muldiv(-header.rclFrame.top, 100, 254) + 1;
        pt.x = muldiv( header.rclFrame.left, 100, 254);
1289 1290
        break;
    case MM_TWIPS:
1291 1292
        pt.y = muldiv(-header.rclFrame.top, 72 * 20, 2540) + 1;
        pt.x = muldiv( header.rclFrame.left, 72 * 20, 2540);
1293 1294 1295 1296 1297 1298 1299
        break;
    default:
        WARN("Unknown map mode %d\n", map_mode);
        return FALSE;
    }
    SetWindowOrgEx(hdc, pt.x, pt.y, NULL);

1300 1301
    pt.x = muldiv(header.rclFrame.right - header.rclFrame.left, horz_res, horz_size * 100);
    pt.y = muldiv(header.rclFrame.bottom - header.rclFrame.top, vert_res, vert_size * 100);
1302 1303 1304 1305
    SetWindowExtEx(hdc, pt.x, pt.y, NULL);
    return TRUE;
}

1306
/******************************************************************
1307
 *         GetWinMetaFileBits [GDI32.@]
1308
 */
1309 1310
UINT WINAPI GetWinMetaFileBits(HENHMETAFILE hemf,
                                UINT cbBuffer, LPBYTE lpbBuffer,
1311
                                INT map_mode, HDC hdcRef)
1312
{
1313 1314
    HDC hdcmf;
    HMETAFILE hmf;
1315
    UINT ret, full_size;
1316 1317 1318 1319 1320
    RECT rc;

    GetClipBox(hdcRef, &rc);

    TRACE("(%p,%d,%p,%d,%p) rc=%s\n", hemf, cbBuffer, lpbBuffer,
1321
          map_mode, hdcRef, wine_dbgstr_rect(&rc));
1322

1323
    hdcmf = CreateMetaFileW(NULL);
1324 1325

    add_mf_comment(hdcmf, hemf);
1326 1327 1328
    SetMapMode(hdcmf, map_mode);
    if(!set_window(hdcmf, hemf, hdcRef, map_mode))
        goto error;
1329

1330
    PlayEnhMetaFile(hdcmf, hemf, &rc);
1331
    hmf = CloseMetaFile(hdcmf);
1332
    full_size = GetMetaFileBitsEx(hmf, 0, NULL);
1333 1334
    ret = GetMetaFileBitsEx(hmf, cbBuffer, lpbBuffer);
    DeleteMetaFile(hmf);
1335

1336 1337 1338 1339 1340 1341 1342 1343 1344 1345
    if(ret && ret == full_size && lpbBuffer) /* fixup checksum, but only if retrieving all of the bits */
    {
        WORD checksum = 0;
        METARECORD *comment_rec = (METARECORD*)(lpbBuffer + sizeof(METAHEADER));
        UINT i;

        for(i = 0; i < full_size / 2; i++)
            checksum += ((WORD*)lpbBuffer)[i];
        comment_rec->rdParm[8] = ~checksum + 1;
    }
1346
    return ret;
1347 1348 1349 1350

error:
    DeleteMetaFile(CloseMetaFile(hdcmf));
    return 0;
1351
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1352

Alexandre Julliard's avatar
Alexandre Julliard committed
1353
/******************************************************************
1354
 *         MF_Play_MetaCreateRegion
Alexandre Julliard's avatar
Alexandre Julliard committed
1355 1356
 *
 *  Handles META_CREATEREGION for PlayMetaFileRecord().
1357
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1358
 *	The layout of the record looks something like this:
1359
 *
1360
 *	 rdParm	meaning
Alexandre Julliard's avatar
Alexandre Julliard committed
1361 1362 1363 1364 1365
 *	 0		Always 0?
 *	 1		Always 6?
 *	 2		Looks like a handle? - not constant
 *	 3		0 or 1 ??
 *	 4		Total number of bytes
1366
 *	 5		No. of separate bands = n [see below]
Alexandre Julliard's avatar
Alexandre Julliard committed
1367 1368 1369 1370 1371 1372 1373 1374
 *	 6		Largest number of x co-ords in a band
 *	 7-10		Bounding box x1 y1 x2 y2
 *	 11-...		n bands
 *
 *	 Regions are divided into bands that are uniform in the
 *	 y-direction. Each band consists of pairs of on/off x-coords and is
 *	 written as
 *		m y0 y1 x1 x2 x3 ... xm m
1375
 *	 into successive rdParm[]s.
Alexandre Julliard's avatar
Alexandre Julliard committed
1376 1377 1378 1379 1380 1381 1382
 *
 *	 This is probably just a dump of the internal RGNOBJ?
 *
 *	 HDMD - 18/12/97
 *
 */

1383
static BOOL MF_Play_MetaCreateRegion( METARECORD *mr, HRGN hrgn )
Alexandre Julliard's avatar
Alexandre Julliard committed
1384 1385 1386 1387
{
    WORD band, pair;
    WORD *start, *end;
    INT16 y0, y1;
1388
    HRGN hrgn2 = CreateRectRgn( 0, 0, 0, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1389

1390
    for(band  = 0, start = &(mr->rdParm[11]); band < mr->rdParm[5];
Alexandre Julliard's avatar
Alexandre Julliard committed
1391 1392
 					        band++, start = end + 1) {
        if(*start / 2 != (*start + 1) / 2) {
1393
 	    WARN("Delimiter not even.\n");
1394
	    DeleteObject( hrgn2 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1395 1396 1397 1398 1399
 	    return FALSE;
        }

	end = start + *start + 3;
	if(end > (WORD *)mr + mr->rdSize) {
1400
	    WARN("End points outside record.\n");
1401
	    DeleteObject( hrgn2 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1402 1403 1404 1405
	    return FALSE;
        }

	if(*start != *end) {
1406
	    WARN("Mismatched delimiters.\n");
1407
	    DeleteObject( hrgn2 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1408 1409 1410 1411 1412 1413
	    return FALSE;
	}

	y0 = *(INT16 *)(start + 1);
	y1 = *(INT16 *)(start + 2);
	for(pair = 0; pair < *start / 2; pair++) {
1414
	    SetRectRgn( hrgn2, *(INT16 *)(start + 3 + 2*pair), y0,
Alexandre Julliard's avatar
Alexandre Julliard committed
1415
				 *(INT16 *)(start + 4 + 2*pair), y1 );
1416
	    CombineRgn(hrgn, hrgn, hrgn2, RGN_OR);
Alexandre Julliard's avatar
Alexandre Julliard committed
1417 1418
        }
    }
1419
    DeleteObject( hrgn2 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1420 1421
    return TRUE;
 }
1422

Alexandre Julliard's avatar
Alexandre Julliard committed
1423

1424 1425 1426 1427
/******************************************************************
 *         MF_Play_MetaExtTextOut
 *
 *  Handles META_EXTTEXTOUT for PlayMetaFileRecord().
Alexandre Julliard's avatar
Alexandre Julliard committed
1428
 */
1429

1430
static BOOL MF_Play_MetaExtTextOut(HDC hdc, METARECORD *mr)
1431
{
1432 1433
    INT *dx = NULL;
    int i;
1434
    SHORT *dxx;
1435
    LPSTR sot;
1436 1437
    DWORD len;
    WORD s1;
1438
    RECT rect;
1439
    BOOL isrect = mr->rdParm[3] & (ETO_OPAQUE | ETO_CLIPPED);
1440 1441 1442

    s1 = mr->rdParm[2];                              /* String length */
    len = sizeof(METARECORD) + (((s1 + 1) >> 1) * 2) + 2 * sizeof(short)
1443
        + sizeof(UINT16) + (isrect ? 4 * sizeof(SHORT) : 0);
1444 1445 1446
                                           /* rec len without dx array */

    sot = (LPSTR)&mr->rdParm[4];		      /* start_of_text */
1447
    if (isrect)
1448 1449 1450 1451 1452
    {
        rect.left   = (SHORT)mr->rdParm[4];
        rect.top    = (SHORT)mr->rdParm[5];
        rect.right  = (SHORT)mr->rdParm[6];
        rect.bottom = (SHORT)mr->rdParm[7];
1453
        sot += 4 * sizeof(SHORT);  /* there is a rectangle, so add offset */
1454
    }
1455

1456
    if (mr->rdSize == len / 2)
Austin English's avatar
Austin English committed
1457
        dxx = NULL;                      /* determine if array is present */
1458
    else
1459
        if (mr->rdSize == (len + s1 * sizeof(INT16)) / 2)
1460
        {
1461
            dxx = (SHORT *)(sot+(((s1+1)>>1)*2));
1462
            dx = HeapAlloc( GetProcessHeap(), 0, s1*sizeof(INT));
1463
            if (dx) for (i = 0; i < s1; i++) dx[i] = dxx[i];
1464
        }
1465
	else {
1466 1467
            TRACE("%s  len: %d\n",  sot, mr->rdSize);
            WARN("Please report: ExtTextOut len=%d slen=%d rdSize=%d opt=%04x\n",
1468
		 len, s1, mr->rdSize, mr->rdParm[3]);
Austin English's avatar
Austin English committed
1469
	    dxx = NULL; /* shouldn't happen -- but if, we continue with NULL */
1470
	}
1471 1472 1473 1474 1475 1476
    ExtTextOutA( hdc,
                 (SHORT)mr->rdParm[1],       /* X position */
                 (SHORT)mr->rdParm[0],       /* Y position */
                 mr->rdParm[3],              /* options */
                 &rect,                      /* rectangle */
                 sot,                        /* string */
1477
                 s1, dx);                    /* length, dx array */
1478 1479
    if (dx)
    {
1480
        TRACE("%s  len: %d  dx0: %d\n", sot, mr->rdSize, dx[0]);
1481 1482
        HeapFree( GetProcessHeap(), 0, dx );
    }
1483 1484
    return TRUE;
}