wine/dlls/wineps.drv/printproc.c

3183 lines
96 KiB
C

/*
* Print processor implementation.
*
* Copyright 2022 Piotr Caban for CodeWeavers
*
* 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
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <math.h>
#include <stdlib.h>
#include <windows.h>
#include <ntgdi.h>
#include <winspool.h>
#include <ddk/winsplp.h>
#include <usp10.h>
#include "psdrv.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
#define EMFSPOOL_VERSION 0x10000
#define PP_MAGIC 0x952173fe
struct pp_data
{
DWORD magic;
HANDLE hport;
WCHAR *doc_name;
WCHAR *out_file;
print_ctx *ctx;
struct ps_brush_pattern *patterns;
BOOL path;
INT break_extra;
INT break_rem;
INT saved_dc_size;
INT saved_dc_top;
struct
{
INT break_extra;
INT break_rem;
} *saved_dc;
};
typedef enum
{
EMRI_METAFILE = 1,
EMRI_ENGINE_FONT,
EMRI_DEVMODE,
EMRI_TYPE1_FONT,
EMRI_PRESTARTPAGE,
EMRI_DESIGNVECTOR,
EMRI_SUBSET_FONT,
EMRI_DELTA_FONT,
EMRI_FORM_METAFILE,
EMRI_BW_METAFILE,
EMRI_BW_FORM_METAFILE,
EMRI_METAFILE_DATA,
EMRI_METAFILE_EXT,
EMRI_BW_METAFILE_EXT,
EMRI_ENGINE_FONT_EXT,
EMRI_TYPE1_FONT_EXT,
EMRI_DESIGNVECTOR_EXT,
EMRI_SUBSET_FONT_EXT,
EMRI_DELTA_FONT_EXT,
EMRI_PS_JOB_DATA,
EMRI_EMBED_FONT_EXT,
} record_type;
typedef struct
{
unsigned int dwVersion;
unsigned int cjSize;
unsigned int dpszDocName;
unsigned int dpszOutput;
} emfspool_header;
typedef struct
{
unsigned int ulID;
unsigned int cjSize;
} record_hdr;
typedef struct
{
EMR emr;
INT break_extra;
INT break_count;
} EMRSETTEXTJUSTIFICATION;
BOOL WINAPI SeekPrinter(HANDLE, LARGE_INTEGER, LARGE_INTEGER*, DWORD, BOOL);
static const WCHAR emf_1003[] = L"NT EMF 1.003";
#define EMRICASE(x) case x: return #x
static const char * debugstr_rec_type(int id)
{
switch (id)
{
EMRICASE(EMRI_METAFILE);
EMRICASE(EMRI_ENGINE_FONT);
EMRICASE(EMRI_DEVMODE);
EMRICASE(EMRI_TYPE1_FONT);
EMRICASE(EMRI_PRESTARTPAGE);
EMRICASE(EMRI_DESIGNVECTOR);
EMRICASE(EMRI_SUBSET_FONT);
EMRICASE(EMRI_DELTA_FONT);
EMRICASE(EMRI_FORM_METAFILE);
EMRICASE(EMRI_BW_METAFILE);
EMRICASE(EMRI_BW_FORM_METAFILE);
EMRICASE(EMRI_METAFILE_DATA);
EMRICASE(EMRI_METAFILE_EXT);
EMRICASE(EMRI_BW_METAFILE_EXT);
EMRICASE(EMRI_ENGINE_FONT_EXT);
EMRICASE(EMRI_TYPE1_FONT_EXT);
EMRICASE(EMRI_DESIGNVECTOR_EXT);
EMRICASE(EMRI_SUBSET_FONT_EXT);
EMRICASE(EMRI_DELTA_FONT_EXT);
EMRICASE(EMRI_PS_JOB_DATA);
EMRICASE(EMRI_EMBED_FONT_EXT);
default:
FIXME("unknown record type: %d\n", id);
return NULL;
}
}
#undef EMRICASE
static struct pp_data* get_handle_data(HANDLE pp)
{
struct pp_data *ret = (struct pp_data *)pp;
if (!ret || ret->magic != PP_MAGIC)
{
SetLastError(ERROR_INVALID_HANDLE);
return NULL;
}
return ret;
}
static inline INT GDI_ROUND(double val)
{
return (int)floor(val + 0.5);
}
static void translate(RECT *rect, const XFORM *xform)
{
double x, y;
x = rect->left;
y = rect->top;
rect->left = GDI_ROUND(x * xform->eM11 + y * xform->eM21 + xform->eDx);
rect->top = GDI_ROUND(x * xform->eM12 + y * xform->eM22 + xform->eDy);
x = rect->right;
y = rect->bottom;
rect->right = GDI_ROUND(x * xform->eM11 + y * xform->eM21 + xform->eDx);
rect->bottom = GDI_ROUND(x * xform->eM12 + y * xform->eM22 + xform->eDy);
}
static inline void get_bounding_rect(RECT *rect, int x, int y, int width, int height)
{
rect->left = x;
rect->right = x + width;
rect->top = y;
rect->bottom = y + height;
if (rect->left > rect->right)
{
int tmp = rect->left;
rect->left = rect->right + 1;
rect->right = tmp + 1;
}
if (rect->top > rect->bottom)
{
int tmp = rect->top;
rect->top = rect->bottom + 1;
rect->bottom = tmp + 1;
}
}
static inline void order_rect(RECT *rect)
{
if (rect->left > rect->right)
{
int tmp = rect->left;
rect->left = rect->right;
rect->right = tmp;
}
if (rect->top > rect->bottom)
{
int tmp = rect->top;
rect->top = rect->bottom;
rect->bottom = tmp;
}
}
static BOOL intersect_vis_rectangles(struct ps_bitblt_coords *dst, struct ps_bitblt_coords *src)
{
RECT rect;
/* intersect the rectangles */
if ((src->width == dst->width) && (src->height == dst->height)) /* no stretching */
{
OffsetRect(&src->visrect, dst->x - src->x, dst->y - src->y);
if (!IntersectRect(&rect, &src->visrect, &dst->visrect)) return FALSE;
src->visrect = dst->visrect = rect;
OffsetRect(&src->visrect, src->x - dst->x, src->y - dst->y);
}
else /* stretching */
{
/* map source rectangle into destination coordinates */
rect = src->visrect;
OffsetRect(&rect,
-src->x - (src->width < 0 ? 1 : 0),
-src->y - (src->height < 0 ? 1 : 0));
rect.left = rect.left * dst->width / src->width;
rect.top = rect.top * dst->height / src->height;
rect.right = rect.right * dst->width / src->width;
rect.bottom = rect.bottom * dst->height / src->height;
order_rect(&rect);
/* when the source rectangle needs to flip and it doesn't fit in the source device
area, the destination area isn't flipped. So, adjust destination coordinates */
if (src->width < 0 && dst->width > 0 &&
(src->x + src->width + 1 < src->visrect.left || src->x > src->visrect.right))
dst->x += (dst->width - rect.right) - rect.left;
else if (src->width > 0 && dst->width < 0 &&
(src->x < src->visrect.left || src->x + src->width > src->visrect.right))
dst->x -= rect.right - (dst->width - rect.left);
if (src->height < 0 && dst->height > 0 &&
(src->y + src->height + 1 < src->visrect.top || src->y > src->visrect.bottom))
dst->y += (dst->height - rect.bottom) - rect.top;
else if (src->height > 0 && dst->height < 0 &&
(src->y < src->visrect.top || src->y + src->height > src->visrect.bottom))
dst->y -= rect.bottom - (dst->height - rect.top);
OffsetRect(&rect, dst->x, dst->y);
/* avoid rounding errors */
rect.left--;
rect.top--;
rect.right++;
rect.bottom++;
if (!IntersectRect(&dst->visrect, &rect, &dst->visrect)) return FALSE;
/* map destination rectangle back to source coordinates */
rect = dst->visrect;
OffsetRect(&rect,
-dst->x - (dst->width < 0 ? 1 : 0),
-dst->y - (dst->height < 0 ? 1 : 0));
rect.left = src->x + rect.left * src->width / dst->width;
rect.top = src->y + rect.top * src->height / dst->height;
rect.right = src->x + rect.right * src->width / dst->width;
rect.bottom = src->y + rect.bottom * src->height / dst->height;
order_rect(&rect);
/* avoid rounding errors */
rect.left--;
rect.top--;
rect.right++;
rect.bottom++;
if (!IntersectRect(&src->visrect, &rect, &src->visrect)) return FALSE;
}
return TRUE;
}
static void clip_visrect(HDC hdc, RECT *dst, const RECT *src)
{
HRGN hrgn;
hrgn = CreateRectRgn(0, 0, 0, 0);
if (GetRandomRgn(hdc, hrgn, 3) == 1)
{
GetRgnBox(hrgn, dst);
IntersectRect(dst, dst, src);
}
else
{
*dst = *src;
}
DeleteObject(hrgn);
}
static void get_vis_rectangles(HDC hdc, struct ps_bitblt_coords *dst,
const XFORM *xform, DWORD width, DWORD height, struct ps_bitblt_coords *src)
{
RECT rect;
rect.left = dst->log_x;
rect.top = dst->log_y;
rect.right = dst->log_x + dst->log_width;
rect.bottom = dst->log_y + dst->log_height;
LPtoDP(hdc, (POINT *)&rect, 2);
dst->x = rect.left;
dst->y = rect.top;
dst->width = rect.right - rect.left;
dst->height = rect.bottom - rect.top;
if (dst->layout & LAYOUT_RTL && dst->layout & LAYOUT_BITMAPORIENTATIONPRESERVED)
{
dst->x += dst->width;
dst->width = -dst->width;
}
get_bounding_rect(&rect, dst->x, dst->y, dst->width, dst->height);
clip_visrect(hdc, &dst->visrect, &rect);
if (!src) return;
rect.left = src->log_x;
rect.top = src->log_y;
rect.right = src->log_x + src->log_width;
rect.bottom = src->log_y + src->log_height;
translate(&rect, xform);
src->x = rect.left;
src->y = rect.top;
src->width = rect.right - rect.left;
src->height = rect.bottom - rect.top;
get_bounding_rect(&rect, src->x, src->y, src->width, src->height);
if (rect.left < 0) rect.left = 0;
if (rect.top < 0) rect.top = 0;
if (rect.right > width) rect.right = width;
if (rect.bottom > height) rect.bottom = height;
src->visrect = rect;
intersect_vis_rectangles(dst, src);
}
static int stretch_blt(print_ctx *ctx, const EMRSTRETCHBLT *blt,
const BITMAPINFO *bi, const BYTE *src_bits)
{
char dst_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
BITMAPINFO *dst_info = (BITMAPINFO *)dst_buffer;
struct ps_bitblt_coords src, dst;
struct ps_image_bits bits;
DWORD err;
dst.log_x = blt->xDest;
dst.log_y = blt->yDest;
dst.log_width = blt->cxDest;
dst.log_height = blt->cyDest;
dst.layout = GetLayout(ctx->hdc);
if (blt->dwRop & NOMIRRORBITMAP)
dst.layout |= LAYOUT_BITMAPORIENTATIONPRESERVED;
if (!blt->cbBmiSrc)
{
get_vis_rectangles(ctx->hdc, &dst, NULL, 0, 0, NULL);
return PSDRV_PatBlt(ctx, &dst, blt->dwRop);
}
src.log_x = blt->xSrc;
src.log_y = blt->ySrc;
src.log_width = blt->cxSrc;
src.log_height = blt->cySrc;
src.layout = 0;
get_vis_rectangles(ctx->hdc, &dst, &blt->xformSrc,
bi->bmiHeader.biWidth, abs(bi->bmiHeader.biHeight), &src);
memcpy(dst_info, bi, blt->cbBmiSrc);
memset(&bits, 0, sizeof(bits));
bits.ptr = (BYTE *)src_bits;
err = PSDRV_PutImage(ctx, 0, dst_info, &bits, &src, &dst, blt->dwRop);
if (err == ERROR_BAD_FORMAT)
{
HDC hdc = CreateCompatibleDC(NULL);
HBITMAP bitmap;
bits.is_copy = TRUE;
bitmap = CreateDIBSection(hdc, dst_info, DIB_RGB_COLORS, &bits.ptr, NULL, 0);
SetDIBits(hdc, bitmap, 0, bi->bmiHeader.biHeight, src_bits, bi, blt->iUsageSrc);
err = PSDRV_PutImage(ctx, 0, dst_info, &bits, &src, &dst, blt->dwRop);
DeleteObject(bitmap);
DeleteObject(hdc);
}
if (err != ERROR_SUCCESS)
FIXME("PutImage returned %ld\n", err);
return !err;
}
#define FRGND_ROP3(ROP4) ((ROP4) & 0x00FFFFFF)
#define BKGND_ROP3(ROP4) (ROP3Table[((ROP4)>>24) & 0xFF])
static int mask_blt(print_ctx *ctx, const EMRMASKBLT *p, const BITMAPINFO *src_bi,
const BYTE *src_bits, const BITMAPINFO *mask_bi, const BYTE *mask_bits)
{
HBITMAP bmp1, old_bmp1, bmp2, old_bmp2, bmp_src, old_bmp_src;
char bmp2_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
BITMAPINFO *bmp2_info = (BITMAPINFO *)bmp2_buffer;
HBRUSH brush_mask, brush_dest, old_brush;
HDC hdc_src, hdc1, hdc2;
BYTE *bits;
EMRSTRETCHBLT blt;
static const DWORD ROP3Table[256] =
{
0x00000042, 0x00010289,
0x00020C89, 0x000300AA,
0x00040C88, 0x000500A9,
0x00060865, 0x000702C5,
0x00080F08, 0x00090245,
0x000A0329, 0x000B0B2A,
0x000C0324, 0x000D0B25,
0x000E08A5, 0x000F0001,
0x00100C85, 0x001100A6,
0x00120868, 0x001302C8,
0x00140869, 0x001502C9,
0x00165CCA, 0x00171D54,
0x00180D59, 0x00191CC8,
0x001A06C5, 0x001B0768,
0x001C06CA, 0x001D0766,
0x001E01A5, 0x001F0385,
0x00200F09, 0x00210248,
0x00220326, 0x00230B24,
0x00240D55, 0x00251CC5,
0x002606C8, 0x00271868,
0x00280369, 0x002916CA,
0x002A0CC9, 0x002B1D58,
0x002C0784, 0x002D060A,
0x002E064A, 0x002F0E2A,
0x0030032A, 0x00310B28,
0x00320688, 0x00330008,
0x003406C4, 0x00351864,
0x003601A8, 0x00370388,
0x0038078A, 0x00390604,
0x003A0644, 0x003B0E24,
0x003C004A, 0x003D18A4,
0x003E1B24, 0x003F00EA,
0x00400F0A, 0x00410249,
0x00420D5D, 0x00431CC4,
0x00440328, 0x00450B29,
0x004606C6, 0x0047076A,
0x00480368, 0x004916C5,
0x004A0789, 0x004B0605,
0x004C0CC8, 0x004D1954,
0x004E0645, 0x004F0E25,
0x00500325, 0x00510B26,
0x005206C9, 0x00530764,
0x005408A9, 0x00550009,
0x005601A9, 0x00570389,
0x00580785, 0x00590609,
0x005A0049, 0x005B18A9,
0x005C0649, 0x005D0E29,
0x005E1B29, 0x005F00E9,
0x00600365, 0x006116C6,
0x00620786, 0x00630608,
0x00640788, 0x00650606,
0x00660046, 0x006718A8,
0x006858A6, 0x00690145,
0x006A01E9, 0x006B178A,
0x006C01E8, 0x006D1785,
0x006E1E28, 0x006F0C65,
0x00700CC5, 0x00711D5C,
0x00720648, 0x00730E28,
0x00740646, 0x00750E26,
0x00761B28, 0x007700E6,
0x007801E5, 0x00791786,
0x007A1E29, 0x007B0C68,
0x007C1E24, 0x007D0C69,
0x007E0955, 0x007F03C9,
0x008003E9, 0x00810975,
0x00820C49, 0x00831E04,
0x00840C48, 0x00851E05,
0x008617A6, 0x008701C5,
0x008800C6, 0x00891B08,
0x008A0E06, 0x008B0666,
0x008C0E08, 0x008D0668,
0x008E1D7C, 0x008F0CE5,
0x00900C45, 0x00911E08,
0x009217A9, 0x009301C4,
0x009417AA, 0x009501C9,
0x00960169, 0x0097588A,
0x00981888, 0x00990066,
0x009A0709, 0x009B07A8,
0x009C0704, 0x009D07A6,
0x009E16E6, 0x009F0345,
0x00A000C9, 0x00A11B05,
0x00A20E09, 0x00A30669,
0x00A41885, 0x00A50065,
0x00A60706, 0x00A707A5,
0x00A803A9, 0x00A90189,
0x00AA0029, 0x00AB0889,
0x00AC0744, 0x00AD06E9,
0x00AE0B06, 0x00AF0229,
0x00B00E05, 0x00B10665,
0x00B21974, 0x00B30CE8,
0x00B4070A, 0x00B507A9,
0x00B616E9, 0x00B70348,
0x00B8074A, 0x00B906E6,
0x00BA0B09, 0x00BB0226,
0x00BC1CE4, 0x00BD0D7D,
0x00BE0269, 0x00BF08C9,
0x00C000CA, 0x00C11B04,
0x00C21884, 0x00C3006A,
0x00C40E04, 0x00C50664,
0x00C60708, 0x00C707AA,
0x00C803A8, 0x00C90184,
0x00CA0749, 0x00CB06E4,
0x00CC0020, 0x00CD0888,
0x00CE0B08, 0x00CF0224,
0x00D00E0A, 0x00D1066A,
0x00D20705, 0x00D307A4,
0x00D41D78, 0x00D50CE9,
0x00D616EA, 0x00D70349,
0x00D80745, 0x00D906E8,
0x00DA1CE9, 0x00DB0D75,
0x00DC0B04, 0x00DD0228,
0x00DE0268, 0x00DF08C8,
0x00E003A5, 0x00E10185,
0x00E20746, 0x00E306EA,
0x00E40748, 0x00E506E5,
0x00E61CE8, 0x00E70D79,
0x00E81D74, 0x00E95CE6,
0x00EA02E9, 0x00EB0849,
0x00EC02E8, 0x00ED0848,
0x00EE0086, 0x00EF0A08,
0x00F00021, 0x00F10885,
0x00F20B05, 0x00F3022A,
0x00F40B0A, 0x00F50225,
0x00F60265, 0x00F708C5,
0x00F802E5, 0x00F90845,
0x00FA0089, 0x00FB0A09,
0x00FC008A, 0x00FD0A0A,
0x00FE02A9, 0x00FF0062,
};
if (!p->cbBmiMask)
{
blt.rclBounds = p->rclBounds;
blt.xDest = p->xDest;
blt.yDest = p->yDest;
blt.cxDest = p->cxDest;
blt.cyDest = p->cyDest;
blt.dwRop = FRGND_ROP3(p->dwRop);
blt.xSrc = p->xSrc;
blt.ySrc = p->ySrc;
blt.xformSrc = p->xformSrc;
blt.crBkColorSrc = p->crBkColorSrc;
blt.iUsageSrc = p->iUsageSrc;
blt.offBmiSrc = 0;
blt.cbBmiSrc = p->cbBmiSrc;
blt.offBitsSrc = 0;
blt.cbBitsSrc = p->cbBitsSrc;
blt.cxSrc = p->cxDest;
blt.cySrc = p->cyDest;
return stretch_blt(ctx, &blt, src_bi, src_bits);
}
hdc_src = CreateCompatibleDC(NULL);
SetGraphicsMode(hdc_src, GM_ADVANCED);
SetWorldTransform(hdc_src, &p->xformSrc);
brush_dest = CreateSolidBrush(p->crBkColorSrc);
old_brush = SelectObject(hdc_src, brush_dest);
PatBlt(hdc_src, p->rclBounds.left, p->rclBounds.top,
p->rclBounds.right - p->rclBounds.left,
p->rclBounds.bottom - p->rclBounds.top, PATCOPY);
SelectObject(hdc_src, old_brush);
DeleteObject(brush_dest);
bmp_src = CreateDIBSection(hdc_src, src_bi, p->iUsageSrc, (void **)&bits, NULL, 0);
memcpy(bits, src_bits, p->cbBitsSrc);
old_bmp_src = SelectObject(hdc_src, bmp_src);
bmp1 = CreateBitmap(mask_bi->bmiHeader.biWidth, mask_bi->bmiHeader.biHeight, 1, 1, NULL);
SetDIBits(ctx->hdc, bmp1, 0, mask_bi->bmiHeader.biHeight, mask_bits, mask_bi, p->iUsageMask);
brush_mask = CreatePatternBrush(bmp1);
DeleteObject(bmp1);
brush_dest = SelectObject(ctx->hdc, GetStockObject(NULL_BRUSH));
/* make bitmap */
hdc1 = CreateCompatibleDC(NULL);
bmp1 = CreateBitmap(p->cxDest, p->cyDest, 1, 32, NULL);
old_bmp1 = SelectObject(hdc1, bmp1);
/* draw using bkgnd rop */
old_brush = SelectObject(hdc1, brush_dest);
BitBlt(hdc1, 0, 0, p->cxDest, p->cyDest, hdc_src, p->xSrc, p->ySrc, BKGND_ROP3(p->dwRop));
SelectObject(hdc1, old_brush);
/* make bitmap */
hdc2 = CreateCompatibleDC(NULL);
bmp2_info->bmiHeader.biSize = sizeof(bmp2_info->bmiHeader);
bmp2_info->bmiHeader.biWidth = p->cxDest;
bmp2_info->bmiHeader.biHeight = p->cyDest;
bmp2_info->bmiHeader.biPlanes = 1;
bmp2_info->bmiHeader.biBitCount = 32;
bmp2_info->bmiHeader.biCompression = BI_RGB;
bmp2_info->bmiHeader.biSizeImage = p->cxDest * p->cyDest * bmp2_info->bmiHeader.biBitCount / 8;
bmp2 = CreateDIBSection(hdc2, bmp2_info, DIB_RGB_COLORS, (void **)&bits, NULL, 0);
old_bmp2 = SelectObject(hdc2, bmp2);
/* draw using foregnd rop */
old_brush = SelectObject(hdc2, brush_dest);
BitBlt(hdc2, 0, 0, p->cxDest, p->cyDest, hdc_src, p->xSrc, p->ySrc, FRGND_ROP3(p->dwRop));
/* combine both using the mask as a pattern brush */
SelectObject(hdc2, brush_mask);
SetBrushOrgEx(hdc2, -p->xMask, -p->yMask, NULL);
/* (D & P) | (S & ~P) */
BitBlt(hdc2, 0, 0, p->cxDest, p->cyDest, hdc1, 0, 0, 0xac0744);
SelectObject(hdc2, old_brush);
/* blit to dst */
blt.rclBounds = p->rclBounds;
blt.xDest = p->xDest;
blt.yDest = p->yDest;
blt.cxDest = p->cxDest;
blt.cyDest = p->cyDest;
blt.dwRop = SRCCOPY;
blt.xSrc = 0;
blt.ySrc = 0;
GetTransform(hdc2, 0x204, &blt.xformSrc);
blt.crBkColorSrc = p->crBkColorSrc;
blt.iUsageSrc = DIB_RGB_COLORS;
blt.offBmiSrc = 0;
blt.cbBmiSrc = bmp2_info->bmiHeader.biSize;
blt.offBitsSrc = 0;
blt.cbBitsSrc = bmp2_info->bmiHeader.biSizeImage;
blt.cxSrc = p->cxDest;
blt.cySrc = p->cyDest;
stretch_blt(ctx, &blt, bmp2_info, bits);
/* restore all objects */
SelectObject(ctx->hdc, brush_dest);
SelectObject(hdc1, old_bmp1);
SelectObject(hdc2, old_bmp2);
SelectObject(hdc_src, old_bmp_src);
/* delete all temp objects */
DeleteObject(bmp1);
DeleteObject(bmp2);
DeleteObject(bmp_src);
DeleteObject(brush_mask);
DeleteObject(hdc1);
DeleteObject(hdc2);
DeleteObject(hdc_src);
return TRUE;
}
static void combine_transform(XFORM *result, const XFORM *xform1, const XFORM *xform2)
{
XFORM r;
/* Create the result in a temporary XFORM, since result may be
* equal to xform1 or xform2 */
r.eM11 = xform1->eM11 * xform2->eM11 + xform1->eM12 * xform2->eM21;
r.eM12 = xform1->eM11 * xform2->eM12 + xform1->eM12 * xform2->eM22;
r.eM21 = xform1->eM21 * xform2->eM11 + xform1->eM22 * xform2->eM21;
r.eM22 = xform1->eM21 * xform2->eM12 + xform1->eM22 * xform2->eM22;
r.eDx = xform1->eDx * xform2->eM11 + xform1->eDy * xform2->eM21 + xform2->eDx;
r.eDy = xform1->eDx * xform2->eM12 + xform1->eDy * xform2->eM22 + xform2->eDy;
*result = r;
}
static int plg_blt(print_ctx *ctx, const EMRPLGBLT *p)
{
const BITMAPINFO *src_bi, *mask_bi;
const BYTE *src_bits, *mask_bits;
XFORM xf, xform_dest;
EMRMASKBLT maskblt;
/* rect coords */
POINT rect[3];
/* parallelogram coords */
POINT plg[3];
double det;
memcpy(plg, p->aptlDest, sizeof(plg));
rect[0].x = p->xSrc;
rect[0].y = p->ySrc;
rect[1].x = p->xSrc + p->cxSrc;
rect[1].y = p->ySrc;
rect[2].x = p->xSrc;
rect[2].y = p->ySrc + p->cySrc;
/* calc XFORM matrix to transform hdcDest -> hdcSrc (parallelogram to rectangle) */
/* determinant */
det = rect[1].x*(rect[2].y - rect[0].y) - rect[2].x*(rect[1].y - rect[0].y) - rect[0].x*(rect[2].y - rect[1].y);
if (fabs(det) < 1e-5)
return TRUE;
TRACE("%ld,%ld,%ldx%ld -> %ld,%ld,%ld,%ld,%ld,%ld\n", p->xSrc, p->ySrc, p->cxSrc, p->cySrc,
plg[0].x, plg[0].y, plg[1].x, plg[1].y, plg[2].x, plg[2].y);
/* X components */
xf.eM11 = (plg[1].x*(rect[2].y - rect[0].y) - plg[2].x*(rect[1].y - rect[0].y) - plg[0].x*(rect[2].y - rect[1].y)) / det;
xf.eM21 = (rect[1].x*(plg[2].x - plg[0].x) - rect[2].x*(plg[1].x - plg[0].x) - rect[0].x*(plg[2].x - plg[1].x)) / det;
xf.eDx = (rect[0].x*(rect[1].y*plg[2].x - rect[2].y*plg[1].x) -
rect[1].x*(rect[0].y*plg[2].x - rect[2].y*plg[0].x) +
rect[2].x*(rect[0].y*plg[1].x - rect[1].y*plg[0].x)
) / det;
/* Y components */
xf.eM12 = (plg[1].y*(rect[2].y - rect[0].y) - plg[2].y*(rect[1].y - rect[0].y) - plg[0].y*(rect[2].y - rect[1].y)) / det;
xf.eM22 = (rect[1].x*(plg[2].y - plg[0].y) - rect[2].x*(plg[1].y - plg[0].y) - rect[0].x*(plg[2].y - plg[1].y)) / det;
xf.eDy = (rect[0].x*(rect[1].y*plg[2].y - rect[2].y*plg[1].y) -
rect[1].x*(rect[0].y*plg[2].y - rect[2].y*plg[0].y) +
rect[2].x*(rect[0].y*plg[1].y - rect[1].y*plg[0].y)
) / det;
combine_transform(&xf, &xf, &p->xformSrc);
GetTransform(ctx->hdc, 0x203, &xform_dest);
SetWorldTransform(ctx->hdc, &xf);
/* now destination and source DCs use same coords */
maskblt.rclBounds = p->rclBounds;
maskblt.xDest = p->xSrc;
maskblt.yDest = p->ySrc;
maskblt.cxDest = p->cxSrc;
maskblt.cyDest = p->cySrc;
maskblt.dwRop = SRCCOPY;
maskblt.xSrc = p->xSrc;
maskblt.ySrc = p->ySrc;
maskblt.xformSrc = p->xformSrc;
maskblt.crBkColorSrc = p->crBkColorSrc;
maskblt.iUsageSrc = p->iUsageSrc;
maskblt.offBmiSrc = 0;
maskblt.cbBmiSrc = p->cbBmiSrc;
maskblt.offBitsSrc = 0;
maskblt.cbBitsSrc = p->cbBitsSrc;
maskblt.xMask = p->xMask;
maskblt.yMask = p->yMask;
maskblt.iUsageMask = p->iUsageMask;
maskblt.offBmiMask = 0;
maskblt.cbBmiMask = p->cbBmiMask;
maskblt.offBitsMask = 0;
maskblt.cbBitsMask = p->cbBitsMask;
src_bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiSrc);
src_bits = (BYTE *)p + p->offBitsSrc;
mask_bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiMask);
mask_bits = (BYTE *)p + p->offBitsMask;
mask_blt(ctx, &maskblt, src_bi, src_bits, mask_bi, mask_bits);
SetWorldTransform(ctx->hdc, &xform_dest);
return TRUE;
}
static inline int get_dib_stride( int width, int bpp )
{
return ((width * bpp + 31) >> 3) & ~3;
}
static inline int get_dib_image_size( const BITMAPINFO *info )
{
return get_dib_stride( info->bmiHeader.biWidth, info->bmiHeader.biBitCount )
* abs( info->bmiHeader.biHeight );
}
static int set_di_bits_to_device(print_ctx *ctx, const EMRSETDIBITSTODEVICE *p)
{
const BITMAPINFO *info = (const BITMAPINFO *)((BYTE *)p + p->offBmiSrc);
char bi_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
BITMAPINFO *bi = (BITMAPINFO *)bi_buffer;
HBITMAP bitmap, old_bitmap;
int width, height, ret;
BYTE *bits;
width = min(p->cxSrc, info->bmiHeader.biWidth);
height = min(p->cySrc, abs(info->bmiHeader.biHeight));
memset(bi_buffer, 0, sizeof(bi_buffer));
bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
bi->bmiHeader.biWidth = width;
bi->bmiHeader.biHeight = height;
bi->bmiHeader.biPlanes = 1;
if (p->iUsageSrc == DIB_PAL_COLORS && (info->bmiHeader.biBitCount == 1 ||
info->bmiHeader.biBitCount == 4 || info->bmiHeader.biBitCount == 8))
{
PALETTEENTRY pal[256];
HPALETTE hpal;
UINT i, size;
bi->bmiHeader.biBitCount = info->bmiHeader.biBitCount;
bi->bmiHeader.biClrUsed = 1 << info->bmiHeader.biBitCount;
bi->bmiHeader.biClrImportant = bi->bmiHeader.biClrUsed;
hpal = GetCurrentObject(ctx->hdc, OBJ_PAL);
size = GetPaletteEntries(hpal, 0, bi->bmiHeader.biClrUsed, pal);
for (i = 0; i < size; i++)
{
bi->bmiColors[i].rgbBlue = pal[i].peBlue;
bi->bmiColors[i].rgbGreen = pal[i].peGreen;
bi->bmiColors[i].rgbRed = pal[i].peRed;
}
}
else
{
bi->bmiHeader.biBitCount = 24;
}
bi->bmiHeader.biCompression = BI_RGB;
bitmap = CreateDIBSection(ctx->hdc, bi, DIB_RGB_COLORS, (void **)&bits, NULL, 0);
if (!bitmap)
return 1;
old_bitmap = SelectObject(ctx->hdc, bitmap);
ret = SetDIBitsToDevice(ctx->hdc, 0, 0, width, height, p->xSrc, p->ySrc,
p->iStartScan, p->cScans, (const BYTE*)p + p->offBitsSrc, info, p->iUsageSrc);
SelectObject(ctx->hdc, old_bitmap);
if (ret)
{
EMRSTRETCHBLT blt;
memset(&blt, 0, sizeof(blt));
blt.rclBounds = p->rclBounds;
blt.xDest = p->xDest;
blt.yDest = p->yDest + p->cySrc - height;
blt.cxDest = width;
blt.cyDest = ret;
blt.dwRop = SRCCOPY;
blt.xformSrc.eM11 = 1;
blt.xformSrc.eM22 = 1;
blt.iUsageSrc = DIB_RGB_COLORS;
blt.cbBmiSrc = sizeof(bi_buffer);
blt.cbBitsSrc = get_dib_image_size(bi);
blt.cxSrc = blt.cxDest;
blt.cySrc = blt.cyDest;
stretch_blt(ctx, &blt, bi, bits);
}
DeleteObject(bitmap);
return 1;
}
static int stretch_di_bits(print_ctx *ctx, const EMRSTRETCHDIBITS *p)
{
char bi_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
const BYTE *bits = (BYTE *)p + p->offBitsSrc;
BITMAPINFO *bi = (BITMAPINFO *)bi_buffer;
EMRSTRETCHBLT blt;
memcpy(bi, (BYTE *)p + p->offBmiSrc, p->cbBmiSrc);
memset(bi_buffer + p->cbBmiSrc, 0, sizeof(bi_buffer) - p->cbBmiSrc);
if (p->iUsageSrc == DIB_PAL_COLORS && (bi->bmiHeader.biBitCount == 1 ||
bi->bmiHeader.biBitCount == 4 || bi->bmiHeader.biBitCount == 8))
{
PALETTEENTRY pal[256];
HPALETTE hpal;
UINT i, size;
hpal = GetCurrentObject(ctx->hdc, OBJ_PAL);
size = GetPaletteEntries(hpal, 0, 1 << bi->bmiHeader.biBitCount, pal);
for (i = 0; i < size; i++)
{
bi->bmiColors[i].rgbBlue = pal[i].peBlue;
bi->bmiColors[i].rgbGreen = pal[i].peGreen;
bi->bmiColors[i].rgbRed = pal[i].peRed;
}
}
memset(&blt, 0, sizeof(blt));
blt.rclBounds = p->rclBounds;
blt.xDest = p->xDest;
blt.yDest = p->yDest;
blt.cxDest = p->cxDest;
blt.cyDest = p->cyDest;
blt.dwRop = p->dwRop;
blt.xSrc = p->xSrc;
blt.ySrc = abs(bi->bmiHeader.biHeight) - p->ySrc - p->cySrc;
blt.xformSrc.eM11 = 1;
blt.xformSrc.eM22 = 1;
blt.iUsageSrc = p->iUsageSrc;
blt.cbBmiSrc = sizeof(bi_buffer);
blt.cbBitsSrc = p->cbBitsSrc;
blt.cxSrc = p->cxSrc;
blt.cySrc = p->cySrc;
return stretch_blt(ctx, &blt, bi, bits);
}
static int poly_draw(print_ctx *ctx, const POINT *points, const BYTE *types, DWORD count)
{
POINT first, cur, pts[4];
DWORD i, num_pts;
/* check for valid point types */
for (i = 0; i < count; i++)
{
switch (types[i])
{
case PT_MOVETO:
case PT_LINETO | PT_CLOSEFIGURE:
case PT_LINETO:
break;
case PT_BEZIERTO:
if (i + 2 >= count) return FALSE;
if (types[i + 1] != PT_BEZIERTO) return FALSE;
if ((types[i + 2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO) return FALSE;
i += 2;
break;
default:
return FALSE;
}
}
GetCurrentPositionEx(ctx->hdc, &cur);
first = cur;
for (i = 0; i < count; i++)
{
switch (types[i])
{
case PT_MOVETO:
first = points[i];
break;
case PT_LINETO:
case (PT_LINETO | PT_CLOSEFIGURE):
pts[0] = cur;
pts[1] = points[i];
num_pts = 2;
if (!PSDRV_PolyPolyline(ctx, pts, &num_pts, 1))
return FALSE;
break;
case PT_BEZIERTO:
pts[0] = cur;
pts[1] = points[i];
pts[2] = points[i + 1];
pts[3] = points[i + 2];
if (!PSDRV_PolyBezier(ctx, pts, 4))
return FALSE;
i += 2;
break;
}
cur = points[i];
if (types[i] & PT_CLOSEFIGURE)
{
pts[0] = cur;
pts[1] = first;
num_pts = 2;
if (!PSDRV_PolyPolyline(ctx, pts, &num_pts, 1))
return FALSE;
}
}
return TRUE;
}
static inline void reset_bounds(RECT *bounds)
{
bounds->left = bounds->top = INT_MAX;
bounds->right = bounds->bottom = INT_MIN;
}
static BOOL gradient_fill(print_ctx *ctx, const TRIVERTEX *vert_array, DWORD nvert,
const void *grad_array, DWORD ngrad, ULONG mode)
{
char buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
BITMAPINFO *info = (BITMAPINFO *)buffer;
struct ps_bitblt_coords src, dst;
struct ps_image_bits bits;
HBITMAP bmp, old_bmp;
BOOL ret = FALSE;
TRIVERTEX *pts;
unsigned int i;
HDC hdc_src;
HRGN rgn;
if (!(pts = malloc(nvert * sizeof(*pts)))) return FALSE;
memcpy(pts, vert_array, sizeof(*pts) * nvert);
for (i = 0; i < nvert; i++)
LPtoDP(ctx->hdc, (POINT *)&pts[i], 1);
/* compute bounding rect of all the rectangles/triangles */
reset_bounds(&dst.visrect);
for (i = 0; i < ngrad * (mode == GRADIENT_FILL_TRIANGLE ? 3 : 2); i++)
{
ULONG v = ((ULONG *)grad_array)[i];
dst.visrect.left = min(dst.visrect.left, pts[v].x);
dst.visrect.top = min(dst.visrect.top, pts[v].y);
dst.visrect.right = max(dst.visrect.right, pts[v].x);
dst.visrect.bottom = max(dst.visrect.bottom, pts[v].y);
}
dst.x = dst.visrect.left;
dst.y = dst.visrect.top;
dst.width = dst.visrect.right - dst.visrect.left;
dst.height = dst.visrect.bottom - dst.visrect.top;
clip_visrect(ctx->hdc, &dst.visrect, &dst.visrect);
info->bmiHeader.biSize = sizeof(info->bmiHeader);
info->bmiHeader.biPlanes = 1;
info->bmiHeader.biBitCount = 24;
info->bmiHeader.biCompression = BI_RGB;
info->bmiHeader.biXPelsPerMeter = 0;
info->bmiHeader.biYPelsPerMeter = 0;
info->bmiHeader.biClrUsed = 0;
info->bmiHeader.biClrImportant = 0;
info->bmiHeader.biWidth = dst.visrect.right - dst.visrect.left;
info->bmiHeader.biHeight = dst.visrect.bottom - dst.visrect.top;
info->bmiHeader.biSizeImage = 0;
memset(&bits, 0, sizeof(bits));
hdc_src = CreateCompatibleDC(NULL);
if (!hdc_src)
{
free(pts);
return FALSE;
}
bmp = CreateDIBSection(hdc_src, info, DIB_RGB_COLORS, &bits.ptr, NULL, 0);
if (!bmp)
{
DeleteObject(hdc_src);
free(pts);
return FALSE;
}
old_bmp = SelectObject(hdc_src, bmp);
/* make src and points relative to the bitmap */
src = dst;
src.x -= dst.visrect.left;
src.y -= dst.visrect.top;
OffsetRect(&src.visrect, -dst.visrect.left, -dst.visrect.top);
for (i = 0; i < nvert; i++)
{
pts[i].x -= dst.visrect.left;
pts[i].y -= dst.visrect.top;
}
ret = GdiGradientFill(hdc_src, pts, nvert, (void *)grad_array, ngrad, mode);
SelectObject(hdc_src, old_bmp);
DeleteObject(hdc_src);
rgn = CreateRectRgn(0, 0, 0, 0);
if (mode == GRADIENT_FILL_TRIANGLE)
{
const GRADIENT_TRIANGLE *gt = grad_array;
POINT triangle[3];
HRGN tmp;
for (i = 0; i < ngrad; i++)
{
triangle[0].x = pts[gt[i].Vertex1].x;
triangle[0].y = pts[gt[i].Vertex1].y;
triangle[1].x = pts[gt[i].Vertex2].x;
triangle[1].y = pts[gt[i].Vertex2].y;
triangle[2].x = pts[gt[i].Vertex3].x;
triangle[2].y = pts[gt[i].Vertex3].y;
tmp = CreatePolygonRgn(triangle, 3, ALTERNATE);
CombineRgn(rgn, rgn, tmp, RGN_OR);
DeleteObject(tmp);
}
}
else
{
const GRADIENT_RECT *gr = grad_array;
HRGN tmp = CreateRectRgn(0, 0, 0, 0);
for (i = 0; i < ngrad; i++)
{
SetRectRgn(tmp, pts[gr[i].UpperLeft].x, pts[gr[i].UpperLeft].y,
pts[gr[i].LowerRight].x, pts[gr[i].LowerRight].y);
CombineRgn(rgn, rgn, tmp, RGN_OR);
}
DeleteObject(tmp);
}
free(pts);
OffsetRgn(rgn, dst.visrect.left, dst.visrect.top);
if (ret)
ret = (PSDRV_PutImage(ctx, rgn, info, &bits, &src, &dst, SRCCOPY) == ERROR_SUCCESS);
DeleteObject(rgn);
DeleteObject(bmp);
return ret;
}
static HGDIOBJ get_object_handle(struct pp_data *data, HANDLETABLE *handletable,
DWORD i, struct ps_brush_pattern **pattern)
{
if (i & 0x80000000)
{
*pattern = NULL;
return GetStockObject(i & 0x7fffffff);
}
*pattern = data->patterns + i;
return handletable->objectHandle[i];
}
static BOOL select_hbrush(struct pp_data *data, HANDLETABLE *htable, int handle_count, HBRUSH brush)
{
struct ps_brush_pattern *pattern = NULL;
int i;
for (i = 0; i < handle_count; i++)
{
if (htable->objectHandle[i] == brush)
{
pattern = data->patterns + i;
break;
}
}
return PSDRV_SelectBrush(data->ctx, brush, pattern) != NULL;
}
/* Performs a device to world transformation on the specified size (which
* is in integer format).
*/
static inline INT INTERNAL_YWSTODS(HDC hdc, INT height)
{
POINT pt[2];
pt[0].x = pt[0].y = 0;
pt[1].x = 0;
pt[1].y = height;
LPtoDP(hdc, pt, 2);
return pt[1].y - pt[0].y;
}
extern const unsigned short bidi_direction_table[];
/*------------------------------------------------------------------------
Bidirectional Character Types
as defined by the Unicode Bidirectional Algorithm Table 3-7.
Note:
The list of bidirectional character types here is not grouped the
same way as the table 3-7, since the numeric values for the types
are chosen to keep the state and action tables compact.
------------------------------------------------------------------------*/
enum directions
{
/* input types */
/* ON MUST be zero, code relies on ON = N = 0 */
ON = 0, /* Other Neutral */
L, /* Left Letter */
R, /* Right Letter */
AN, /* Arabic Number */
EN, /* European Number */
AL, /* Arabic Letter (Right-to-left) */
NSM, /* Non-spacing Mark */
CS, /* Common Separator */
ES, /* European Separator */
ET, /* European Terminator (post/prefix e.g. $ and %) */
/* resolved types */
BN, /* Boundary neutral (type of RLE etc after explicit levels) */
/* input types, */
S, /* Segment Separator (TAB) // used only in L1 */
WS, /* White space // used only in L1 */
B, /* Paragraph Separator (aka as PS) */
/* types for explicit controls */
RLO, /* these are used only in X1-X9 */
RLE,
LRO,
LRE,
PDF,
LRI, /* Isolate formatting characters new with 6.3 */
RLI,
FSI,
PDI,
/* resolved types, also resolved directions */
NI = ON, /* alias, where ON, WS and S are treated the same */
};
static inline unsigned short get_table_entry_32(const unsigned short *table, UINT ch)
{
return table[table[table[table[ch >> 12] + ((ch >> 8) & 0x0f)] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
}
/* Convert the libwine information to the direction enum */
static void classify(LPCWSTR lpString, WORD *chartype, DWORD uCount)
{
unsigned i;
for (i = 0; i < uCount; ++i)
chartype[i] = get_table_entry_32(bidi_direction_table, lpString[i]);
}
/* Set a run of cval values at locations all prior to, but not including */
/* iStart, to the new value nval. */
static void SetDeferredRun(BYTE *pval, int cval, int iStart, int nval)
{
int i = iStart - 1;
for (; i >= iStart - cval; i--)
{
pval[i] = nval;
}
}
/* THE PARAGRAPH LEVEL */
/*------------------------------------------------------------------------
Function: resolveParagraphs
Resolves the input strings into blocks over which the algorithm
is then applied.
Implements Rule P1 of the Unicode Bidi Algorithm
Input: Text string
Character count
Output: revised character count
Note: This is a very simplistic function. In effect it restricts
the action of the algorithm to the first paragraph in the input
where a paragraph ends at the end of the first block separator
or at the end of the input text.
------------------------------------------------------------------------*/
static int resolveParagraphs(WORD *types, int cch)
{
/* skip characters not of type B */
int ich = 0;
for(; ich < cch && types[ich] != B; ich++);
/* stop after first B, make it a BN for use in the next steps */
if (ich < cch && types[ich] == B)
types[ich++] = BN;
return ich;
}
/* REORDER */
/*------------------------------------------------------------------------
Function: resolveLines
Breaks a paragraph into lines
Input: Array of line break flags
Character count
In/Out: Array of characters
Returns the count of characters on the first line
Note: This function only breaks lines at hard line breaks. Other
line breaks can be passed in. If pbrk[n] is TRUE, then a break
occurs after the character in pszInput[n]. Breaks before the first
character are not allowed.
------------------------------------------------------------------------*/
static int resolveLines(LPCWSTR pszInput, const BOOL * pbrk, int cch)
{
/* skip characters not of type LS */
int ich = 0;
for(; ich < cch; ich++)
{
if (pszInput[ich] == (WCHAR)'\n' || (pbrk && pbrk[ich]))
{
ich++;
break;
}
}
return ich;
}
/*------------------------------------------------------------------------
Function: resolveWhiteSpace
Resolves levels for WS and S
Implements rule L1 of the Unicode bidi Algorithm.
Input: Base embedding level
Character count
Array of direction classes (for one line of text)
In/Out: Array of embedding levels (for one line of text)
Note: this should be applied a line at a time. The default driver
code supplied in this file assumes a single line of text; for
a real implementation, cch and the initial pointer values
would have to be adjusted.
------------------------------------------------------------------------*/
static void resolveWhitespace(int baselevel, const WORD *pcls, BYTE *plevel, int cch)
{
int cchrun = 0;
BYTE oldlevel = baselevel;
int ich = 0;
for (; ich < cch; ich++)
{
switch(pcls[ich])
{
default:
cchrun = 0; /* any other character breaks the run */
break;
case WS:
cchrun++;
break;
case RLE:
case LRE:
case LRO:
case RLO:
case PDF:
case LRI:
case RLI:
case FSI:
case PDI:
case BN:
plevel[ich] = oldlevel;
cchrun++;
break;
case S:
case B:
/* reset levels for WS before eot */
SetDeferredRun(plevel, cchrun, ich, baselevel);
cchrun = 0;
plevel[ich] = baselevel;
break;
}
oldlevel = plevel[ich];
}
/* reset level before eot */
SetDeferredRun(plevel, cchrun, ich, baselevel);
}
/*------------------------------------------------------------------------
Function: BidiLines
Implements the Line-by-Line phases of the Unicode Bidi Algorithm
Input: Count of characters
Array of character directions
Inp/Out: Input text
Array of levels
------------------------------------------------------------------------*/
static void BidiLines(int baselevel, LPWSTR pszOutLine, LPCWSTR pszLine, const WORD * pclsLine,
BYTE * plevelLine, int cchPara, const BOOL * pbrk)
{
int cchLine = 0;
int done = 0;
int *run;
run = HeapAlloc(GetProcessHeap(), 0, cchPara * sizeof(int));
if (!run)
{
WARN("Out of memory\n");
return;
}
do
{
/* break lines at LS */
cchLine = resolveLines(pszLine, pbrk, cchPara);
/* resolve whitespace */
resolveWhitespace(baselevel, pclsLine, plevelLine, cchLine);
if (pszOutLine)
{
int i;
/* reorder each line in place */
ScriptLayout(cchLine, plevelLine, NULL, run);
for (i = 0; i < cchLine; i++)
pszOutLine[done+run[i]] = pszLine[i];
}
pszLine += cchLine;
plevelLine += cchLine;
pbrk += pbrk ? cchLine : 0;
pclsLine += cchLine;
cchPara -= cchLine;
done += cchLine;
} while (cchPara);
HeapFree(GetProcessHeap(), 0, run);
}
#define WINE_GCPW_FORCE_LTR 0
#define WINE_GCPW_FORCE_RTL 1
#define WINE_GCPW_DIR_MASK 3
static BOOL BIDI_Reorder(HDC hDC, /* [in] Display DC */
LPCWSTR lpString, /* [in] The string for which information is to be returned */
INT uCount, /* [in] Number of WCHARs in string. */
DWORD dwFlags, /* [in] GetCharacterPlacement compatible flags */
DWORD dwWineGCP_Flags, /* [in] Wine internal flags - Force paragraph direction */
LPWSTR lpOutString, /* [out] Reordered string */
INT uCountOut, /* [in] Size of output buffer */
UINT *lpOrder, /* [out] Logical -> Visual order map */
WORD **lpGlyphs, /* [out] reordered, mirrored, shaped glyphs to display */
INT *cGlyphs) /* [out] number of glyphs generated */
{
WORD *chartype = NULL;
BYTE *levels = NULL;
INT i, done;
unsigned glyph_i;
BOOL is_complex, ret = FALSE;
int maxItems;
int nItems;
SCRIPT_CONTROL Control;
SCRIPT_STATE State;
SCRIPT_ITEM *pItems = NULL;
HRESULT res;
SCRIPT_CACHE psc = NULL;
WORD *run_glyphs = NULL;
WORD *pwLogClust = NULL;
SCRIPT_VISATTR *psva = NULL;
DWORD cMaxGlyphs = 0;
BOOL doGlyphs = TRUE;
TRACE("%s, %d, 0x%08lx lpOutString=%p, lpOrder=%p\n",
debugstr_wn(lpString, uCount), uCount, dwFlags,
lpOutString, lpOrder);
memset(&Control, 0, sizeof(Control));
memset(&State, 0, sizeof(State));
if (lpGlyphs)
*lpGlyphs = NULL;
if (!(dwFlags & GCP_REORDER))
{
FIXME("Asked to reorder without reorder flag set\n");
return FALSE;
}
if (lpOutString && uCountOut < uCount)
{
FIXME("lpOutString too small\n");
return FALSE;
}
chartype = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(WORD));
if (!chartype)
{
WARN("Out of memory\n");
return FALSE;
}
if (lpOutString)
memcpy(lpOutString, lpString, uCount * sizeof(WCHAR));
is_complex = FALSE;
for (i = 0; i < uCount && !is_complex; i++)
{
if ((lpString[i] >= 0x900 && lpString[i] <= 0xfff) ||
(lpString[i] >= 0x1cd0 && lpString[i] <= 0x1cff) ||
(lpString[i] >= 0xa840 && lpString[i] <= 0xa8ff))
is_complex = TRUE;
}
/* Verify reordering will be required */
if (WINE_GCPW_FORCE_RTL == (dwWineGCP_Flags & WINE_GCPW_DIR_MASK))
State.uBidiLevel = 1;
else if (!is_complex)
{
done = 1;
classify(lpString, chartype, uCount);
for (i = 0; i < uCount; i++)
switch (chartype[i])
{
case R:
case AL:
case RLE:
case RLO:
done = 0;
break;
}
if (done)
{
HeapFree(GetProcessHeap(), 0, chartype);
if (lpOrder)
{
for (i = 0; i < uCount; i++)
lpOrder[i] = i;
}
return TRUE;
}
}
levels = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(BYTE));
if (!levels)
{
WARN("Out of memory\n");
goto cleanup;
}
maxItems = 5;
pItems = HeapAlloc(GetProcessHeap(),0, maxItems * sizeof(SCRIPT_ITEM));
if (!pItems)
{
WARN("Out of memory\n");
goto cleanup;
}
if (lpGlyphs)
{
cMaxGlyphs = 1.5 * uCount + 16;
run_glyphs = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * cMaxGlyphs);
if (!run_glyphs)
{
WARN("Out of memory\n");
goto cleanup;
}
pwLogClust = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * uCount);
if (!pwLogClust)
{
WARN("Out of memory\n");
goto cleanup;
}
psva = HeapAlloc(GetProcessHeap(),0,sizeof(SCRIPT_VISATTR) * cMaxGlyphs);
if (!psva)
{
WARN("Out of memory\n");
goto cleanup;
}
}
done = 0;
glyph_i = 0;
while (done < uCount)
{
INT j;
classify(lpString + done, chartype, uCount - done);
/* limit text to first block */
i = resolveParagraphs(chartype, uCount - done);
for (j = 0; j < i; ++j)
switch(chartype[j])
{
case B:
case S:
case WS:
case ON: chartype[j] = NI;
default: continue;
}
res = ScriptItemize(lpString + done, i, maxItems, &Control, &State, pItems, &nItems);
while (res == E_OUTOFMEMORY)
{
SCRIPT_ITEM *new_pItems = HeapReAlloc(GetProcessHeap(), 0, pItems, sizeof(*pItems) * maxItems * 2);
if (!new_pItems)
{
WARN("Out of memory\n");
goto cleanup;
}
pItems = new_pItems;
maxItems *= 2;
res = ScriptItemize(lpString + done, i, maxItems, &Control, &State, pItems, &nItems);
}
if (lpOutString || lpOrder)
for (j = 0; j < nItems; j++)
{
int k;
for (k = pItems[j].iCharPos; k < pItems[j+1].iCharPos; k++)
levels[k] = pItems[j].a.s.uBidiLevel;
}
if (lpOutString)
{
/* assign directional types again, but for WS, S this time */
classify(lpString + done, chartype, i);
BidiLines(State.uBidiLevel, lpOutString + done, lpString + done,
chartype, levels, i, 0);
}
if (lpOrder)
{
int k, lastgood;
for (j = lastgood = 0; j < i; ++j)
if (levels[j] != levels[lastgood])
{
--j;
if (levels[lastgood] & 1)
for (k = j; k >= lastgood; --k)
lpOrder[done + k] = done + j - k;
else
for (k = lastgood; k <= j; ++k)
lpOrder[done + k] = done + k;
lastgood = ++j;
}
if (levels[lastgood] & 1)
for (k = j - 1; k >= lastgood; --k)
lpOrder[done + k] = done + j - 1 - k;
else
for (k = lastgood; k < j; ++k)
lpOrder[done + k] = done + k;
}
if (lpGlyphs && doGlyphs)
{
BYTE *runOrder;
int *visOrder;
SCRIPT_ITEM *curItem;
runOrder = HeapAlloc(GetProcessHeap(), 0, maxItems * sizeof(*runOrder));
visOrder = HeapAlloc(GetProcessHeap(), 0, maxItems * sizeof(*visOrder));
if (!runOrder || !visOrder)
{
WARN("Out of memory\n");
HeapFree(GetProcessHeap(), 0, runOrder);
HeapFree(GetProcessHeap(), 0, visOrder);
goto cleanup;
}
for (j = 0; j < nItems; j++)
runOrder[j] = pItems[j].a.s.uBidiLevel;
ScriptLayout(nItems, runOrder, visOrder, NULL);
for (j = 0; j < nItems; j++)
{
int k;
int cChars,cOutGlyphs;
curItem = &pItems[visOrder[j]];
cChars = pItems[visOrder[j]+1].iCharPos - curItem->iCharPos;
res = ScriptShape(hDC, &psc, lpString + done + curItem->iCharPos, cChars, cMaxGlyphs, &curItem->a, run_glyphs, pwLogClust, psva, &cOutGlyphs);
while (res == E_OUTOFMEMORY)
{
WORD *new_run_glyphs = HeapReAlloc(GetProcessHeap(), 0, run_glyphs, sizeof(*run_glyphs) * cMaxGlyphs * 2);
SCRIPT_VISATTR *new_psva = HeapReAlloc(GetProcessHeap(), 0, psva, sizeof(*psva) * cMaxGlyphs * 2);
if (!new_run_glyphs || !new_psva)
{
WARN("Out of memory\n");
HeapFree(GetProcessHeap(), 0, runOrder);
HeapFree(GetProcessHeap(), 0, visOrder);
HeapFree(GetProcessHeap(), 0, *lpGlyphs);
*lpGlyphs = NULL;
if (new_run_glyphs)
run_glyphs = new_run_glyphs;
if (new_psva)
psva = new_psva;
goto cleanup;
}
run_glyphs = new_run_glyphs;
psva = new_psva;
cMaxGlyphs *= 2;
res = ScriptShape(hDC, &psc, lpString + done + curItem->iCharPos, cChars, cMaxGlyphs, &curItem->a, run_glyphs, pwLogClust, psva, &cOutGlyphs);
}
if (res)
{
if (res == USP_E_SCRIPT_NOT_IN_FONT)
TRACE("Unable to shape with currently selected font\n");
else
FIXME("Unable to shape string (%lx)\n",res);
j = nItems;
doGlyphs = FALSE;
HeapFree(GetProcessHeap(), 0, *lpGlyphs);
*lpGlyphs = NULL;
}
else
{
WORD *new_glyphs;
if (*lpGlyphs)
new_glyphs = HeapReAlloc(GetProcessHeap(), 0, *lpGlyphs, sizeof(**lpGlyphs) * (glyph_i + cOutGlyphs));
else
new_glyphs = HeapAlloc(GetProcessHeap(), 0, sizeof(**lpGlyphs) * (glyph_i + cOutGlyphs));
if (!new_glyphs)
{
WARN("Out of memory\n");
HeapFree(GetProcessHeap(), 0, runOrder);
HeapFree(GetProcessHeap(), 0, visOrder);
HeapFree(GetProcessHeap(), 0, *lpGlyphs);
*lpGlyphs = NULL;
goto cleanup;
}
*lpGlyphs = new_glyphs;
for (k = 0; k < cOutGlyphs; k++)
(*lpGlyphs)[glyph_i+k] = run_glyphs[k];
glyph_i += cOutGlyphs;
}
}
HeapFree(GetProcessHeap(), 0, runOrder);
HeapFree(GetProcessHeap(), 0, visOrder);
}
done += i;
}
if (cGlyphs)
*cGlyphs = glyph_i;
ret = TRUE;
cleanup:
HeapFree(GetProcessHeap(), 0, chartype);
HeapFree(GetProcessHeap(), 0, levels);
HeapFree(GetProcessHeap(), 0, pItems);
HeapFree(GetProcessHeap(), 0, run_glyphs);
HeapFree(GetProcessHeap(), 0, pwLogClust);
HeapFree(GetProcessHeap(), 0, psva);
ScriptFreeCache(&psc);
return ret;
}
static inline BOOL intersect_rect(RECT *dst, const RECT *src1, const RECT *src2)
{
dst->left = max(src1->left, src2->left);
dst->top = max(src1->top, src2->top);
dst->right = min(src1->right, src2->right);
dst->bottom = min(src1->bottom, src2->bottom);
return !IsRectEmpty(dst);
}
/***********************************************************************
* get_line_width
*
* Scale the underline / strikeout line width.
*/
static inline int get_line_width(HDC hdc, int metric_size)
{
int width = abs(INTERNAL_YWSTODS(hdc, metric_size));
if (width == 0) width = 1;
if (metric_size < 0) width = -width;
return width;
}
static BOOL ext_text_out(struct pp_data *data, HANDLETABLE *htable,
int handle_count, INT x, INT y, UINT flags, const RECT *rect,
const WCHAR *str, UINT count, const INT *dx)
{
HDC hdc = data->ctx->hdc;
BOOL ret = FALSE;
UINT align;
DWORD layout;
POINT pt;
TEXTMETRICW tm;
LOGFONTW lf;
double cosEsc, sinEsc;
INT char_extra;
SIZE sz;
RECT rc;
POINT *deltas = NULL, width = {0, 0};
INT breakRem;
WORD *glyphs = NULL;
XFORM xform;
if (!(flags & (ETO_GLYPH_INDEX | ETO_IGNORELANGUAGE)) && count > 0)
{
int glyphs_count = 0;
UINT bidi_flags;
bidi_flags = (GetTextAlign(hdc) & TA_RTLREADING) || (flags & ETO_RTLREADING)
? WINE_GCPW_FORCE_RTL : WINE_GCPW_FORCE_LTR;
BIDI_Reorder(hdc, str, count, GCP_REORDER, bidi_flags,
NULL, 0, NULL, &glyphs, &glyphs_count);
flags |= ETO_IGNORELANGUAGE;
if (glyphs)
{
flags |= ETO_GLYPH_INDEX;
count = glyphs_count;
str = glyphs;
}
}
align = GetTextAlign(hdc);
breakRem = data->break_rem;
layout = GetLayout(hdc);
if (flags & ETO_RTLREADING) align |= TA_RTLREADING;
if (layout & LAYOUT_RTL)
{
if ((align & TA_CENTER) != TA_CENTER) align ^= TA_RIGHT;
align ^= TA_RTLREADING;
}
TRACE("%d, %d, %08x, %s, %s, %d, %p)\n", x, y, flags,
wine_dbgstr_rect(rect), debugstr_wn(str, count), count, dx);
TRACE("align = %x bkmode = %x mapmode = %x\n", align, GetBkMode(hdc),
GetMapMode(hdc));
if(align & TA_UPDATECP)
{
GetCurrentPositionEx(hdc, &pt);
x = pt.x;
y = pt.y;
}
GetTextMetricsW(hdc, &tm);
GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf);
if(!(tm.tmPitchAndFamily & TMPF_VECTOR)) /* Non-scalable fonts shouldn't be rotated */
lf.lfEscapement = 0;
GetWorldTransform(hdc, &xform);
if (GetGraphicsMode(hdc) == GM_COMPATIBLE &&
xform.eM11 * xform.eM22 < 0)
{
lf.lfEscapement = -lf.lfEscapement;
}
if(lf.lfEscapement != 0)
{
cosEsc = cos(lf.lfEscapement * M_PI / 1800);
sinEsc = sin(lf.lfEscapement * M_PI / 1800);
}
else
{
cosEsc = 1;
sinEsc = 0;
}
if (rect && (flags & (ETO_OPAQUE | ETO_CLIPPED)))
{
rc = *rect;
LPtoDP(hdc, (POINT*)&rc, 2);
order_rect(&rc);
if (flags & ETO_OPAQUE)
PSDRV_ExtTextOut(data->ctx, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
}
else flags &= ~ETO_CLIPPED;
if(count == 0)
{
ret = TRUE;
goto done;
}
pt.x = x;
pt.y = y;
LPtoDP(hdc, &pt, 1);
x = pt.x;
y = pt.y;
char_extra = GetTextCharacterExtra(hdc);
if (char_extra && dx)
char_extra = 0; /* Printer drivers don't add char_extra if dx is supplied */
if(char_extra || data->break_extra || breakRem || dx || lf.lfEscapement != 0)
{
UINT i;
POINT total = {0, 0}, desired[2];
deltas = malloc(count * sizeof(*deltas));
if (dx)
{
if (flags & ETO_PDY)
{
for (i = 0; i < count; i++)
{
deltas[i].x = dx[i * 2] + char_extra;
deltas[i].y = -dx[i * 2 + 1];
}
}
else
{
for (i = 0; i < count; i++)
{
deltas[i].x = dx[i] + char_extra;
deltas[i].y = 0;
}
}
}
else
{
INT *dx = malloc(count * sizeof(*dx));
NtGdiGetTextExtentExW(hdc, str, count, -1, NULL, dx, &sz, !!(flags & ETO_GLYPH_INDEX));
deltas[0].x = dx[0];
deltas[0].y = 0;
for (i = 1; i < count; i++)
{
deltas[i].x = dx[i] - dx[i - 1];
deltas[i].y = 0;
}
free(dx);
}
for(i = 0; i < count; i++)
{
total.x += deltas[i].x;
total.y += deltas[i].y;
desired[0].x = desired[0].y = 0;
desired[1].x = cosEsc * total.x + sinEsc * total.y;
desired[1].y = -sinEsc * total.x + cosEsc * total.y;
LPtoDP(hdc, desired, 2);
desired[1].x -= desired[0].x;
desired[1].y -= desired[0].y;
if (GetGraphicsMode(hdc) == GM_COMPATIBLE)
{
if (xform.eM11 < 0)
desired[1].x = -desired[1].x;
if (xform.eM22 < 0)
desired[1].y = -desired[1].y;
}
deltas[i].x = desired[1].x - width.x;
deltas[i].y = desired[1].y - width.y;
width = desired[1];
}
flags |= ETO_PDY;
}
else
{
POINT desired[2];
NtGdiGetTextExtentExW(hdc, str, count, 0, NULL, NULL, &sz, !!(flags & ETO_GLYPH_INDEX));
desired[0].x = desired[0].y = 0;
desired[1].x = sz.cx;
desired[1].y = 0;
LPtoDP(hdc, desired, 2);
desired[1].x -= desired[0].x;
desired[1].y -= desired[0].y;
if (GetGraphicsMode(hdc) == GM_COMPATIBLE)
{
if (xform.eM11 < 0)
desired[1].x = -desired[1].x;
if (xform.eM22 < 0)
desired[1].y = -desired[1].y;
}
width = desired[1];
}
tm.tmAscent = abs(INTERNAL_YWSTODS(hdc, tm.tmAscent));
tm.tmDescent = abs(INTERNAL_YWSTODS(hdc, tm.tmDescent));
switch(align & (TA_LEFT | TA_RIGHT | TA_CENTER))
{
case TA_LEFT:
if (align & TA_UPDATECP)
{
pt.x = x + width.x;
pt.y = y + width.y;
DPtoLP(hdc, &pt, 1);
MoveToEx(hdc, pt.x, pt.y, NULL);
}
break;
case TA_CENTER:
x -= width.x / 2;
y -= width.y / 2;
break;
case TA_RIGHT:
x -= width.x;
y -= width.y;
if (align & TA_UPDATECP)
{
pt.x = x;
pt.y = y;
DPtoLP(hdc, &pt, 1);
MoveToEx(hdc, pt.x, pt.y, NULL);
}
break;
}
switch(align & (TA_TOP | TA_BOTTOM | TA_BASELINE))
{
case TA_TOP:
y += tm.tmAscent * cosEsc;
x += tm.tmAscent * sinEsc;
break;
case TA_BOTTOM:
y -= tm.tmDescent * cosEsc;
x -= tm.tmDescent * sinEsc;
break;
case TA_BASELINE:
break;
}
if (GetBkMode(hdc) != TRANSPARENT)
{
if(!((flags & ETO_CLIPPED) && (flags & ETO_OPAQUE)))
{
if(!(flags & ETO_OPAQUE) || !rect ||
x < rc.left || x + width.x >= rc.right ||
y - tm.tmAscent < rc.top || y + tm.tmDescent >= rc.bottom)
{
RECT text_box;
text_box.left = x;
text_box.right = x + width.x;
text_box.top = y - tm.tmAscent;
text_box.bottom = y + tm.tmDescent;
if (flags & ETO_CLIPPED) intersect_rect(&text_box, &text_box, &rc);
if (!IsRectEmpty(&text_box))
PSDRV_ExtTextOut(data->ctx, 0, 0, ETO_OPAQUE, &text_box, NULL, 0, NULL);
}
}
}
ret = PSDRV_ExtTextOut(data->ctx, x, y, (flags & ~ETO_OPAQUE), &rc,
str, count, (INT*)deltas);
done:
free(deltas);
if (ret && (lf.lfUnderline || lf.lfStrikeOut))
{
int underlinePos, strikeoutPos;
int underlineWidth, strikeoutWidth;
UINT size = NtGdiGetOutlineTextMetricsInternalW(hdc, 0, NULL, 0);
OUTLINETEXTMETRICW* otm = NULL;
POINT pts[5];
HBRUSH hbrush = CreateSolidBrush(GetTextColor(hdc));
HPEN hpen = GetStockObject(NULL_PEN);
PSDRV_SelectPen(data->ctx, hpen, NULL);
hpen = SelectObject(hdc, hpen);
PSDRV_SelectBrush(data->ctx, hbrush, NULL);
hbrush = SelectObject(hdc, hbrush);
if(!size)
{
underlinePos = 0;
underlineWidth = tm.tmAscent / 20 + 1;
strikeoutPos = tm.tmAscent / 2;
strikeoutWidth = underlineWidth;
}
else
{
otm = malloc(size);
NtGdiGetOutlineTextMetricsInternalW(hdc, size, otm, 0);
underlinePos = abs(INTERNAL_YWSTODS(hdc, otm->otmsUnderscorePosition));
if (otm->otmsUnderscorePosition < 0) underlinePos = -underlinePos;
underlineWidth = get_line_width(hdc, otm->otmsUnderscoreSize);
strikeoutPos = abs(INTERNAL_YWSTODS(hdc, otm->otmsStrikeoutPosition));
if (otm->otmsStrikeoutPosition < 0) strikeoutPos = -strikeoutPos;
strikeoutWidth = get_line_width(hdc, otm->otmsStrikeoutSize);
free(otm);
}
if (lf.lfUnderline)
{
const INT cnt = 5;
pts[0].x = x - (underlinePos + underlineWidth / 2) * sinEsc;
pts[0].y = y - (underlinePos + underlineWidth / 2) * cosEsc;
pts[1].x = x + width.x - (underlinePos + underlineWidth / 2) * sinEsc;
pts[1].y = y + width.y - (underlinePos + underlineWidth / 2) * cosEsc;
pts[2].x = pts[1].x + underlineWidth * sinEsc;
pts[2].y = pts[1].y + underlineWidth * cosEsc;
pts[3].x = pts[0].x + underlineWidth * sinEsc;
pts[3].y = pts[0].y + underlineWidth * cosEsc;
pts[4].x = pts[0].x;
pts[4].y = pts[0].y;
DPtoLP(hdc, pts, 5);
PSDRV_PolyPolygon(data->ctx, pts, &cnt, 1);
}
if (lf.lfStrikeOut)
{
const INT cnt = 5;
pts[0].x = x - (strikeoutPos + strikeoutWidth / 2) * sinEsc;
pts[0].y = y - (strikeoutPos + strikeoutWidth / 2) * cosEsc;
pts[1].x = x + width.x - (strikeoutPos + strikeoutWidth / 2) * sinEsc;
pts[1].y = y + width.y - (strikeoutPos + strikeoutWidth / 2) * cosEsc;
pts[2].x = pts[1].x + strikeoutWidth * sinEsc;
pts[2].y = pts[1].y + strikeoutWidth * cosEsc;
pts[3].x = pts[0].x + strikeoutWidth * sinEsc;
pts[3].y = pts[0].y + strikeoutWidth * cosEsc;
pts[4].x = pts[0].x;
pts[4].y = pts[0].y;
DPtoLP(hdc, pts, 5);
PSDRV_PolyPolygon(data->ctx, pts, &cnt, 1);
}
PSDRV_SelectPen(data->ctx, hpen, NULL);
SelectObject(hdc, hpen);
select_hbrush(data, htable, handle_count, hbrush);
SelectObject(hdc, hbrush);
DeleteObject(hbrush);
}
HeapFree(GetProcessHeap(), 0, glyphs);
return ret;
}
static BOOL fill_rgn(struct pp_data *data, HANDLETABLE *htable, int handle_count, DWORD brush, HRGN rgn)
{
struct ps_brush_pattern *pattern;
HBRUSH hbrush;
int ret;
hbrush = get_object_handle(data, htable, brush, &pattern);
PSDRV_SelectBrush(data->ctx, hbrush, pattern);
ret = PSDRV_PaintRgn(data->ctx, rgn);
select_hbrush(data, htable, handle_count, GetCurrentObject(data->ctx->hdc, OBJ_BRUSH));
PSDRV_SelectBrush(data->ctx, hbrush, pattern);
return ret;
}
static BOOL is_path_record(int type)
{
switch(type)
{
case EMR_POLYBEZIER:
case EMR_POLYGON:
case EMR_POLYLINE:
case EMR_POLYBEZIERTO:
case EMR_POLYLINETO:
case EMR_POLYPOLYLINE:
case EMR_POLYPOLYGON:
case EMR_MOVETOEX:
case EMR_ANGLEARC:
case EMR_ELLIPSE:
case EMR_RECTANGLE:
case EMR_ROUNDRECT:
case EMR_ARC:
case EMR_CHORD:
case EMR_PIE:
case EMR_LINETO:
case EMR_ARCTO:
case EMR_POLYDRAW:
case EMR_EXTTEXTOUTA:
case EMR_EXTTEXTOUTW:
case EMR_POLYBEZIER16:
case EMR_POLYGON16:
case EMR_POLYLINE16:
case EMR_POLYBEZIERTO16:
case EMR_POLYLINETO16:
case EMR_POLYPOLYLINE16:
case EMR_POLYPOLYGON16:
case EMR_POLYDRAW16:
return TRUE;
default:
return FALSE;
}
}
static int WINAPI hmf_proc(HDC hdc, HANDLETABLE *htable,
const ENHMETARECORD *rec, int handle_count, LPARAM arg)
{
struct pp_data *data = (struct pp_data *)arg;
if (data->path && is_path_record(rec->iType))
return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
switch (rec->iType)
{
case EMR_HEADER:
{
const ENHMETAHEADER *header = (const ENHMETAHEADER *)rec;
data->patterns = calloc(sizeof(*data->patterns), header->nHandles);
return data->patterns && PSDRV_StartPage(data->ctx);
}
case EMR_POLYBEZIER:
{
const EMRPOLYBEZIER *p = (const EMRPOLYBEZIER *)rec;
return PSDRV_PolyBezier(data->ctx, (const POINT *)p->aptl, p->cptl);
}
case EMR_POLYGON:
{
const EMRPOLYGON *p = (const EMRPOLYGON *)rec;
return PSDRV_PolyPolygon(data->ctx, (const POINT *)p->aptl,
(const INT *)&p->cptl, 1);
}
case EMR_POLYLINE:
{
const EMRPOLYLINE *p = (const EMRPOLYLINE *)rec;
return PSDRV_PolyPolyline(data->ctx,
(const POINT *)p->aptl, &p->cptl, 1);
}
case EMR_POLYBEZIERTO:
{
const EMRPOLYBEZIERTO *p = (const EMRPOLYBEZIERTO *)rec;
return PSDRV_PolyBezierTo(data->ctx, (const POINT *)p->aptl, p->cptl) &&
MoveToEx(data->ctx->hdc, p->aptl[p->cptl - 1].x, p->aptl[p->cptl - 1].y, NULL);
}
case EMR_POLYLINETO:
{
const EMRPOLYLINETO *p = (const EMRPOLYLINETO *)rec;
POINT *pts;
DWORD cnt;
int ret;
cnt = p->cptl + 1;
pts = malloc(sizeof(*pts) * cnt);
if (!pts) return 0;
GetCurrentPositionEx(data->ctx->hdc, pts);
memcpy(pts + 1, p->aptl, sizeof(*pts) * p->cptl);
ret = PSDRV_PolyPolyline(data->ctx, pts, &cnt, 1) &&
MoveToEx(data->ctx->hdc, pts[cnt - 1].x, pts[cnt - 1].y, NULL);
free(pts);
return ret;
}
case EMR_POLYPOLYLINE:
{
const EMRPOLYPOLYLINE *p = (const EMRPOLYPOLYLINE *)rec;
return PSDRV_PolyPolyline(data->ctx,
(const POINT *)(p->aPolyCounts + p->nPolys),
p->aPolyCounts, p->nPolys);
}
case EMR_POLYPOLYGON:
{
const EMRPOLYPOLYGON *p = (const EMRPOLYPOLYGON *)rec;
return PSDRV_PolyPolygon(data->ctx,
(const POINT *)(p->aPolyCounts + p->nPolys),
(const INT *)p->aPolyCounts, p->nPolys);
}
case EMR_EOF:
return PSDRV_EndPage(data->ctx);
case EMR_SETPIXELV:
{
const EMRSETPIXELV *p = (const EMRSETPIXELV *)rec;
return PSDRV_SetPixel(data->ctx, p->ptlPixel.x,
p->ptlPixel.y, p->crColor);
}
case EMR_SETTEXTCOLOR:
{
const EMRSETTEXTCOLOR *p = (const EMRSETTEXTCOLOR *)rec;
SetTextColor(data->ctx->hdc, p->crColor);
PSDRV_SetTextColor(data->ctx, p->crColor);
return 1;
}
case EMR_SETBKCOLOR:
{
const EMRSETBKCOLOR *p = (const EMRSETBKCOLOR *)rec;
SetBkColor(data->ctx->hdc, p->crColor);
PSDRV_SetBkColor(data->ctx, p->crColor);
return 1;
}
case EMR_SAVEDC:
{
int ret = PlayEnhMetaFileRecord(hdc, htable, rec, handle_count);
if (!data->saved_dc_size)
{
data->saved_dc = malloc(8 * sizeof(*data->saved_dc));
if (data->saved_dc)
data->saved_dc_size = 8;
}
else if (data->saved_dc_size == data->saved_dc_top)
{
void *alloc = realloc(data->saved_dc, data->saved_dc_size * 2);
if (alloc)
{
data->saved_dc = alloc;
data->saved_dc_size *= 2;
}
}
if (data->saved_dc_size == data->saved_dc_top)
return 0;
data->saved_dc[data->saved_dc_top].break_extra = data->break_extra;
data->saved_dc[data->saved_dc_top].break_rem = data->break_rem;
data->saved_dc_top++;
return ret;
}
case EMR_RESTOREDC:
{
const EMRRESTOREDC *p = (const EMRRESTOREDC *)rec;
HDC hdc = data->ctx->hdc;
int ret = PlayEnhMetaFileRecord(hdc, htable, rec, handle_count);
UINT aa_flags;
if (ret)
{
select_hbrush(data, htable, handle_count, GetCurrentObject(hdc, OBJ_BRUSH));
PSDRV_SelectFont(data->ctx, GetCurrentObject(hdc, OBJ_FONT), &aa_flags);
PSDRV_SelectPen(data->ctx, GetCurrentObject(hdc, OBJ_PEN), NULL);
PSDRV_SetBkColor(data->ctx, GetBkColor(hdc));
PSDRV_SetTextColor(data->ctx, GetTextColor(hdc));
if (p->iRelative >= 0 || data->saved_dc_top + p->iRelative < 0)
return 0;
data->saved_dc_top += p->iRelative;
data->break_extra = data->saved_dc[data->saved_dc_top].break_extra;
data->break_rem = data->saved_dc[data->saved_dc_top].break_rem;
}
return ret;
}
case EMR_SELECTOBJECT:
{
const EMRSELECTOBJECT *so = (const EMRSELECTOBJECT *)rec;
struct ps_brush_pattern *pattern;
UINT aa_flags;
HGDIOBJ obj;
obj = get_object_handle(data, htable, so->ihObject, &pattern);
SelectObject(data->ctx->hdc, obj);
switch (GetObjectType(obj))
{
case OBJ_PEN: return PSDRV_SelectPen(data->ctx, obj, NULL) != NULL;
case OBJ_BRUSH: return PSDRV_SelectBrush(data->ctx, obj, pattern) != NULL;
case OBJ_FONT: return PSDRV_SelectFont(data->ctx, obj, &aa_flags) != NULL;
default:
FIXME("unhandled object type %ld\n", GetObjectType(obj));
return 1;
}
}
case EMR_DELETEOBJECT:
{
const EMRDELETEOBJECT *p = (const EMRDELETEOBJECT *)rec;
memset(&data->patterns[p->ihObject], 0, sizeof(*data->patterns));
return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
}
case EMR_ANGLEARC:
{
const EMRANGLEARC *p = (const EMRANGLEARC *)rec;
int arc_dir = SetArcDirection(data->ctx->hdc,
p->eSweepAngle >= 0 ? AD_COUNTERCLOCKWISE : AD_CLOCKWISE);
EMRARCTO arcto;
int ret;
arcto.emr.iType = EMR_ARCTO;
arcto.rclBox.left = p->ptlCenter.x - p->nRadius;
arcto.rclBox.top = p->ptlCenter.y - p->nRadius;
arcto.rclBox.right = p->ptlCenter.x + p->nRadius;
arcto.rclBox.bottom = p->ptlCenter.y + p->nRadius;
arcto.ptlStart.x = GDI_ROUND(p->ptlCenter.x +
cos(p->eStartAngle * M_PI / 180) * p->nRadius);
arcto.ptlStart.y = GDI_ROUND(p->ptlCenter.y -
sin(p->eStartAngle * M_PI / 180) * p->nRadius);
arcto.ptlEnd.x = GDI_ROUND(p->ptlCenter.x +
cos((p->eStartAngle + p->eSweepAngle) * M_PI / 180) * p->nRadius);
arcto.ptlEnd.y = GDI_ROUND(p->ptlCenter.y -
sin((p->eStartAngle + p->eSweepAngle) * M_PI / 180) * p->nRadius);
ret = hmf_proc(hdc, htable, (ENHMETARECORD *)&arcto, handle_count, arg);
SetArcDirection(data->ctx->hdc, arc_dir);
return ret;
}
case EMR_ELLIPSE:
{
const EMRELLIPSE *p = (const EMRELLIPSE *)rec;
const RECTL *r = &p->rclBox;
return PSDRV_Ellipse(data->ctx, r->left, r->top, r->right, r->bottom);
}
case EMR_RECTANGLE:
{
const EMRRECTANGLE *rect = (const EMRRECTANGLE *)rec;
return PSDRV_Rectangle(data->ctx, rect->rclBox.left,
rect->rclBox.top, rect->rclBox.right, rect->rclBox.bottom);
}
case EMR_ROUNDRECT:
{
const EMRROUNDRECT *p = (const EMRROUNDRECT *)rec;
return PSDRV_RoundRect(data->ctx, p->rclBox.left,
p->rclBox.top, p->rclBox.right, p->rclBox.bottom,
p->szlCorner.cx, p->szlCorner.cy);
}
case EMR_ARC:
{
const EMRARC *p = (const EMRARC *)rec;
return PSDRV_Arc(data->ctx, p->rclBox.left, p->rclBox.top,
p->rclBox.right, p->rclBox.bottom, p->ptlStart.x,
p->ptlStart.y, p->ptlEnd.x, p->ptlEnd.y);
}
case EMR_CHORD:
{
const EMRCHORD *p = (const EMRCHORD *)rec;
return PSDRV_Chord(data->ctx, p->rclBox.left, p->rclBox.top,
p->rclBox.right, p->rclBox.bottom, p->ptlStart.x,
p->ptlStart.y, p->ptlEnd.x, p->ptlEnd.y);
}
case EMR_PIE:
{
const EMRPIE *p = (const EMRPIE *)rec;
return PSDRV_Pie(data->ctx, p->rclBox.left, p->rclBox.top,
p->rclBox.right, p->rclBox.bottom, p->ptlStart.x,
p->ptlStart.y, p->ptlEnd.x, p->ptlEnd.y);
}
case EMR_LINETO:
{
const EMRLINETO *line = (const EMRLINETO *)rec;
return PSDRV_LineTo(data->ctx, line->ptl.x, line->ptl.y) &&
MoveToEx(data->ctx->hdc, line->ptl.x, line->ptl.y, NULL);
}
case EMR_ARCTO:
{
const EMRARCTO *p = (const EMRARCTO *)rec;
POINT pt;
BOOL ret;
ret = GetCurrentPositionEx(data->ctx->hdc, &pt);
if (ret)
{
ret = ArcTo(data->ctx->hdc, p->rclBox.left, p->rclBox.top,
p->rclBox.right, p->rclBox.bottom, p->ptlStart.x,
p->ptlStart.y, p->ptlStart.x, p->ptlStart.y);
}
if (ret)
ret = PSDRV_LineTo(data->ctx, pt.x, pt.y);
if (ret)
{
ret = PSDRV_Arc(data->ctx, p->rclBox.left, p->rclBox.top,
p->rclBox.right, p->rclBox.bottom, p->ptlStart.x,
p->ptlStart.y, p->ptlEnd.x, p->ptlEnd.y);
}
if (ret)
{
ret = ArcTo(data->ctx->hdc, p->rclBox.left, p->rclBox.top,
p->rclBox.right, p->rclBox.bottom, p->ptlEnd.x,
p->ptlEnd.y, p->ptlEnd.x, p->ptlEnd.y);
}
return ret;
}
case EMR_POLYDRAW:
{
const EMRPOLYDRAW *p = (const EMRPOLYDRAW *)rec;
const POINT *pts = (const POINT *)p->aptl;
return poly_draw(data->ctx, pts, (BYTE *)(p->aptl + p->cptl), p->cptl) &&
MoveToEx(data->ctx->hdc, pts[p->cptl - 1].x, pts[p->cptl - 1].y, NULL);
}
case EMR_BEGINPATH:
{
data->path = TRUE;
return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
}
case EMR_ENDPATH:
{
data->path = FALSE;
return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
}
case EMR_FILLPATH:
PSDRV_FillPath(data->ctx);
return 1;
case EMR_STROKEANDFILLPATH:
PSDRV_StrokeAndFillPath(data->ctx);
return 1;
case EMR_STROKEPATH:
PSDRV_StrokePath(data->ctx);
return 1;
case EMR_ABORTPATH:
{
data->path = FALSE;
return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
}
case EMR_FILLRGN:
{
const EMRFILLRGN *p = (const EMRFILLRGN *)rec;
HRGN rgn;
int ret;
rgn = ExtCreateRegion(NULL, p->cbRgnData, (const RGNDATA *)p->RgnData);
ret = fill_rgn(data, htable, handle_count, p->ihBrush, rgn);
DeleteObject(rgn);
return ret;
}
case EMR_FRAMERGN:
{
const EMRFRAMERGN *p = (const EMRFRAMERGN *)rec;
HRGN rgn, frame;
int ret;
rgn = ExtCreateRegion(NULL, p->cbRgnData, (const RGNDATA *)p->RgnData);
frame = CreateRectRgn(0, 0, 0, 0);
CombineRgn(frame, rgn, 0, RGN_COPY);
OffsetRgn(frame, -p->szlStroke.cx, 0);
OffsetRgn(rgn, p->szlStroke.cx, 0);
CombineRgn(frame, frame, rgn, RGN_AND);
OffsetRgn(rgn, -p->szlStroke.cx, -p->szlStroke.cy);
CombineRgn(frame, frame, rgn, RGN_AND);
OffsetRgn(rgn, 0, 2*p->szlStroke.cy);
CombineRgn(frame, frame, rgn, RGN_AND);
OffsetRgn(rgn, 0, -p->szlStroke.cy);
CombineRgn(frame, rgn, frame, RGN_DIFF);
ret = fill_rgn(data, htable, handle_count, p->ihBrush, frame);
DeleteObject(rgn);
DeleteObject(frame);
return ret;
}
case EMR_INVERTRGN:
{
const EMRINVERTRGN *p = (const EMRINVERTRGN *)rec;
int old_rop, ret;
HRGN rgn;
rgn = ExtCreateRegion(NULL, p->cbRgnData, (const RGNDATA *)p->RgnData);
old_rop = SetROP2(data->ctx->hdc, R2_NOT);
ret = fill_rgn(data, htable, handle_count, 0x80000000 | BLACK_BRUSH, rgn);
SetROP2(data->ctx->hdc, old_rop);
DeleteObject(rgn);
return ret;
}
case EMR_PAINTRGN:
{
const EMRPAINTRGN *p = (const EMRPAINTRGN *)rec;
HRGN rgn = ExtCreateRegion(NULL, p->cbRgnData, (const RGNDATA *)p->RgnData);
int ret;
ret = PSDRV_PaintRgn(data->ctx, rgn);
DeleteObject(rgn);
return ret;
}
case EMR_BITBLT:
{
const EMRBITBLT *p = (const EMRBITBLT *)rec;
const BITMAPINFO *bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiSrc);
const BYTE *src_bits = (BYTE *)p + p->offBitsSrc;
EMRSTRETCHBLT blt;
blt.rclBounds = p->rclBounds;
blt.xDest = p->xDest;
blt.yDest = p->yDest;
blt.cxDest = p->cxDest;
blt.cyDest = p->cyDest;
blt.dwRop = p->dwRop;
blt.xSrc = p->xSrc;
blt.ySrc = p->ySrc;
blt.xformSrc = p->xformSrc;
blt.crBkColorSrc = p->crBkColorSrc;
blt.iUsageSrc = p->iUsageSrc;
blt.offBmiSrc = p->offBmiSrc;
blt.cbBmiSrc = p->cbBmiSrc;
blt.offBitsSrc = p->offBitsSrc;
blt.cbBitsSrc = p->cbBitsSrc;
blt.cxSrc = p->cxDest;
blt.cySrc = p->cyDest;
return stretch_blt(data->ctx, &blt, bi, src_bits);
}
case EMR_STRETCHBLT:
{
const EMRSTRETCHBLT *p = (const EMRSTRETCHBLT *)rec;
const BITMAPINFO *bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiSrc);
const BYTE *src_bits = (BYTE *)p + p->offBitsSrc;
return stretch_blt(data->ctx, p, bi, src_bits);
}
case EMR_MASKBLT:
{
const EMRMASKBLT *p = (const EMRMASKBLT *)rec;
const BITMAPINFO *mask_bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiMask);
const BITMAPINFO *src_bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiSrc);
const BYTE *mask_bits = (BYTE *)p + p->offBitsMask;
const BYTE *src_bits = (BYTE *)p + p->offBitsSrc;
return mask_blt(data->ctx, p, src_bi, src_bits, mask_bi, mask_bits);
}
case EMR_PLGBLT:
{
const EMRPLGBLT *p = (const EMRPLGBLT *)rec;
return plg_blt(data->ctx, p);
}
case EMR_SETDIBITSTODEVICE:
return set_di_bits_to_device(data->ctx, (const EMRSETDIBITSTODEVICE *)rec);
case EMR_STRETCHDIBITS:
return stretch_di_bits(data->ctx, (const EMRSTRETCHDIBITS *)rec);
case EMR_EXTTEXTOUTW:
{
const EMREXTTEXTOUTW *p = (const EMREXTTEXTOUTW *)rec;
HDC hdc = data->ctx->hdc;
const INT *dx = NULL;
int old_mode, ret;
RECT rect;
rect.left = p->emrtext.rcl.left;
rect.top = p->emrtext.rcl.top;
rect.right = p->emrtext.rcl.right;
rect.bottom = p->emrtext.rcl.bottom;
old_mode = SetGraphicsMode(hdc, p->iGraphicsMode);
/* Reselect the font back into the dc so that the transformation
gets updated. */
SelectObject(hdc, GetCurrentObject(hdc, OBJ_FONT));
if (p->emrtext.offDx)
dx = (const INT *)((const BYTE *)rec + p->emrtext.offDx);
ret = ext_text_out(data, htable, handle_count, p->emrtext.ptlReference.x,
p->emrtext.ptlReference.y, p->emrtext.fOptions, &rect,
(LPCWSTR)((const BYTE *)rec + p->emrtext.offString), p->emrtext.nChars, dx);
SetGraphicsMode(hdc, old_mode);
return ret;
}
case EMR_POLYBEZIER16:
{
const EMRPOLYBEZIER16 *p = (const EMRPOLYBEZIER16 *)rec;
POINT *pts;
int i;
pts = malloc(sizeof(*pts) * p->cpts);
if (!pts) return 0;
for (i = 0; i < p->cpts; i++)
{
pts[i].x = p->apts[i].x;
pts[i].y = p->apts[i].y;
}
i = PSDRV_PolyBezier(data->ctx, pts, p->cpts);
free(pts);
return i;
}
case EMR_POLYGON16:
{
const EMRPOLYGON16 *p = (const EMRPOLYGON16 *)rec;
POINT *pts;
int i;
pts = malloc(sizeof(*pts) * p->cpts);
if (!pts) return 0;
for (i = 0; i < p->cpts; i++)
{
pts[i].x = p->apts[i].x;
pts[i].y = p->apts[i].y;
}
i = PSDRV_PolyPolygon(data->ctx, pts, (const INT *)&p->cpts, 1);
free(pts);
return i;
}
case EMR_POLYLINE16:
{
const EMRPOLYLINE16 *p = (const EMRPOLYLINE16 *)rec;
POINT *pts;
int i;
pts = malloc(sizeof(*pts) * p->cpts);
if (!pts) return 0;
for (i = 0; i < p->cpts; i++)
{
pts[i].x = p->apts[i].x;
pts[i].y = p->apts[i].y;
}
i = PSDRV_PolyPolyline(data->ctx, pts, &p->cpts, 1);
free(pts);
return i;
}
case EMR_POLYBEZIERTO16:
{
const EMRPOLYBEZIERTO16 *p = (const EMRPOLYBEZIERTO16 *)rec;
POINT *pts;
int i;
pts = malloc(sizeof(*pts) * p->cpts);
if (!pts) return 0;
for (i = 0; i < p->cpts; i++)
{
pts[i].x = p->apts[i].x;
pts[i].y = p->apts[i].y;
}
i = PSDRV_PolyBezierTo(data->ctx, pts, p->cpts) &&
MoveToEx(data->ctx->hdc, pts[p->cpts - 1].x, pts[p->cpts - 1].y, NULL);
free(pts);
return i;
}
case EMR_POLYLINETO16:
{
const EMRPOLYLINETO16 *p = (const EMRPOLYLINETO16 *)rec;
POINT *pts;
DWORD cnt;
int i;
cnt = p->cpts + 1;
pts = malloc(sizeof(*pts) * cnt);
if (!pts) return 0;
GetCurrentPositionEx(data->ctx->hdc, pts);
for (i = 0; i < p->cpts; i++)
{
pts[i + 1].x = p->apts[i].x;
pts[i + 1].y = p->apts[i].y;
}
i = PSDRV_PolyPolyline(data->ctx, pts, &cnt, 1) &&
MoveToEx(data->ctx->hdc, pts[cnt - 1].x, pts[cnt - 1].y, NULL);
free(pts);
return i;
}
case EMR_POLYPOLYLINE16:
{
const EMRPOLYPOLYLINE16 *p = (const EMRPOLYPOLYLINE16 *)rec;
POINT *pts;
int i;
pts = malloc(sizeof(*pts) * p->cpts);
if (!pts) return 0;
for (i = 0; i < p->cpts; i++)
{
pts[i].x = ((const POINTS *)(p->aPolyCounts + p->nPolys))[i].x;
pts[i].y = ((const POINTS *)(p->aPolyCounts + p->nPolys))[i].y;
}
i = PSDRV_PolyPolyline(data->ctx, pts, p->aPolyCounts, p->nPolys);
free(pts);
return i;
}
case EMR_POLYPOLYGON16:
{
const EMRPOLYPOLYGON16 *p = (const EMRPOLYPOLYGON16 *)rec;
POINT *pts;
int i;
pts = malloc(sizeof(*pts) * p->cpts);
if (!pts) return 0;
for (i = 0; i < p->cpts; i++)
{
pts[i].x = ((const POINTS *)(p->aPolyCounts + p->nPolys))[i].x;
pts[i].y = ((const POINTS *)(p->aPolyCounts + p->nPolys))[i].y;
}
i = PSDRV_PolyPolygon(data->ctx, pts, (const INT *)p->aPolyCounts, p->nPolys);
free(pts);
return i;
}
case EMR_POLYDRAW16:
{
const EMRPOLYDRAW16 *p = (const EMRPOLYDRAW16 *)rec;
POINT *pts;
int i;
pts = malloc(sizeof(*pts) * p->cpts);
if (!pts) return 0;
for (i = 0; i < p->cpts; i++)
{
pts[i].x = p->apts[i].x;
pts[i].y = p->apts[i].y;
}
i = poly_draw(data->ctx, pts, (BYTE *)(p->apts + p->cpts), p->cpts) &&
MoveToEx(data->ctx->hdc, pts[p->cpts - 1].x, pts[p->cpts - 1].y, NULL);
free(pts);
return i;
}
case EMR_CREATEMONOBRUSH:
{
const EMRCREATEMONOBRUSH *p = (const EMRCREATEMONOBRUSH *)rec;
if (!PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count))
return 0;
data->patterns[p->ihBrush].usage = p->iUsage;
data->patterns[p->ihBrush].info = (BITMAPINFO *)((BYTE *)p + p->offBmi);
data->patterns[p->ihBrush].bits.ptr = (BYTE *)p + p->offBits;
return 1;
}
case EMR_CREATEDIBPATTERNBRUSHPT:
{
const EMRCREATEDIBPATTERNBRUSHPT *p = (const EMRCREATEDIBPATTERNBRUSHPT *)rec;
if (!PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count))
return 0;
data->patterns[p->ihBrush].usage = p->iUsage;
data->patterns[p->ihBrush].info = (BITMAPINFO *)((BYTE *)p + p->offBmi);
data->patterns[p->ihBrush].bits.ptr = (BYTE *)p + p->offBits;
return 1;
}
case EMR_EXTESCAPE:
{
const struct EMREXTESCAPE
{
EMR emr;
DWORD escape;
DWORD size;
BYTE data[1];
} *p = (const struct EMREXTESCAPE *)rec;
PSDRV_ExtEscape(data->ctx, p->escape, p->size, p->data, 0, NULL);
return 1;
}
case EMR_GRADIENTFILL:
{
const EMRGRADIENTFILL *p = (const EMRGRADIENTFILL *)rec;
return gradient_fill(data->ctx, p->Ver, p->nVer,
p->Ver + p->nVer, p->nTri, p->ulMode);
}
case EMR_SETTEXTJUSTIFICATION:
{
const EMRSETTEXTJUSTIFICATION *p = (const EMRSETTEXTJUSTIFICATION *)rec;
if (p->break_count)
{
data->break_extra = p->break_extra / p->break_count;
data->break_rem = p->break_extra - data->break_extra * p->break_count;
}
else
{
data->break_extra = 0;
data->break_rem = 0;
}
return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
}
case EMR_EXTFLOODFILL:
case EMR_ALPHABLEND:
break;
case EMR_SETWINDOWEXTEX:
case EMR_SETWINDOWORGEX:
case EMR_SETVIEWPORTEXTEX:
case EMR_SETVIEWPORTORGEX:
case EMR_SETBRUSHORGEX:
case EMR_SETMAPPERFLAGS:
case EMR_SETMAPMODE:
case EMR_SETBKMODE:
case EMR_SETPOLYFILLMODE:
case EMR_SETROP2:
case EMR_SETSTRETCHBLTMODE:
case EMR_SETTEXTALIGN:
case EMR_OFFSETCLIPRGN:
case EMR_MOVETOEX:
case EMR_EXCLUDECLIPRECT:
case EMR_INTERSECTCLIPRECT:
case EMR_SCALEVIEWPORTEXTEX:
case EMR_SCALEWINDOWEXTEX:
case EMR_SETWORLDTRANSFORM:
case EMR_MODIFYWORLDTRANSFORM:
case EMR_CREATEPEN:
case EMR_CREATEBRUSHINDIRECT:
case EMR_SELECTPALETTE:
case EMR_CREATEPALETTE:
case EMR_SETPALETTEENTRIES:
case EMR_RESIZEPALETTE:
case EMR_REALIZEPALETTE:
case EMR_SETARCDIRECTION:
case EMR_CLOSEFIGURE:
case EMR_FLATTENPATH:
case EMR_WIDENPATH:
case EMR_SELECTCLIPPATH:
case EMR_EXTSELECTCLIPRGN:
case EMR_EXTCREATEFONTINDIRECTW:
case EMR_SETLAYOUT:
return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
default:
FIXME("unsupported record: %ld\n", rec->iType);
}
return 1;
}
static BOOL print_metafile(struct pp_data *data, HANDLE hdata)
{
XFORM xform = { .eM11 = 1, .eM22 = 1 };
record_hdr header;
HENHMETAFILE hmf;
BYTE *buf;
BOOL ret;
DWORD r;
if (!ReadPrinter(hdata, &header, sizeof(header), &r))
return FALSE;
if (r != sizeof(header))
{
SetLastError(ERROR_INVALID_DATA);
return FALSE;
}
buf = malloc(header.cjSize);
if (!buf)
return FALSE;
if (!ReadPrinter(hdata, buf, header.cjSize, &r))
{
free(buf);
return FALSE;
}
if (r != header.cjSize)
{
free(buf);
SetLastError(ERROR_INVALID_DATA);
return FALSE;
}
hmf = SetEnhMetaFileBits(header.cjSize, buf);
free(buf);
if (!hmf)
return FALSE;
AbortPath(data->ctx->hdc);
MoveToEx(data->ctx->hdc, 0, 0, NULL);
SetBkColor(data->ctx->hdc, RGB(255, 255, 255));
SetBkMode(data->ctx->hdc, OPAQUE);
SetMapMode(data->ctx->hdc, MM_TEXT);
SetPolyFillMode(data->ctx->hdc, ALTERNATE);
SetROP2(data->ctx->hdc, R2_COPYPEN);
SetStretchBltMode(data->ctx->hdc, BLACKONWHITE);
SetTextAlign(data->ctx->hdc, TA_LEFT | TA_TOP);
SetTextColor(data->ctx->hdc, 0);
SetTextJustification(data->ctx->hdc, 0, 0);
SetWorldTransform(data->ctx->hdc, &xform);
PSDRV_SetTextColor(data->ctx, 0);
PSDRV_SetBkColor(data->ctx, RGB(255, 255, 255));
ret = EnumEnhMetaFile(NULL, hmf, hmf_proc, (void *)data, NULL);
DeleteEnhMetaFile(hmf);
free(data->patterns);
data->patterns = NULL;
data->path = FALSE;
data->break_extra = 0;
data->break_rem = 0;
data->saved_dc_top = 0;
return ret;
}
BOOL WINAPI EnumPrintProcessorDatatypesW(WCHAR *server, WCHAR *name, DWORD level,
BYTE *datatypes, DWORD size, DWORD *needed, DWORD *no)
{
DATATYPES_INFO_1W *info = (DATATYPES_INFO_1W *)datatypes;
TRACE("%s, %s, %ld, %p, %ld, %p, %p\n", debugstr_w(server), debugstr_w(name),
level, datatypes, size, needed, no);
if (!needed || !no)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
*no = 0;
*needed = sizeof(*info) + sizeof(emf_1003);
if (level != 1 || (size && !datatypes))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (size < *needed)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
*no = 1;
info->pName = (WCHAR*)(info + 1);
memcpy(info + 1, emf_1003, sizeof(emf_1003));
return TRUE;
}
HANDLE WINAPI OpenPrintProcessor(WCHAR *port, PRINTPROCESSOROPENDATA *open_data)
{
struct pp_data *data;
HANDLE hport;
HDC hdc;
TRACE("%s, %p\n", debugstr_w(port), open_data);
if (!port || !open_data || !open_data->pDatatype)
{
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
if (wcscmp(open_data->pDatatype, emf_1003))
{
SetLastError(ERROR_INVALID_DATATYPE);
return NULL;
}
if (!OpenPrinterW(port, &hport, NULL))
return NULL;
data = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(*data));
if (!data)
return NULL;
data->magic = PP_MAGIC;
data->hport = hport;
data->doc_name = wcsdup(open_data->pDocumentName);
data->out_file = wcsdup(open_data->pOutputFile);
hdc = CreateDCW(L"winspool", open_data->pPrinterName, NULL, open_data->pDevMode);
if (!hdc)
{
LocalFree(data);
return NULL;
}
SetGraphicsMode(hdc, GM_ADVANCED);
data->ctx = create_print_ctx(hdc, open_data->pPrinterName, open_data->pDevMode);
if (!data->ctx)
{
DeleteDC(hdc);
LocalFree(data);
return NULL;
}
return (HANDLE)data;
}
BOOL WINAPI PrintDocumentOnPrintProcessor(HANDLE pp, WCHAR *doc_name)
{
struct pp_data *data = get_handle_data(pp);
emfspool_header header;
LARGE_INTEGER pos, cur;
record_hdr record;
HANDLE spool_data;
DOC_INFO_1W info;
BOOL ret;
DWORD r;
TRACE("%p, %s\n", pp, debugstr_w(doc_name));
if (!data)
return FALSE;
if (!OpenPrinterW(doc_name, &spool_data, NULL))
return FALSE;
info.pDocName = data->doc_name;
info.pOutputFile = data->out_file;
info.pDatatype = (WCHAR *)L"RAW";
data->ctx->job.id = StartDocPrinterW(data->hport, 1, (BYTE *)&info);
if (!data->ctx->job.id)
{
ClosePrinter(spool_data);
return FALSE;
}
if (!(ret = ReadPrinter(spool_data, &header, sizeof(header), &r)))
goto cleanup;
if (r != sizeof(header))
{
SetLastError(ERROR_INVALID_DATA);
ret = FALSE;
goto cleanup;
}
if (header.dwVersion != EMFSPOOL_VERSION)
{
FIXME("unrecognized spool file format\n");
SetLastError(ERROR_INTERNAL_ERROR);
goto cleanup;
}
pos.QuadPart = header.cjSize;
if (!(ret = SeekPrinter(spool_data, pos, NULL, FILE_BEGIN, FALSE)))
goto cleanup;
data->ctx->job.hprinter = data->hport;
if (!PSDRV_WriteHeader(data->ctx, data->doc_name))
{
WARN("Failed to write header\n");
goto cleanup;
}
data->ctx->job.OutOfPage = TRUE;
data->ctx->job.PageNo = 0;
data->ctx->job.quiet = FALSE;
data->ctx->job.passthrough_state = passthrough_none;
data->ctx->job.doc_name = strdupW(data->doc_name);
while (1)
{
if (!(ret = ReadPrinter(spool_data, &record, sizeof(record), &r)))
goto cleanup;
if (!r)
break;
if (r != sizeof(record))
{
SetLastError(ERROR_INVALID_DATA);
ret = FALSE;
goto cleanup;
}
switch (record.ulID)
{
case EMRI_DEVMODE:
{
DEVMODEW *devmode = NULL;
if (record.cjSize)
{
devmode = malloc(record.cjSize);
if (!devmode)
goto cleanup;
ret = ReadPrinter(spool_data, devmode, record.cjSize, &r);
if (ret && r != record.cjSize)
{
SetLastError(ERROR_INVALID_DATA);
ret = FALSE;
}
}
if (ret)
ret = PSDRV_ResetDC(data->ctx, devmode);
free(devmode);
if (!ret)
goto cleanup;
break;
}
case EMRI_METAFILE_DATA:
pos.QuadPart = record.cjSize;
ret = SeekPrinter(spool_data, pos, NULL, FILE_CURRENT, FALSE);
if (!ret)
goto cleanup;
break;
case EMRI_METAFILE_EXT:
case EMRI_BW_METAFILE_EXT:
pos.QuadPart = 0;
ret = SeekPrinter(spool_data, pos, &cur, FILE_CURRENT, FALSE);
if (ret)
{
cur.QuadPart += record.cjSize;
ret = ReadPrinter(spool_data, &pos, sizeof(pos), &r);
if (ret && r != sizeof(pos))
{
SetLastError(ERROR_INVALID_DATA);
ret = FALSE;
}
}
pos.QuadPart = -pos.QuadPart - 2 * sizeof(record);
if (ret)
ret = SeekPrinter(spool_data, pos, NULL, FILE_CURRENT, FALSE);
if (ret)
ret = print_metafile(data, spool_data);
if (ret)
ret = SeekPrinter(spool_data, cur, NULL, FILE_BEGIN, FALSE);
if (!ret)
goto cleanup;
break;
default:
FIXME("%s not supported, skipping\n", debugstr_rec_type(record.ulID));
pos.QuadPart = record.cjSize;
ret = SeekPrinter(spool_data, pos, NULL, FILE_CURRENT, FALSE);
if (!ret)
goto cleanup;
break;
}
}
cleanup:
if (data->ctx->job.PageNo)
PSDRV_WriteFooter(data->ctx);
HeapFree(GetProcessHeap(), 0, data->ctx->job.doc_name);
ClosePrinter(spool_data);
return EndDocPrinter(data->hport) && ret;
}
BOOL WINAPI ControlPrintProcessor(HANDLE pp, DWORD cmd)
{
FIXME("%p, %ld\n", pp, cmd);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
BOOL WINAPI ClosePrintProcessor(HANDLE pp)
{
struct pp_data *data = get_handle_data(pp);
TRACE("%p\n", pp);
if (!data)
return FALSE;
ClosePrinter(data->hport);
free(data->doc_name);
free(data->out_file);
DeleteDC(data->ctx->hdc);
HeapFree(GetProcessHeap(), 0, data->ctx->Devmode);
HeapFree(GetProcessHeap(), 0, data->ctx);
free(data->saved_dc);
memset(data, 0, sizeof(*data));
LocalFree(data);
return TRUE;
}
HRESULT WINAPI DllRegisterServer(void)
{
AddPrintProcessorW(NULL, (WCHAR *)L"Windows 4.0", (WCHAR *)L"wineps.drv", (WCHAR *)L"wineps");
AddPrintProcessorW(NULL, NULL, (WCHAR *)L"wineps.drv", (WCHAR *)L"wineps");
return S_OK;
}