metafile.c 44.5 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
    HPEN hPen;
    HBRUSH hBrush;
397 398
    HPALETTE hPal;
    HRGN hRgn;
399 400
    BOOL loaded = FALSE;

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

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

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

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

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

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

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

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

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

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

/******************************************************************
486
 *            EnumMetaFile   (GDI32.@)
487 488 489 490
 *
 *  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)
491
 *  or when no records are left (which is considered to be success).
492 493 494 495
 *
 * RETURNS
 *  TRUE on success, FALSE on failure.
 */
496
BOOL WINAPI EnumMetaFile(HDC hdc, HMETAFILE hmf, MFENUMPROC lpEnumFunc, LPARAM lpData)
Jon Griffiths's avatar
Jon Griffiths committed
497
{
Alexandre Julliard's avatar
Alexandre Julliard committed
498
    METAHEADER *mhTemp = NULL, *mh = MF_GetMetaHeader(hmf);
Alexandre Julliard's avatar
Alexandre Julliard committed
499
    METARECORD *mr;
500
    HANDLETABLE *ht;
Alexandre Julliard's avatar
Alexandre Julliard committed
501
    BOOL result = TRUE;
502 503
    int i;
    unsigned int offset = 0;
504 505 506
    HPEN hPen;
    HBRUSH hBrush;
    HFONT hFont;
Alexandre Julliard's avatar
Alexandre Julliard committed
507

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    case META_SCALEWINDOWEXT:
650 651 652
        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
653 654

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

    case META_SCALEVIEWPORTEXT:
659 660 661
        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
662 663

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

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

    case META_EXCLUDECLIPRECT:
672 673 674
        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
675 676

    case META_INTERSECTCLIPRECT:
677 678 679
        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
680 681

    case META_ARC:
682 683 684 685 686
        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
687 688

    case META_ELLIPSE:
689 690 691
        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
692 693

    case META_FLOODFILL:
694 695 696
        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
697 698

    case META_PIE:
699 700 701 702 703
        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
704 705

    case META_RECTANGLE:
706 707 708
        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
709 710

    case META_ROUNDRECT:
711 712 713 714
        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
715 716

    case META_PATBLT:
717 718 719 720
        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
721 722

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

    case META_SETPIXEL:
727 728 729
        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
730 731

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

    case META_TEXTOUT:
736 737 738 739 740
        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
741 742

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

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

            for (i = total = 0; i < mr->rdParm[0]; i++) total += counts[i];
756
            pt = convert_points( total, (POINTS *)(counts + mr->rdParm[0]) );
757 758 759 760 761 762 763 764 765 766 767 768 769
            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
770

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

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

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

    case META_CHORD:
788 789 790 791 792
        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
793 794

    case META_CREATEPATTERNBRUSH:
795 796 797 798 799 800 801 802 803
        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,
804 805
                                      mr->rdParm +
                                      (sizeof(BITMAPINFOHEADER) / 2) + 4)));
806 807 808 809 810 811 812 813 814 815 816 817 818
            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;
819

Alexandre Julliard's avatar
Alexandre Julliard committed
820
    case META_CREATEPENINDIRECT:
821 822 823 824 825 826 827 828 829
        {
            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
830

Alexandre Julliard's avatar
Alexandre Julliard committed
831
    case META_CREATEFONTINDIRECT:
832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850
        {
            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
851

Alexandre Julliard's avatar
Alexandre Julliard committed
852
    case META_CREATEBRUSHINDIRECT:
853 854 855 856 857 858 859 860
        {
            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
861

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

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

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

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

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

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
899 900
    case META_STRETCHDIB:
      {
901
        LPBITMAPINFO info = (LPBITMAPINFO) &(mr->rdParm[11]);
902
        LPSTR bits = (LPSTR)info + bitmap_info_size( info, mr->rdParm[2] );
903 904 905
        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,
906
                       mr->rdParm[2],MAKELONG(mr->rdParm[0],mr->rdParm[1]));
Alexandre Julliard's avatar
Alexandre Julliard committed
907 908
      }
      break;
Alexandre Julliard's avatar
Alexandre Julliard committed
909 910 911

    case META_DIBSTRETCHBLT:
      {
912
        LPBITMAPINFO info = (LPBITMAPINFO) &(mr->rdParm[10]);
913
        LPSTR bits = (LPSTR)info + bitmap_info_size( info, DIB_RGB_COLORS );
914 915 916
        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,
917
                       DIB_RGB_COLORS,MAKELONG(mr->rdParm[0],mr->rdParm[1]));
Alexandre Julliard's avatar
Alexandre Julliard committed
918
      }
919
      break;
Alexandre Julliard's avatar
Alexandre Julliard committed
920 921 922

    case META_STRETCHBLT:
      {
923 924 925 926 927
        HDC hdcSrc = CreateCompatibleDC(hdc);
        HBITMAP hbitmap = CreateBitmap(mr->rdParm[10], /*Width */
                                       mr->rdParm[11], /*Height*/
                                       mr->rdParm[13], /*Planes*/
                                       mr->rdParm[14], /*BitsPixel*/
928
                                       &mr->rdParm[15]); /*bits*/
929 930 931 932 933 934 935
        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
936 937 938
      }
      break;

939
    case META_BITBLT:
Alexandre Julliard's avatar
Alexandre Julliard committed
940
      {
941 942
        HDC hdcSrc = CreateCompatibleDC(hdc);
        HBITMAP hbitmap = CreateBitmap(mr->rdParm[7]/*Width */,
943 944 945
                                        mr->rdParm[8]/*Height*/,
                                        mr->rdParm[10]/*Planes*/,
                                        mr->rdParm[11]/*BitsPixel*/,
946
                                        &mr->rdParm[12]/*bits*/);
947 948 949 950
        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],
951
                MAKELONG(0,mr->rdParm[0]));
952
        DeleteDC(hdcSrc);
Alexandre Julliard's avatar
Alexandre Julliard committed
953 954
      }
      break;
Alexandre Julliard's avatar
Alexandre Julliard committed
955 956

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

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

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

    case META_FRAMEREGION:
971 972 973
        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
974 975
        break;

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

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

984
    case META_SELECTCLIPREGION:
985 986 987 988 989 990
        {
            HRGN hrgn = 0;

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

993
    case META_DIBCREATEPATTERNBRUSH:
994 995 996 997
        /*  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
998

999 1000
    case META_DIBBITBLT:
      /* In practice I've found that there are two layouts for
1001 1002 1003 1004
         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.
1005

1006
         Caolan.McNamara@ul.ie */
1007 1008

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

            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;
1022

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

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

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

1037
    case META_SETDIBTODEV:
1038 1039
        {
            BITMAPINFO *info = (BITMAPINFO *) &(mr->rdParm[9]);
1040
            char *bits = (char *)info + bitmap_info_size( info, mr->rdParm[0] );
1041 1042 1043 1044 1045 1046 1047
            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;
        }
1048 1049

#define META_UNIMP(x) case x: \
1050
FIXME("PlayMetaFileRecord:record type "#x" not implemented.\n"); \
1051
break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065
    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
1066

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

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

    if (size & 1) return 0;

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

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

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

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

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

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
#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;
}

1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243
/*******************************************************************
 *        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;
}

1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268
/******************************************************************
 *         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:
1269 1270
        pt.y = muldiv(header.rclFrame.top, vert_res, vert_size * 100);
        pt.x = muldiv(header.rclFrame.left, horz_res, horz_size * 100);
1271 1272
        break;
    case MM_LOMETRIC:
1273 1274
        pt.y = muldiv(-header.rclFrame.top, 1, 10) + 1;
        pt.x = muldiv( header.rclFrame.left, 1, 10);
1275 1276 1277
        break;
    case MM_HIMETRIC:
        pt.y = -header.rclFrame.top + 1;
1278
        pt.x = (header.rclFrame.left >= 0) ? header.rclFrame.left : header.rclFrame.left + 1; /* See the tests */
1279 1280
        break;
    case MM_LOENGLISH:
1281 1282
        pt.y = muldiv(-header.rclFrame.top, 10, 254) + 1;
        pt.x = muldiv( header.rclFrame.left, 10, 254);
1283 1284
        break;
    case MM_HIENGLISH:
1285 1286
        pt.y = muldiv(-header.rclFrame.top, 100, 254) + 1;
        pt.x = muldiv( header.rclFrame.left, 100, 254);
1287 1288
        break;
    case MM_TWIPS:
1289 1290
        pt.y = muldiv(-header.rclFrame.top, 72 * 20, 2540) + 1;
        pt.x = muldiv( header.rclFrame.left, 72 * 20, 2540);
1291 1292 1293 1294 1295 1296 1297
        break;
    default:
        WARN("Unknown map mode %d\n", map_mode);
        return FALSE;
    }
    SetWindowOrgEx(hdc, pt.x, pt.y, NULL);

1298 1299
    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);
1300 1301 1302 1303
    SetWindowExtEx(hdc, pt.x, pt.y, NULL);
    return TRUE;
}

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

    GetClipBox(hdcRef, &rc);

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

1321
    hdcmf = CreateMetaFileW(NULL);
1322 1323

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

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

1334 1335 1336 1337 1338 1339 1340 1341 1342 1343
    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;
    }
1344
    return ret;
1345 1346 1347 1348

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1351
/******************************************************************
1352
 *         MF_Play_MetaCreateRegion
Alexandre Julliard's avatar
Alexandre Julliard committed
1353 1354
 *
 *  Handles META_CREATEREGION for PlayMetaFileRecord().
1355
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1356
 *	The layout of the record looks something like this:
1357
 *
1358
 *	 rdParm	meaning
Alexandre Julliard's avatar
Alexandre Julliard committed
1359 1360 1361 1362 1363
 *	 0		Always 0?
 *	 1		Always 6?
 *	 2		Looks like a handle? - not constant
 *	 3		0 or 1 ??
 *	 4		Total number of bytes
1364
 *	 5		No. of separate bands = n [see below]
Alexandre Julliard's avatar
Alexandre Julliard committed
1365 1366 1367 1368 1369 1370 1371 1372
 *	 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
1373
 *	 into successive rdParm[]s.
Alexandre Julliard's avatar
Alexandre Julliard committed
1374 1375 1376 1377 1378 1379 1380
 *
 *	 This is probably just a dump of the internal RGNOBJ?
 *
 *	 HDMD - 18/12/97
 *
 */

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

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

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

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1421

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

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

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

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

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