gdi32/uniscribe: Ensure the cache is initialised.
[wine.git] / dlls / wineps.drv / printproc.c
blob84f4b9f59002a9e412df38b48fbecc5529cb70d1
1 /*
2 * Print processor implementation.
4 * Copyright 2022 Piotr Caban for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <math.h>
22 #include <stdlib.h>
24 #include <windows.h>
25 #include <ntgdi.h>
26 #include <winppi.h>
27 #include <winspool.h>
28 #include <ddk/winsplp.h>
29 #include <usp10.h>
31 #include "psdrv.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
37 #define EMFSPOOL_VERSION 0x10000
38 #define PP_MAGIC 0x952173fe
40 struct pp_data
42 DWORD magic;
43 HANDLE hport;
44 WCHAR *port;
45 WCHAR *doc_name;
46 WCHAR *out_file;
48 print_ctx *ctx;
50 struct ps_brush_pattern *patterns;
51 BOOL path;
52 INT break_extra;
53 INT break_rem;
55 INT saved_dc_size;
56 INT saved_dc_top;
57 struct
59 INT break_extra;
60 INT break_rem;
61 } *saved_dc;
64 typedef enum
66 EMRI_METAFILE = 1,
67 EMRI_ENGINE_FONT,
68 EMRI_DEVMODE,
69 EMRI_TYPE1_FONT,
70 EMRI_PRESTARTPAGE,
71 EMRI_DESIGNVECTOR,
72 EMRI_SUBSET_FONT,
73 EMRI_DELTA_FONT,
74 EMRI_FORM_METAFILE,
75 EMRI_BW_METAFILE,
76 EMRI_BW_FORM_METAFILE,
77 EMRI_METAFILE_DATA,
78 EMRI_METAFILE_EXT,
79 EMRI_BW_METAFILE_EXT,
80 EMRI_ENGINE_FONT_EXT,
81 EMRI_TYPE1_FONT_EXT,
82 EMRI_DESIGNVECTOR_EXT,
83 EMRI_SUBSET_FONT_EXT,
84 EMRI_DELTA_FONT_EXT,
85 EMRI_PS_JOB_DATA,
86 EMRI_EMBED_FONT_EXT,
87 } record_type;
89 typedef struct
91 unsigned int dwVersion;
92 unsigned int cjSize;
93 unsigned int dpszDocName;
94 unsigned int dpszOutput;
95 } emfspool_header;
97 typedef struct
99 unsigned int ulID;
100 unsigned int cjSize;
101 } record_hdr;
103 typedef struct
105 EMR emr;
106 INT break_extra;
107 INT break_count;
108 } EMRSETTEXTJUSTIFICATION;
110 BOOL WINAPI SeekPrinter(HANDLE, LARGE_INTEGER, LARGE_INTEGER*, DWORD, BOOL);
112 static const WCHAR emf_1003[] = L"NT EMF 1.003";
114 #define EMRICASE(x) case x: return #x
115 static const char * debugstr_rec_type(int id)
117 switch (id)
119 EMRICASE(EMRI_METAFILE);
120 EMRICASE(EMRI_ENGINE_FONT);
121 EMRICASE(EMRI_DEVMODE);
122 EMRICASE(EMRI_TYPE1_FONT);
123 EMRICASE(EMRI_PRESTARTPAGE);
124 EMRICASE(EMRI_DESIGNVECTOR);
125 EMRICASE(EMRI_SUBSET_FONT);
126 EMRICASE(EMRI_DELTA_FONT);
127 EMRICASE(EMRI_FORM_METAFILE);
128 EMRICASE(EMRI_BW_METAFILE);
129 EMRICASE(EMRI_BW_FORM_METAFILE);
130 EMRICASE(EMRI_METAFILE_DATA);
131 EMRICASE(EMRI_METAFILE_EXT);
132 EMRICASE(EMRI_BW_METAFILE_EXT);
133 EMRICASE(EMRI_ENGINE_FONT_EXT);
134 EMRICASE(EMRI_TYPE1_FONT_EXT);
135 EMRICASE(EMRI_DESIGNVECTOR_EXT);
136 EMRICASE(EMRI_SUBSET_FONT_EXT);
137 EMRICASE(EMRI_DELTA_FONT_EXT);
138 EMRICASE(EMRI_PS_JOB_DATA);
139 EMRICASE(EMRI_EMBED_FONT_EXT);
140 default:
141 FIXME("unknown record type: %d\n", id);
142 return NULL;
145 #undef EMRICASE
147 static struct pp_data* get_handle_data(HANDLE pp)
149 struct pp_data *ret = (struct pp_data *)pp;
151 if (!ret || ret->magic != PP_MAGIC)
153 SetLastError(ERROR_INVALID_HANDLE);
154 return NULL;
156 return ret;
159 static inline INT GDI_ROUND(double val)
161 return (int)floor(val + 0.5);
164 static void translate(RECT *rect, const XFORM *xform)
166 double x, y;
168 x = rect->left;
169 y = rect->top;
170 rect->left = GDI_ROUND(x * xform->eM11 + y * xform->eM21 + xform->eDx);
171 rect->top = GDI_ROUND(x * xform->eM12 + y * xform->eM22 + xform->eDy);
173 x = rect->right;
174 y = rect->bottom;
175 rect->right = GDI_ROUND(x * xform->eM11 + y * xform->eM21 + xform->eDx);
176 rect->bottom = GDI_ROUND(x * xform->eM12 + y * xform->eM22 + xform->eDy);
179 static inline void get_bounding_rect(RECT *rect, int x, int y, int width, int height)
181 rect->left = x;
182 rect->right = x + width;
183 rect->top = y;
184 rect->bottom = y + height;
185 if (rect->left > rect->right)
187 int tmp = rect->left;
188 rect->left = rect->right + 1;
189 rect->right = tmp + 1;
191 if (rect->top > rect->bottom)
193 int tmp = rect->top;
194 rect->top = rect->bottom + 1;
195 rect->bottom = tmp + 1;
199 static inline void order_rect(RECT *rect)
201 if (rect->left > rect->right)
203 int tmp = rect->left;
204 rect->left = rect->right;
205 rect->right = tmp;
207 if (rect->top > rect->bottom)
209 int tmp = rect->top;
210 rect->top = rect->bottom;
211 rect->bottom = tmp;
215 static BOOL intersect_vis_rectangles(struct ps_bitblt_coords *dst, struct ps_bitblt_coords *src)
217 RECT rect;
219 /* intersect the rectangles */
221 if ((src->width == dst->width) && (src->height == dst->height)) /* no stretching */
223 OffsetRect(&src->visrect, dst->x - src->x, dst->y - src->y);
224 if (!IntersectRect(&rect, &src->visrect, &dst->visrect)) return FALSE;
225 src->visrect = dst->visrect = rect;
226 OffsetRect(&src->visrect, src->x - dst->x, src->y - dst->y);
228 else /* stretching */
230 /* map source rectangle into destination coordinates */
231 rect = src->visrect;
232 OffsetRect(&rect,
233 -src->x - (src->width < 0 ? 1 : 0),
234 -src->y - (src->height < 0 ? 1 : 0));
235 rect.left = rect.left * dst->width / src->width;
236 rect.top = rect.top * dst->height / src->height;
237 rect.right = rect.right * dst->width / src->width;
238 rect.bottom = rect.bottom * dst->height / src->height;
239 order_rect(&rect);
241 /* when the source rectangle needs to flip and it doesn't fit in the source device
242 area, the destination area isn't flipped. So, adjust destination coordinates */
243 if (src->width < 0 && dst->width > 0 &&
244 (src->x + src->width + 1 < src->visrect.left || src->x > src->visrect.right))
245 dst->x += (dst->width - rect.right) - rect.left;
246 else if (src->width > 0 && dst->width < 0 &&
247 (src->x < src->visrect.left || src->x + src->width > src->visrect.right))
248 dst->x -= rect.right - (dst->width - rect.left);
250 if (src->height < 0 && dst->height > 0 &&
251 (src->y + src->height + 1 < src->visrect.top || src->y > src->visrect.bottom))
252 dst->y += (dst->height - rect.bottom) - rect.top;
253 else if (src->height > 0 && dst->height < 0 &&
254 (src->y < src->visrect.top || src->y + src->height > src->visrect.bottom))
255 dst->y -= rect.bottom - (dst->height - rect.top);
257 OffsetRect(&rect, dst->x, dst->y);
259 /* avoid rounding errors */
260 rect.left--;
261 rect.top--;
262 rect.right++;
263 rect.bottom++;
264 if (!IntersectRect(&dst->visrect, &rect, &dst->visrect)) return FALSE;
266 /* map destination rectangle back to source coordinates */
267 rect = dst->visrect;
268 OffsetRect(&rect,
269 -dst->x - (dst->width < 0 ? 1 : 0),
270 -dst->y - (dst->height < 0 ? 1 : 0));
271 rect.left = src->x + rect.left * src->width / dst->width;
272 rect.top = src->y + rect.top * src->height / dst->height;
273 rect.right = src->x + rect.right * src->width / dst->width;
274 rect.bottom = src->y + rect.bottom * src->height / dst->height;
275 order_rect(&rect);
277 /* avoid rounding errors */
278 rect.left--;
279 rect.top--;
280 rect.right++;
281 rect.bottom++;
282 if (!IntersectRect(&src->visrect, &rect, &src->visrect)) return FALSE;
284 return TRUE;
287 static void clip_visrect(HDC hdc, RECT *dst, const RECT *src)
289 HRGN hrgn;
291 hrgn = CreateRectRgn(0, 0, 0, 0);
292 if (GetRandomRgn(hdc, hrgn, 3) == 1)
294 GetRgnBox(hrgn, dst);
295 IntersectRect(dst, dst, src);
297 else
299 *dst = *src;
301 DeleteObject(hrgn);
304 static BOOL get_vis_rectangles(HDC hdc, struct ps_bitblt_coords *dst,
305 const XFORM *xform, DWORD width, DWORD height, struct ps_bitblt_coords *src)
307 RECT rect;
309 rect.left = dst->log_x;
310 rect.top = dst->log_y;
311 rect.right = dst->log_x + dst->log_width;
312 rect.bottom = dst->log_y + dst->log_height;
313 LPtoDP(hdc, (POINT *)&rect, 2);
314 dst->x = rect.left;
315 dst->y = rect.top;
316 dst->width = rect.right - rect.left;
317 dst->height = rect.bottom - rect.top;
318 if (dst->layout & LAYOUT_RTL && dst->layout & LAYOUT_BITMAPORIENTATIONPRESERVED)
320 dst->x += dst->width;
321 dst->width = -dst->width;
323 get_bounding_rect(&rect, dst->x, dst->y, dst->width, dst->height);
324 clip_visrect(hdc, &dst->visrect, &rect);
326 if (IsRectEmpty(&dst->visrect)) return FALSE;
327 if (!src) return TRUE;
329 rect.left = src->log_x;
330 rect.top = src->log_y;
331 rect.right = src->log_x + src->log_width;
332 rect.bottom = src->log_y + src->log_height;
333 translate(&rect, xform);
334 src->x = rect.left;
335 src->y = rect.top;
336 src->width = rect.right - rect.left;
337 src->height = rect.bottom - rect.top;
338 get_bounding_rect(&rect, src->x, src->y, src->width, src->height);
339 if (rect.left < 0) rect.left = 0;
340 if (rect.top < 0) rect.top = 0;
341 if (rect.right > width) rect.right = width;
342 if (rect.bottom > height) rect.bottom = height;
343 src->visrect = rect;
345 return intersect_vis_rectangles(dst, src);
348 static int stretch_blt(print_ctx *ctx, const EMRSTRETCHBLT *blt,
349 const BITMAPINFO *bi, const BYTE *src_bits)
351 char dst_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
352 BITMAPINFO *dst_info = (BITMAPINFO *)dst_buffer;
353 struct ps_bitblt_coords src, dst;
354 struct ps_image_bits bits;
355 DWORD err;
357 dst.log_x = blt->xDest;
358 dst.log_y = blt->yDest;
359 dst.log_width = blt->cxDest;
360 dst.log_height = blt->cyDest;
361 dst.layout = GetLayout(ctx->hdc);
362 if (blt->dwRop & NOMIRRORBITMAP)
363 dst.layout |= LAYOUT_BITMAPORIENTATIONPRESERVED;
365 if (!blt->cbBmiSrc)
367 if (!get_vis_rectangles(ctx->hdc, &dst, NULL, 0, 0, NULL))
368 return TRUE;
369 return PSDRV_PatBlt(ctx, &dst, blt->dwRop);
372 src.log_x = blt->xSrc;
373 src.log_y = blt->ySrc;
374 src.log_width = blt->cxSrc;
375 src.log_height = blt->cySrc;
376 src.layout = 0;
378 if (!get_vis_rectangles(ctx->hdc, &dst, &blt->xformSrc,
379 bi->bmiHeader.biWidth, abs(bi->bmiHeader.biHeight), &src))
380 return TRUE;
382 memcpy(dst_info, bi, blt->cbBmiSrc);
383 memset(&bits, 0, sizeof(bits));
384 bits.ptr = (BYTE *)src_bits;
385 err = PSDRV_PutImage(ctx, 0, dst_info, &bits, &src, &dst, blt->dwRop);
386 if (err == ERROR_BAD_FORMAT)
388 HDC hdc = CreateCompatibleDC(NULL);
389 HBITMAP bitmap;
391 bits.is_copy = TRUE;
392 bitmap = CreateDIBSection(hdc, dst_info, DIB_RGB_COLORS, &bits.ptr, NULL, 0);
393 SetDIBits(hdc, bitmap, 0, abs(bi->bmiHeader.biHeight), src_bits, bi, blt->iUsageSrc);
395 err = PSDRV_PutImage(ctx, 0, dst_info, &bits, &src, &dst, blt->dwRop);
396 DeleteObject(bitmap);
397 DeleteObject(hdc);
400 if (err != ERROR_SUCCESS)
401 FIXME("PutImage returned %ld\n", err);
402 return !err;
405 #define FRGND_ROP3(ROP4) ((ROP4) & 0x00FFFFFF)
406 #define BKGND_ROP3(ROP4) (ROP3Table[((ROP4)>>24) & 0xFF])
408 static int mask_blt(print_ctx *ctx, const EMRMASKBLT *p, const BITMAPINFO *src_bi,
409 const BYTE *src_bits, const BITMAPINFO *mask_bi, const BYTE *mask_bits)
411 HBITMAP bmp1, old_bmp1, bmp2, old_bmp2, bmp_src, old_bmp_src;
412 char bmp2_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
413 BITMAPINFO *bmp2_info = (BITMAPINFO *)bmp2_buffer;
414 HBRUSH brush_mask, brush_dest, old_brush;
415 HDC hdc_src, hdc1, hdc2;
416 BYTE *bits;
417 EMRSTRETCHBLT blt;
419 static const DWORD ROP3Table[256] =
421 0x00000042, 0x00010289,
422 0x00020C89, 0x000300AA,
423 0x00040C88, 0x000500A9,
424 0x00060865, 0x000702C5,
425 0x00080F08, 0x00090245,
426 0x000A0329, 0x000B0B2A,
427 0x000C0324, 0x000D0B25,
428 0x000E08A5, 0x000F0001,
429 0x00100C85, 0x001100A6,
430 0x00120868, 0x001302C8,
431 0x00140869, 0x001502C9,
432 0x00165CCA, 0x00171D54,
433 0x00180D59, 0x00191CC8,
434 0x001A06C5, 0x001B0768,
435 0x001C06CA, 0x001D0766,
436 0x001E01A5, 0x001F0385,
437 0x00200F09, 0x00210248,
438 0x00220326, 0x00230B24,
439 0x00240D55, 0x00251CC5,
440 0x002606C8, 0x00271868,
441 0x00280369, 0x002916CA,
442 0x002A0CC9, 0x002B1D58,
443 0x002C0784, 0x002D060A,
444 0x002E064A, 0x002F0E2A,
445 0x0030032A, 0x00310B28,
446 0x00320688, 0x00330008,
447 0x003406C4, 0x00351864,
448 0x003601A8, 0x00370388,
449 0x0038078A, 0x00390604,
450 0x003A0644, 0x003B0E24,
451 0x003C004A, 0x003D18A4,
452 0x003E1B24, 0x003F00EA,
453 0x00400F0A, 0x00410249,
454 0x00420D5D, 0x00431CC4,
455 0x00440328, 0x00450B29,
456 0x004606C6, 0x0047076A,
457 0x00480368, 0x004916C5,
458 0x004A0789, 0x004B0605,
459 0x004C0CC8, 0x004D1954,
460 0x004E0645, 0x004F0E25,
461 0x00500325, 0x00510B26,
462 0x005206C9, 0x00530764,
463 0x005408A9, 0x00550009,
464 0x005601A9, 0x00570389,
465 0x00580785, 0x00590609,
466 0x005A0049, 0x005B18A9,
467 0x005C0649, 0x005D0E29,
468 0x005E1B29, 0x005F00E9,
469 0x00600365, 0x006116C6,
470 0x00620786, 0x00630608,
471 0x00640788, 0x00650606,
472 0x00660046, 0x006718A8,
473 0x006858A6, 0x00690145,
474 0x006A01E9, 0x006B178A,
475 0x006C01E8, 0x006D1785,
476 0x006E1E28, 0x006F0C65,
477 0x00700CC5, 0x00711D5C,
478 0x00720648, 0x00730E28,
479 0x00740646, 0x00750E26,
480 0x00761B28, 0x007700E6,
481 0x007801E5, 0x00791786,
482 0x007A1E29, 0x007B0C68,
483 0x007C1E24, 0x007D0C69,
484 0x007E0955, 0x007F03C9,
485 0x008003E9, 0x00810975,
486 0x00820C49, 0x00831E04,
487 0x00840C48, 0x00851E05,
488 0x008617A6, 0x008701C5,
489 0x008800C6, 0x00891B08,
490 0x008A0E06, 0x008B0666,
491 0x008C0E08, 0x008D0668,
492 0x008E1D7C, 0x008F0CE5,
493 0x00900C45, 0x00911E08,
494 0x009217A9, 0x009301C4,
495 0x009417AA, 0x009501C9,
496 0x00960169, 0x0097588A,
497 0x00981888, 0x00990066,
498 0x009A0709, 0x009B07A8,
499 0x009C0704, 0x009D07A6,
500 0x009E16E6, 0x009F0345,
501 0x00A000C9, 0x00A11B05,
502 0x00A20E09, 0x00A30669,
503 0x00A41885, 0x00A50065,
504 0x00A60706, 0x00A707A5,
505 0x00A803A9, 0x00A90189,
506 0x00AA0029, 0x00AB0889,
507 0x00AC0744, 0x00AD06E9,
508 0x00AE0B06, 0x00AF0229,
509 0x00B00E05, 0x00B10665,
510 0x00B21974, 0x00B30CE8,
511 0x00B4070A, 0x00B507A9,
512 0x00B616E9, 0x00B70348,
513 0x00B8074A, 0x00B906E6,
514 0x00BA0B09, 0x00BB0226,
515 0x00BC1CE4, 0x00BD0D7D,
516 0x00BE0269, 0x00BF08C9,
517 0x00C000CA, 0x00C11B04,
518 0x00C21884, 0x00C3006A,
519 0x00C40E04, 0x00C50664,
520 0x00C60708, 0x00C707AA,
521 0x00C803A8, 0x00C90184,
522 0x00CA0749, 0x00CB06E4,
523 0x00CC0020, 0x00CD0888,
524 0x00CE0B08, 0x00CF0224,
525 0x00D00E0A, 0x00D1066A,
526 0x00D20705, 0x00D307A4,
527 0x00D41D78, 0x00D50CE9,
528 0x00D616EA, 0x00D70349,
529 0x00D80745, 0x00D906E8,
530 0x00DA1CE9, 0x00DB0D75,
531 0x00DC0B04, 0x00DD0228,
532 0x00DE0268, 0x00DF08C8,
533 0x00E003A5, 0x00E10185,
534 0x00E20746, 0x00E306EA,
535 0x00E40748, 0x00E506E5,
536 0x00E61CE8, 0x00E70D79,
537 0x00E81D74, 0x00E95CE6,
538 0x00EA02E9, 0x00EB0849,
539 0x00EC02E8, 0x00ED0848,
540 0x00EE0086, 0x00EF0A08,
541 0x00F00021, 0x00F10885,
542 0x00F20B05, 0x00F3022A,
543 0x00F40B0A, 0x00F50225,
544 0x00F60265, 0x00F708C5,
545 0x00F802E5, 0x00F90845,
546 0x00FA0089, 0x00FB0A09,
547 0x00FC008A, 0x00FD0A0A,
548 0x00FE02A9, 0x00FF0062,
551 if (!p->cbBmiMask)
553 blt.rclBounds = p->rclBounds;
554 blt.xDest = p->xDest;
555 blt.yDest = p->yDest;
556 blt.cxDest = p->cxDest;
557 blt.cyDest = p->cyDest;
558 blt.dwRop = FRGND_ROP3(p->dwRop);
559 blt.xSrc = p->xSrc;
560 blt.ySrc = p->ySrc;
561 blt.xformSrc = p->xformSrc;
562 blt.crBkColorSrc = p->crBkColorSrc;
563 blt.iUsageSrc = p->iUsageSrc;
564 blt.offBmiSrc = 0;
565 blt.cbBmiSrc = p->cbBmiSrc;
566 blt.offBitsSrc = 0;
567 blt.cbBitsSrc = p->cbBitsSrc;
568 blt.cxSrc = p->cxDest;
569 blt.cySrc = p->cyDest;
571 return stretch_blt(ctx, &blt, src_bi, src_bits);
574 hdc_src = CreateCompatibleDC(NULL);
575 SetGraphicsMode(hdc_src, GM_ADVANCED);
576 SetWorldTransform(hdc_src, &p->xformSrc);
577 brush_dest = CreateSolidBrush(p->crBkColorSrc);
578 old_brush = SelectObject(hdc_src, brush_dest);
579 PatBlt(hdc_src, p->rclBounds.left, p->rclBounds.top,
580 p->rclBounds.right - p->rclBounds.left,
581 p->rclBounds.bottom - p->rclBounds.top, PATCOPY);
582 SelectObject(hdc_src, old_brush);
583 DeleteObject(brush_dest);
585 bmp_src = CreateDIBSection(hdc_src, src_bi, p->iUsageSrc, (void **)&bits, NULL, 0);
586 memcpy(bits, src_bits, p->cbBitsSrc);
587 old_bmp_src = SelectObject(hdc_src, bmp_src);
589 bmp1 = CreateBitmap(mask_bi->bmiHeader.biWidth, mask_bi->bmiHeader.biHeight, 1, 1, NULL);
590 SetDIBits(ctx->hdc, bmp1, 0, mask_bi->bmiHeader.biHeight, mask_bits, mask_bi, p->iUsageMask);
591 brush_mask = CreatePatternBrush(bmp1);
592 DeleteObject(bmp1);
593 brush_dest = SelectObject(ctx->hdc, GetStockObject(NULL_BRUSH));
595 /* make bitmap */
596 hdc1 = CreateCompatibleDC(NULL);
597 bmp1 = CreateBitmap(p->cxDest, p->cyDest, 1, 32, NULL);
598 old_bmp1 = SelectObject(hdc1, bmp1);
600 /* draw using bkgnd rop */
601 old_brush = SelectObject(hdc1, brush_dest);
602 BitBlt(hdc1, 0, 0, p->cxDest, p->cyDest, hdc_src, p->xSrc, p->ySrc, BKGND_ROP3(p->dwRop));
603 SelectObject(hdc1, old_brush);
605 /* make bitmap */
606 hdc2 = CreateCompatibleDC(NULL);
607 bmp2_info->bmiHeader.biSize = sizeof(bmp2_info->bmiHeader);
608 bmp2_info->bmiHeader.biWidth = p->cxDest;
609 bmp2_info->bmiHeader.biHeight = p->cyDest;
610 bmp2_info->bmiHeader.biPlanes = 1;
611 bmp2_info->bmiHeader.biBitCount = 32;
612 bmp2_info->bmiHeader.biCompression = BI_RGB;
613 bmp2_info->bmiHeader.biSizeImage = p->cxDest * p->cyDest * bmp2_info->bmiHeader.biBitCount / 8;
614 bmp2 = CreateDIBSection(hdc2, bmp2_info, DIB_RGB_COLORS, (void **)&bits, NULL, 0);
615 old_bmp2 = SelectObject(hdc2, bmp2);
617 /* draw using foregnd rop */
618 old_brush = SelectObject(hdc2, brush_dest);
619 BitBlt(hdc2, 0, 0, p->cxDest, p->cyDest, hdc_src, p->xSrc, p->ySrc, FRGND_ROP3(p->dwRop));
621 /* combine both using the mask as a pattern brush */
622 SelectObject(hdc2, brush_mask);
623 SetBrushOrgEx(hdc2, -p->xMask, -p->yMask, NULL);
624 /* (D & P) | (S & ~P) */
625 BitBlt(hdc2, 0, 0, p->cxDest, p->cyDest, hdc1, 0, 0, 0xac0744);
626 SelectObject(hdc2, old_brush);
628 /* blit to dst */
629 blt.rclBounds = p->rclBounds;
630 blt.xDest = p->xDest;
631 blt.yDest = p->yDest;
632 blt.cxDest = p->cxDest;
633 blt.cyDest = p->cyDest;
634 blt.dwRop = SRCCOPY;
635 blt.xSrc = 0;
636 blt.ySrc = 0;
637 GetTransform(hdc2, 0x204, &blt.xformSrc);
638 blt.crBkColorSrc = p->crBkColorSrc;
639 blt.iUsageSrc = DIB_RGB_COLORS;
640 blt.offBmiSrc = 0;
641 blt.cbBmiSrc = bmp2_info->bmiHeader.biSize;
642 blt.offBitsSrc = 0;
643 blt.cbBitsSrc = bmp2_info->bmiHeader.biSizeImage;
644 blt.cxSrc = p->cxDest;
645 blt.cySrc = p->cyDest;
647 stretch_blt(ctx, &blt, bmp2_info, bits);
649 /* restore all objects */
650 SelectObject(ctx->hdc, brush_dest);
651 SelectObject(hdc1, old_bmp1);
652 SelectObject(hdc2, old_bmp2);
653 SelectObject(hdc_src, old_bmp_src);
655 /* delete all temp objects */
656 DeleteObject(bmp1);
657 DeleteObject(bmp2);
658 DeleteObject(bmp_src);
659 DeleteObject(brush_mask);
661 DeleteObject(hdc1);
662 DeleteObject(hdc2);
663 DeleteObject(hdc_src);
665 return TRUE;
668 static void combine_transform(XFORM *result, const XFORM *xform1, const XFORM *xform2)
670 XFORM r;
672 /* Create the result in a temporary XFORM, since result may be
673 * equal to xform1 or xform2 */
674 r.eM11 = xform1->eM11 * xform2->eM11 + xform1->eM12 * xform2->eM21;
675 r.eM12 = xform1->eM11 * xform2->eM12 + xform1->eM12 * xform2->eM22;
676 r.eM21 = xform1->eM21 * xform2->eM11 + xform1->eM22 * xform2->eM21;
677 r.eM22 = xform1->eM21 * xform2->eM12 + xform1->eM22 * xform2->eM22;
678 r.eDx = xform1->eDx * xform2->eM11 + xform1->eDy * xform2->eM21 + xform2->eDx;
679 r.eDy = xform1->eDx * xform2->eM12 + xform1->eDy * xform2->eM22 + xform2->eDy;
681 *result = r;
684 static int plg_blt(print_ctx *ctx, const EMRPLGBLT *p)
686 const BITMAPINFO *src_bi, *mask_bi;
687 const BYTE *src_bits, *mask_bits;
688 XFORM xf, xform_dest;
689 EMRMASKBLT maskblt;
690 /* rect coords */
691 POINT rect[3];
692 /* parallelogram coords */
693 POINT plg[3];
694 double det;
696 memcpy(plg, p->aptlDest, sizeof(plg));
697 rect[0].x = p->xSrc;
698 rect[0].y = p->ySrc;
699 rect[1].x = p->xSrc + p->cxSrc;
700 rect[1].y = p->ySrc;
701 rect[2].x = p->xSrc;
702 rect[2].y = p->ySrc + p->cySrc;
703 /* calc XFORM matrix to transform hdcDest -> hdcSrc (parallelogram to rectangle) */
704 /* determinant */
705 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);
707 if (fabs(det) < 1e-5)
708 return TRUE;
710 TRACE("%ld,%ld,%ldx%ld -> %ld,%ld,%ld,%ld,%ld,%ld\n", p->xSrc, p->ySrc, p->cxSrc, p->cySrc,
711 plg[0].x, plg[0].y, plg[1].x, plg[1].y, plg[2].x, plg[2].y);
713 /* X components */
714 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;
715 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;
716 xf.eDx = (rect[0].x*(rect[1].y*plg[2].x - rect[2].y*plg[1].x) -
717 rect[1].x*(rect[0].y*plg[2].x - rect[2].y*plg[0].x) +
718 rect[2].x*(rect[0].y*plg[1].x - rect[1].y*plg[0].x)
719 ) / det;
721 /* Y components */
722 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;
723 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;
724 xf.eDy = (rect[0].x*(rect[1].y*plg[2].y - rect[2].y*plg[1].y) -
725 rect[1].x*(rect[0].y*plg[2].y - rect[2].y*plg[0].y) +
726 rect[2].x*(rect[0].y*plg[1].y - rect[1].y*plg[0].y)
727 ) / det;
729 combine_transform(&xf, &xf, &p->xformSrc);
731 GetTransform(ctx->hdc, 0x203, &xform_dest);
732 SetWorldTransform(ctx->hdc, &xf);
733 /* now destination and source DCs use same coords */
734 maskblt.rclBounds = p->rclBounds;
735 maskblt.xDest = p->xSrc;
736 maskblt.yDest = p->ySrc;
737 maskblt.cxDest = p->cxSrc;
738 maskblt.cyDest = p->cySrc;
739 maskblt.dwRop = SRCCOPY;
740 maskblt.xSrc = p->xSrc;
741 maskblt.ySrc = p->ySrc;
742 maskblt.xformSrc = p->xformSrc;
743 maskblt.crBkColorSrc = p->crBkColorSrc;
744 maskblt.iUsageSrc = p->iUsageSrc;
745 maskblt.offBmiSrc = 0;
746 maskblt.cbBmiSrc = p->cbBmiSrc;
747 maskblt.offBitsSrc = 0;
748 maskblt.cbBitsSrc = p->cbBitsSrc;
749 maskblt.xMask = p->xMask;
750 maskblt.yMask = p->yMask;
751 maskblt.iUsageMask = p->iUsageMask;
752 maskblt.offBmiMask = 0;
753 maskblt.cbBmiMask = p->cbBmiMask;
754 maskblt.offBitsMask = 0;
755 maskblt.cbBitsMask = p->cbBitsMask;
756 src_bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiSrc);
757 src_bits = (BYTE *)p + p->offBitsSrc;
758 mask_bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiMask);
759 mask_bits = (BYTE *)p + p->offBitsMask;
760 mask_blt(ctx, &maskblt, src_bi, src_bits, mask_bi, mask_bits);
761 SetWorldTransform(ctx->hdc, &xform_dest);
762 return TRUE;
765 static inline int get_dib_stride( int width, int bpp )
767 return ((width * bpp + 31) >> 3) & ~3;
770 static inline int get_dib_image_size( const BITMAPINFO *info )
772 return get_dib_stride( info->bmiHeader.biWidth, info->bmiHeader.biBitCount )
773 * abs( info->bmiHeader.biHeight );
776 static int set_di_bits_to_device(print_ctx *ctx, const EMRSETDIBITSTODEVICE *p)
778 const BITMAPINFO *info = (const BITMAPINFO *)((BYTE *)p + p->offBmiSrc);
779 char bi_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
780 BITMAPINFO *bi = (BITMAPINFO *)bi_buffer;
781 HBITMAP bitmap, old_bitmap;
782 int width, height, ret;
783 BYTE *bits;
785 width = min(p->cxSrc, info->bmiHeader.biWidth);
786 height = min(p->cySrc, abs(info->bmiHeader.biHeight));
788 memset(bi_buffer, 0, sizeof(bi_buffer));
789 bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
790 bi->bmiHeader.biWidth = width;
791 bi->bmiHeader.biHeight = height;
792 bi->bmiHeader.biPlanes = 1;
793 if (p->iUsageSrc == DIB_PAL_COLORS && (info->bmiHeader.biBitCount == 1 ||
794 info->bmiHeader.biBitCount == 4 || info->bmiHeader.biBitCount == 8))
796 PALETTEENTRY pal[256];
797 HPALETTE hpal;
798 UINT i, size;
800 bi->bmiHeader.biBitCount = info->bmiHeader.biBitCount;
801 bi->bmiHeader.biClrUsed = 1 << info->bmiHeader.biBitCount;
802 bi->bmiHeader.biClrImportant = bi->bmiHeader.biClrUsed;
804 hpal = GetCurrentObject(ctx->hdc, OBJ_PAL);
805 size = GetPaletteEntries(hpal, 0, bi->bmiHeader.biClrUsed, pal);
806 for (i = 0; i < size; i++)
808 bi->bmiColors[i].rgbBlue = pal[i].peBlue;
809 bi->bmiColors[i].rgbGreen = pal[i].peGreen;
810 bi->bmiColors[i].rgbRed = pal[i].peRed;
813 else
815 bi->bmiHeader.biBitCount = 24;
817 bi->bmiHeader.biCompression = BI_RGB;
818 bitmap = CreateDIBSection(ctx->hdc, bi, DIB_RGB_COLORS, (void **)&bits, NULL, 0);
819 if (!bitmap)
820 return 1;
821 old_bitmap = SelectObject(ctx->hdc, bitmap);
823 ret = SetDIBitsToDevice(ctx->hdc, 0, 0, width, height, p->xSrc, p->ySrc,
824 p->iStartScan, p->cScans, (const BYTE*)p + p->offBitsSrc, info, p->iUsageSrc);
825 SelectObject(ctx->hdc, old_bitmap);
826 if (ret)
828 EMRSTRETCHBLT blt;
830 memset(&blt, 0, sizeof(blt));
831 blt.rclBounds = p->rclBounds;
832 blt.xDest = p->xDest;
833 blt.yDest = p->yDest + p->cySrc - height;
834 blt.cxDest = width;
835 blt.cyDest = ret;
836 blt.dwRop = SRCCOPY;
837 blt.xformSrc.eM11 = 1;
838 blt.xformSrc.eM22 = 1;
839 blt.iUsageSrc = DIB_RGB_COLORS;
840 blt.cbBmiSrc = sizeof(bi_buffer);
841 blt.cbBitsSrc = get_dib_image_size(bi);
842 blt.cxSrc = blt.cxDest;
843 blt.cySrc = blt.cyDest;
844 stretch_blt(ctx, &blt, bi, bits);
847 DeleteObject(bitmap);
848 return 1;
851 static int stretch_di_bits(print_ctx *ctx, const EMRSTRETCHDIBITS *p)
853 char bi_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
854 const BYTE *bits = (BYTE *)p + p->offBitsSrc;
855 BITMAPINFO *bi = (BITMAPINFO *)bi_buffer;
856 EMRSTRETCHBLT blt;
858 memcpy(bi, (BYTE *)p + p->offBmiSrc, p->cbBmiSrc);
859 memset(bi_buffer + p->cbBmiSrc, 0, sizeof(bi_buffer) - p->cbBmiSrc);
861 if (p->iUsageSrc == DIB_PAL_COLORS && (bi->bmiHeader.biBitCount == 1 ||
862 bi->bmiHeader.biBitCount == 4 || bi->bmiHeader.biBitCount == 8))
864 PALETTEENTRY pal[256];
865 HPALETTE hpal;
866 UINT i, size;
868 hpal = GetCurrentObject(ctx->hdc, OBJ_PAL);
869 size = GetPaletteEntries(hpal, 0, 1 << bi->bmiHeader.biBitCount, pal);
870 for (i = 0; i < size; i++)
872 bi->bmiColors[i].rgbBlue = pal[i].peBlue;
873 bi->bmiColors[i].rgbGreen = pal[i].peGreen;
874 bi->bmiColors[i].rgbRed = pal[i].peRed;
878 memset(&blt, 0, sizeof(blt));
879 blt.rclBounds = p->rclBounds;
880 blt.xDest = p->xDest;
881 blt.yDest = p->yDest;
882 blt.cxDest = p->cxDest;
883 blt.cyDest = p->cyDest;
884 blt.dwRop = p->dwRop;
885 blt.xSrc = p->xSrc;
886 blt.ySrc = abs(bi->bmiHeader.biHeight) - p->ySrc - p->cySrc;
887 blt.xformSrc.eM11 = 1;
888 blt.xformSrc.eM22 = 1;
889 blt.iUsageSrc = p->iUsageSrc;
890 blt.cbBmiSrc = sizeof(bi_buffer);
891 blt.cbBitsSrc = p->cbBitsSrc;
892 blt.cxSrc = p->cxSrc;
893 blt.cySrc = p->cySrc;
894 return stretch_blt(ctx, &blt, bi, bits);
897 static int poly_draw(print_ctx *ctx, const POINT *points, const BYTE *types, DWORD count)
899 POINT first, cur, pts[4];
900 DWORD i, num_pts;
902 /* check for valid point types */
903 for (i = 0; i < count; i++)
905 switch (types[i])
907 case PT_MOVETO:
908 case PT_LINETO | PT_CLOSEFIGURE:
909 case PT_LINETO:
910 break;
911 case PT_BEZIERTO:
912 if (i + 2 >= count) return FALSE;
913 if (types[i + 1] != PT_BEZIERTO) return FALSE;
914 if ((types[i + 2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO) return FALSE;
915 i += 2;
916 break;
917 default:
918 return FALSE;
922 GetCurrentPositionEx(ctx->hdc, &cur);
923 first = cur;
925 for (i = 0; i < count; i++)
927 switch (types[i])
929 case PT_MOVETO:
930 first = points[i];
931 break;
932 case PT_LINETO:
933 case (PT_LINETO | PT_CLOSEFIGURE):
934 pts[0] = cur;
935 pts[1] = points[i];
936 num_pts = 2;
937 if (!PSDRV_PolyPolyline(ctx, pts, &num_pts, 1))
938 return FALSE;
939 break;
940 case PT_BEZIERTO:
941 pts[0] = cur;
942 pts[1] = points[i];
943 pts[2] = points[i + 1];
944 pts[3] = points[i + 2];
945 if (!PSDRV_PolyBezier(ctx, pts, 4))
946 return FALSE;
947 i += 2;
948 break;
951 cur = points[i];
952 if (types[i] & PT_CLOSEFIGURE)
954 pts[0] = cur;
955 pts[1] = first;
956 num_pts = 2;
957 if (!PSDRV_PolyPolyline(ctx, pts, &num_pts, 1))
958 return FALSE;
962 return TRUE;
965 static inline void reset_bounds(RECT *bounds)
967 bounds->left = bounds->top = INT_MAX;
968 bounds->right = bounds->bottom = INT_MIN;
971 static BOOL gradient_fill(print_ctx *ctx, const TRIVERTEX *vert_array, DWORD nvert,
972 const void *grad_array, DWORD ngrad, ULONG mode)
974 char buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
975 BITMAPINFO *info = (BITMAPINFO *)buffer;
976 struct ps_bitblt_coords src, dst;
977 struct ps_image_bits bits;
978 HBITMAP bmp, old_bmp;
979 BOOL ret = FALSE;
980 TRIVERTEX *pts;
981 unsigned int i;
982 HDC hdc_src;
983 HRGN rgn;
985 if (!(pts = malloc(nvert * sizeof(*pts)))) return FALSE;
986 memcpy(pts, vert_array, sizeof(*pts) * nvert);
987 for (i = 0; i < nvert; i++)
988 LPtoDP(ctx->hdc, (POINT *)&pts[i], 1);
990 /* compute bounding rect of all the rectangles/triangles */
991 reset_bounds(&dst.visrect);
992 for (i = 0; i < ngrad * (mode == GRADIENT_FILL_TRIANGLE ? 3 : 2); i++)
994 ULONG v = ((ULONG *)grad_array)[i];
995 dst.visrect.left = min(dst.visrect.left, pts[v].x);
996 dst.visrect.top = min(dst.visrect.top, pts[v].y);
997 dst.visrect.right = max(dst.visrect.right, pts[v].x);
998 dst.visrect.bottom = max(dst.visrect.bottom, pts[v].y);
1001 dst.x = dst.visrect.left;
1002 dst.y = dst.visrect.top;
1003 dst.width = dst.visrect.right - dst.visrect.left;
1004 dst.height = dst.visrect.bottom - dst.visrect.top;
1005 clip_visrect(ctx->hdc, &dst.visrect, &dst.visrect);
1007 info->bmiHeader.biSize = sizeof(info->bmiHeader);
1008 info->bmiHeader.biPlanes = 1;
1009 info->bmiHeader.biBitCount = 24;
1010 info->bmiHeader.biCompression = BI_RGB;
1011 info->bmiHeader.biXPelsPerMeter = 0;
1012 info->bmiHeader.biYPelsPerMeter = 0;
1013 info->bmiHeader.biClrUsed = 0;
1014 info->bmiHeader.biClrImportant = 0;
1015 info->bmiHeader.biWidth = dst.visrect.right - dst.visrect.left;
1016 info->bmiHeader.biHeight = dst.visrect.bottom - dst.visrect.top;
1017 info->bmiHeader.biSizeImage = 0;
1018 memset(&bits, 0, sizeof(bits));
1019 hdc_src = CreateCompatibleDC(NULL);
1020 if (!hdc_src)
1022 free(pts);
1023 return FALSE;
1025 bmp = CreateDIBSection(hdc_src, info, DIB_RGB_COLORS, &bits.ptr, NULL, 0);
1026 if (!bmp)
1028 DeleteObject(hdc_src);
1029 free(pts);
1030 return FALSE;
1032 old_bmp = SelectObject(hdc_src, bmp);
1034 /* make src and points relative to the bitmap */
1035 src = dst;
1036 src.x -= dst.visrect.left;
1037 src.y -= dst.visrect.top;
1038 OffsetRect(&src.visrect, -dst.visrect.left, -dst.visrect.top);
1039 for (i = 0; i < nvert; i++)
1041 pts[i].x -= dst.visrect.left;
1042 pts[i].y -= dst.visrect.top;
1044 ret = GdiGradientFill(hdc_src, pts, nvert, (void *)grad_array, ngrad, mode);
1045 SelectObject(hdc_src, old_bmp);
1046 DeleteObject(hdc_src);
1048 rgn = CreateRectRgn(0, 0, 0, 0);
1049 if (mode == GRADIENT_FILL_TRIANGLE)
1051 const GRADIENT_TRIANGLE *gt = grad_array;
1052 POINT triangle[3];
1053 HRGN tmp;
1055 for (i = 0; i < ngrad; i++)
1057 triangle[0].x = pts[gt[i].Vertex1].x;
1058 triangle[0].y = pts[gt[i].Vertex1].y;
1059 triangle[1].x = pts[gt[i].Vertex2].x;
1060 triangle[1].y = pts[gt[i].Vertex2].y;
1061 triangle[2].x = pts[gt[i].Vertex3].x;
1062 triangle[2].y = pts[gt[i].Vertex3].y;
1063 tmp = CreatePolygonRgn(triangle, 3, ALTERNATE);
1064 CombineRgn(rgn, rgn, tmp, RGN_OR);
1065 DeleteObject(tmp);
1068 else
1070 const GRADIENT_RECT *gr = grad_array;
1071 HRGN tmp = CreateRectRgn(0, 0, 0, 0);
1073 for (i = 0; i < ngrad; i++)
1075 SetRectRgn(tmp, pts[gr[i].UpperLeft].x, pts[gr[i].UpperLeft].y,
1076 pts[gr[i].LowerRight].x, pts[gr[i].LowerRight].y);
1077 CombineRgn(rgn, rgn, tmp, RGN_OR);
1079 DeleteObject(tmp);
1081 free(pts);
1083 OffsetRgn(rgn, dst.visrect.left, dst.visrect.top);
1084 if (ret)
1085 ret = (PSDRV_PutImage(ctx, rgn, info, &bits, &src, &dst, SRCCOPY) == ERROR_SUCCESS);
1086 DeleteObject(rgn);
1087 DeleteObject(bmp);
1088 return ret;
1091 static HGDIOBJ get_object_handle(struct pp_data *data, HANDLETABLE *handletable,
1092 DWORD i, struct ps_brush_pattern **pattern)
1094 if (i & 0x80000000)
1096 *pattern = NULL;
1097 return GetStockObject(i & 0x7fffffff);
1099 *pattern = data->patterns + i;
1100 return handletable->objectHandle[i];
1103 static BOOL select_hbrush(struct pp_data *data, HANDLETABLE *htable, int handle_count, HBRUSH brush)
1105 struct ps_brush_pattern *pattern = NULL;
1106 int i;
1108 for (i = 0; i < handle_count; i++)
1110 if (htable->objectHandle[i] == brush)
1112 pattern = data->patterns + i;
1113 break;
1117 return PSDRV_SelectBrush(data->ctx, brush, pattern) != NULL;
1120 /* Performs a device to world transformation on the specified size (which
1121 * is in integer format).
1123 static inline INT INTERNAL_YWSTODS(HDC hdc, INT height)
1125 POINT pt[2];
1126 pt[0].x = pt[0].y = 0;
1127 pt[1].x = 0;
1128 pt[1].y = height;
1129 LPtoDP(hdc, pt, 2);
1130 return pt[1].y - pt[0].y;
1133 extern const unsigned short bidi_direction_table[];
1135 /*------------------------------------------------------------------------
1136 Bidirectional Character Types
1138 as defined by the Unicode Bidirectional Algorithm Table 3-7.
1140 Note:
1142 The list of bidirectional character types here is not grouped the
1143 same way as the table 3-7, since the numeric values for the types
1144 are chosen to keep the state and action tables compact.
1145 ------------------------------------------------------------------------*/
1146 enum directions
1148 /* input types */
1149 /* ON MUST be zero, code relies on ON = N = 0 */
1150 ON = 0, /* Other Neutral */
1151 L, /* Left Letter */
1152 R, /* Right Letter */
1153 AN, /* Arabic Number */
1154 EN, /* European Number */
1155 AL, /* Arabic Letter (Right-to-left) */
1156 NSM, /* Non-spacing Mark */
1157 CS, /* Common Separator */
1158 ES, /* European Separator */
1159 ET, /* European Terminator (post/prefix e.g. $ and %) */
1161 /* resolved types */
1162 BN, /* Boundary neutral (type of RLE etc after explicit levels) */
1164 /* input types, */
1165 S, /* Segment Separator (TAB) // used only in L1 */
1166 WS, /* White space // used only in L1 */
1167 B, /* Paragraph Separator (aka as PS) */
1169 /* types for explicit controls */
1170 RLO, /* these are used only in X1-X9 */
1171 RLE,
1172 LRO,
1173 LRE,
1174 PDF,
1176 LRI, /* Isolate formatting characters new with 6.3 */
1177 RLI,
1178 FSI,
1179 PDI,
1181 /* resolved types, also resolved directions */
1182 NI = ON, /* alias, where ON, WS and S are treated the same */
1185 static inline unsigned short get_table_entry_32(const unsigned short *table, UINT ch)
1187 return table[table[table[table[ch >> 12] + ((ch >> 8) & 0x0f)] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
1190 /* Convert the libwine information to the direction enum */
1191 static void classify(LPCWSTR lpString, WORD *chartype, DWORD uCount)
1193 unsigned i;
1195 for (i = 0; i < uCount; ++i)
1196 chartype[i] = get_table_entry_32(bidi_direction_table, lpString[i]);
1199 /* Set a run of cval values at locations all prior to, but not including */
1200 /* iStart, to the new value nval. */
1201 static void SetDeferredRun(BYTE *pval, int cval, int iStart, int nval)
1203 int i = iStart - 1;
1204 for (; i >= iStart - cval; i--)
1206 pval[i] = nval;
1210 /* THE PARAGRAPH LEVEL */
1212 /*------------------------------------------------------------------------
1213 Function: resolveParagraphs
1215 Resolves the input strings into blocks over which the algorithm
1216 is then applied.
1218 Implements Rule P1 of the Unicode Bidi Algorithm
1220 Input: Text string
1221 Character count
1223 Output: revised character count
1225 Note: This is a very simplistic function. In effect it restricts
1226 the action of the algorithm to the first paragraph in the input
1227 where a paragraph ends at the end of the first block separator
1228 or at the end of the input text.
1230 ------------------------------------------------------------------------*/
1232 static int resolveParagraphs(WORD *types, int cch)
1234 /* skip characters not of type B */
1235 int ich = 0;
1236 for(; ich < cch && types[ich] != B; ich++);
1237 /* stop after first B, make it a BN for use in the next steps */
1238 if (ich < cch && types[ich] == B)
1239 types[ich++] = BN;
1240 return ich;
1243 /* REORDER */
1244 /*------------------------------------------------------------------------
1245 Function: resolveLines
1247 Breaks a paragraph into lines
1249 Input: Array of line break flags
1250 Character count
1251 In/Out: Array of characters
1253 Returns the count of characters on the first line
1255 Note: This function only breaks lines at hard line breaks. Other
1256 line breaks can be passed in. If pbrk[n] is TRUE, then a break
1257 occurs after the character in pszInput[n]. Breaks before the first
1258 character are not allowed.
1259 ------------------------------------------------------------------------*/
1260 static int resolveLines(LPCWSTR pszInput, const BOOL * pbrk, int cch)
1262 /* skip characters not of type LS */
1263 int ich = 0;
1264 for(; ich < cch; ich++)
1266 if (pszInput[ich] == (WCHAR)'\n' || (pbrk && pbrk[ich]))
1268 ich++;
1269 break;
1273 return ich;
1276 /*------------------------------------------------------------------------
1277 Function: resolveWhiteSpace
1279 Resolves levels for WS and S
1280 Implements rule L1 of the Unicode bidi Algorithm.
1282 Input: Base embedding level
1283 Character count
1284 Array of direction classes (for one line of text)
1286 In/Out: Array of embedding levels (for one line of text)
1288 Note: this should be applied a line at a time. The default driver
1289 code supplied in this file assumes a single line of text; for
1290 a real implementation, cch and the initial pointer values
1291 would have to be adjusted.
1292 ------------------------------------------------------------------------*/
1293 static void resolveWhitespace(int baselevel, const WORD *pcls, BYTE *plevel, int cch)
1295 int cchrun = 0;
1296 BYTE oldlevel = baselevel;
1298 int ich = 0;
1299 for (; ich < cch; ich++)
1301 switch(pcls[ich])
1303 default:
1304 cchrun = 0; /* any other character breaks the run */
1305 break;
1306 case WS:
1307 cchrun++;
1308 break;
1310 case RLE:
1311 case LRE:
1312 case LRO:
1313 case RLO:
1314 case PDF:
1315 case LRI:
1316 case RLI:
1317 case FSI:
1318 case PDI:
1319 case BN:
1320 plevel[ich] = oldlevel;
1321 cchrun++;
1322 break;
1324 case S:
1325 case B:
1326 /* reset levels for WS before eot */
1327 SetDeferredRun(plevel, cchrun, ich, baselevel);
1328 cchrun = 0;
1329 plevel[ich] = baselevel;
1330 break;
1332 oldlevel = plevel[ich];
1334 /* reset level before eot */
1335 SetDeferredRun(plevel, cchrun, ich, baselevel);
1338 /*------------------------------------------------------------------------
1339 Function: BidiLines
1341 Implements the Line-by-Line phases of the Unicode Bidi Algorithm
1343 Input: Count of characters
1344 Array of character directions
1346 Inp/Out: Input text
1347 Array of levels
1349 ------------------------------------------------------------------------*/
1350 static void BidiLines(int baselevel, LPWSTR pszOutLine, LPCWSTR pszLine, const WORD * pclsLine,
1351 BYTE * plevelLine, int cchPara, const BOOL * pbrk)
1353 int cchLine = 0;
1354 int done = 0;
1355 int *run;
1357 run = HeapAlloc(GetProcessHeap(), 0, cchPara * sizeof(int));
1358 if (!run)
1360 WARN("Out of memory\n");
1361 return;
1366 /* break lines at LS */
1367 cchLine = resolveLines(pszLine, pbrk, cchPara);
1369 /* resolve whitespace */
1370 resolveWhitespace(baselevel, pclsLine, plevelLine, cchLine);
1372 if (pszOutLine)
1374 int i;
1375 /* reorder each line in place */
1376 ScriptLayout(cchLine, plevelLine, NULL, run);
1377 for (i = 0; i < cchLine; i++)
1378 pszOutLine[done+run[i]] = pszLine[i];
1381 pszLine += cchLine;
1382 plevelLine += cchLine;
1383 pbrk += pbrk ? cchLine : 0;
1384 pclsLine += cchLine;
1385 cchPara -= cchLine;
1386 done += cchLine;
1388 } while (cchPara);
1390 HeapFree(GetProcessHeap(), 0, run);
1393 #define WINE_GCPW_FORCE_LTR 0
1394 #define WINE_GCPW_FORCE_RTL 1
1395 #define WINE_GCPW_DIR_MASK 3
1397 static BOOL BIDI_Reorder(HDC hDC, /* [in] Display DC */
1398 LPCWSTR lpString, /* [in] The string for which information is to be returned */
1399 INT uCount, /* [in] Number of WCHARs in string. */
1400 DWORD dwFlags, /* [in] GetCharacterPlacement compatible flags */
1401 DWORD dwWineGCP_Flags, /* [in] Wine internal flags - Force paragraph direction */
1402 LPWSTR lpOutString, /* [out] Reordered string */
1403 INT uCountOut, /* [in] Size of output buffer */
1404 UINT *lpOrder, /* [out] Logical -> Visual order map */
1405 WORD **lpGlyphs, /* [out] reordered, mirrored, shaped glyphs to display */
1406 INT *cGlyphs) /* [out] number of glyphs generated */
1408 WORD *chartype = NULL;
1409 BYTE *levels = NULL;
1410 INT i, done;
1411 unsigned glyph_i;
1412 BOOL is_complex, ret = FALSE;
1414 int maxItems;
1415 int nItems;
1416 SCRIPT_CONTROL Control;
1417 SCRIPT_STATE State;
1418 SCRIPT_ITEM *pItems = NULL;
1419 HRESULT res;
1420 SCRIPT_CACHE psc = NULL;
1421 WORD *run_glyphs = NULL;
1422 WORD *pwLogClust = NULL;
1423 SCRIPT_VISATTR *psva = NULL;
1424 DWORD cMaxGlyphs = 0;
1425 BOOL doGlyphs = TRUE;
1427 TRACE("%s, %d, 0x%08lx lpOutString=%p, lpOrder=%p\n",
1428 debugstr_wn(lpString, uCount), uCount, dwFlags,
1429 lpOutString, lpOrder);
1431 memset(&Control, 0, sizeof(Control));
1432 memset(&State, 0, sizeof(State));
1433 if (lpGlyphs)
1434 *lpGlyphs = NULL;
1436 if (!(dwFlags & GCP_REORDER))
1438 FIXME("Asked to reorder without reorder flag set\n");
1439 return FALSE;
1442 if (lpOutString && uCountOut < uCount)
1444 FIXME("lpOutString too small\n");
1445 return FALSE;
1448 chartype = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(WORD));
1449 if (!chartype)
1451 WARN("Out of memory\n");
1452 return FALSE;
1455 if (lpOutString)
1456 memcpy(lpOutString, lpString, uCount * sizeof(WCHAR));
1458 is_complex = FALSE;
1459 for (i = 0; i < uCount && !is_complex; i++)
1461 if ((lpString[i] >= 0x900 && lpString[i] <= 0xfff) ||
1462 (lpString[i] >= 0x1cd0 && lpString[i] <= 0x1cff) ||
1463 (lpString[i] >= 0xa840 && lpString[i] <= 0xa8ff))
1464 is_complex = TRUE;
1467 /* Verify reordering will be required */
1468 if (WINE_GCPW_FORCE_RTL == (dwWineGCP_Flags & WINE_GCPW_DIR_MASK))
1469 State.uBidiLevel = 1;
1470 else if (!is_complex)
1472 done = 1;
1473 classify(lpString, chartype, uCount);
1474 for (i = 0; i < uCount; i++)
1475 switch (chartype[i])
1477 case R:
1478 case AL:
1479 case RLE:
1480 case RLO:
1481 done = 0;
1482 break;
1484 if (done)
1486 HeapFree(GetProcessHeap(), 0, chartype);
1487 if (lpOrder)
1489 for (i = 0; i < uCount; i++)
1490 lpOrder[i] = i;
1492 return TRUE;
1496 levels = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(BYTE));
1497 if (!levels)
1499 WARN("Out of memory\n");
1500 goto cleanup;
1503 maxItems = 5;
1504 pItems = HeapAlloc(GetProcessHeap(),0, maxItems * sizeof(SCRIPT_ITEM));
1505 if (!pItems)
1507 WARN("Out of memory\n");
1508 goto cleanup;
1511 if (lpGlyphs)
1513 cMaxGlyphs = 1.5 * uCount + 16;
1514 run_glyphs = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * cMaxGlyphs);
1515 if (!run_glyphs)
1517 WARN("Out of memory\n");
1518 goto cleanup;
1520 pwLogClust = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * uCount);
1521 if (!pwLogClust)
1523 WARN("Out of memory\n");
1524 goto cleanup;
1526 psva = HeapAlloc(GetProcessHeap(),0,sizeof(SCRIPT_VISATTR) * cMaxGlyphs);
1527 if (!psva)
1529 WARN("Out of memory\n");
1530 goto cleanup;
1534 done = 0;
1535 glyph_i = 0;
1536 while (done < uCount)
1538 INT j;
1539 classify(lpString + done, chartype, uCount - done);
1540 /* limit text to first block */
1541 i = resolveParagraphs(chartype, uCount - done);
1542 for (j = 0; j < i; ++j)
1543 switch(chartype[j])
1545 case B:
1546 case S:
1547 case WS:
1548 case ON: chartype[j] = NI;
1549 default: continue;
1552 res = ScriptItemize(lpString + done, i, maxItems, &Control, &State, pItems, &nItems);
1553 while (res == E_OUTOFMEMORY)
1555 SCRIPT_ITEM *new_pItems = HeapReAlloc(GetProcessHeap(), 0, pItems, sizeof(*pItems) * maxItems * 2);
1556 if (!new_pItems)
1558 WARN("Out of memory\n");
1559 goto cleanup;
1561 pItems = new_pItems;
1562 maxItems *= 2;
1563 res = ScriptItemize(lpString + done, i, maxItems, &Control, &State, pItems, &nItems);
1566 if (lpOutString || lpOrder)
1567 for (j = 0; j < nItems; j++)
1569 int k;
1570 for (k = pItems[j].iCharPos; k < pItems[j+1].iCharPos; k++)
1571 levels[k] = pItems[j].a.s.uBidiLevel;
1574 if (lpOutString)
1576 /* assign directional types again, but for WS, S this time */
1577 classify(lpString + done, chartype, i);
1579 BidiLines(State.uBidiLevel, lpOutString + done, lpString + done,
1580 chartype, levels, i, 0);
1583 if (lpOrder)
1585 int k, lastgood;
1586 for (j = lastgood = 0; j < i; ++j)
1587 if (levels[j] != levels[lastgood])
1589 --j;
1590 if (levels[lastgood] & 1)
1591 for (k = j; k >= lastgood; --k)
1592 lpOrder[done + k] = done + j - k;
1593 else
1594 for (k = lastgood; k <= j; ++k)
1595 lpOrder[done + k] = done + k;
1596 lastgood = ++j;
1598 if (levels[lastgood] & 1)
1599 for (k = j - 1; k >= lastgood; --k)
1600 lpOrder[done + k] = done + j - 1 - k;
1601 else
1602 for (k = lastgood; k < j; ++k)
1603 lpOrder[done + k] = done + k;
1606 if (lpGlyphs && doGlyphs)
1608 BYTE *runOrder;
1609 int *visOrder;
1610 SCRIPT_ITEM *curItem;
1612 runOrder = HeapAlloc(GetProcessHeap(), 0, maxItems * sizeof(*runOrder));
1613 visOrder = HeapAlloc(GetProcessHeap(), 0, maxItems * sizeof(*visOrder));
1614 if (!runOrder || !visOrder)
1616 WARN("Out of memory\n");
1617 HeapFree(GetProcessHeap(), 0, runOrder);
1618 HeapFree(GetProcessHeap(), 0, visOrder);
1619 goto cleanup;
1622 for (j = 0; j < nItems; j++)
1623 runOrder[j] = pItems[j].a.s.uBidiLevel;
1625 ScriptLayout(nItems, runOrder, visOrder, NULL);
1627 for (j = 0; j < nItems; j++)
1629 int k;
1630 int cChars,cOutGlyphs;
1631 curItem = &pItems[visOrder[j]];
1633 cChars = pItems[visOrder[j]+1].iCharPos - curItem->iCharPos;
1635 res = ScriptShape(hDC, &psc, lpString + done + curItem->iCharPos, cChars, cMaxGlyphs, &curItem->a, run_glyphs, pwLogClust, psva, &cOutGlyphs);
1636 while (res == E_OUTOFMEMORY)
1638 WORD *new_run_glyphs = HeapReAlloc(GetProcessHeap(), 0, run_glyphs, sizeof(*run_glyphs) * cMaxGlyphs * 2);
1639 SCRIPT_VISATTR *new_psva = HeapReAlloc(GetProcessHeap(), 0, psva, sizeof(*psva) * cMaxGlyphs * 2);
1640 if (!new_run_glyphs || !new_psva)
1642 WARN("Out of memory\n");
1643 HeapFree(GetProcessHeap(), 0, runOrder);
1644 HeapFree(GetProcessHeap(), 0, visOrder);
1645 HeapFree(GetProcessHeap(), 0, *lpGlyphs);
1646 *lpGlyphs = NULL;
1647 if (new_run_glyphs)
1648 run_glyphs = new_run_glyphs;
1649 if (new_psva)
1650 psva = new_psva;
1651 goto cleanup;
1653 run_glyphs = new_run_glyphs;
1654 psva = new_psva;
1655 cMaxGlyphs *= 2;
1656 res = ScriptShape(hDC, &psc, lpString + done + curItem->iCharPos, cChars, cMaxGlyphs, &curItem->a, run_glyphs, pwLogClust, psva, &cOutGlyphs);
1658 if (res)
1660 if (res == USP_E_SCRIPT_NOT_IN_FONT)
1661 TRACE("Unable to shape with currently selected font\n");
1662 else
1663 FIXME("Unable to shape string (%lx)\n",res);
1664 j = nItems;
1665 doGlyphs = FALSE;
1666 HeapFree(GetProcessHeap(), 0, *lpGlyphs);
1667 *lpGlyphs = NULL;
1669 else
1671 WORD *new_glyphs;
1672 if (*lpGlyphs)
1673 new_glyphs = HeapReAlloc(GetProcessHeap(), 0, *lpGlyphs, sizeof(**lpGlyphs) * (glyph_i + cOutGlyphs));
1674 else
1675 new_glyphs = HeapAlloc(GetProcessHeap(), 0, sizeof(**lpGlyphs) * (glyph_i + cOutGlyphs));
1676 if (!new_glyphs)
1678 WARN("Out of memory\n");
1679 HeapFree(GetProcessHeap(), 0, runOrder);
1680 HeapFree(GetProcessHeap(), 0, visOrder);
1681 HeapFree(GetProcessHeap(), 0, *lpGlyphs);
1682 *lpGlyphs = NULL;
1683 goto cleanup;
1685 *lpGlyphs = new_glyphs;
1686 for (k = 0; k < cOutGlyphs; k++)
1687 (*lpGlyphs)[glyph_i+k] = run_glyphs[k];
1688 glyph_i += cOutGlyphs;
1691 HeapFree(GetProcessHeap(), 0, runOrder);
1692 HeapFree(GetProcessHeap(), 0, visOrder);
1695 done += i;
1697 if (cGlyphs)
1698 *cGlyphs = glyph_i;
1700 ret = TRUE;
1701 cleanup:
1702 HeapFree(GetProcessHeap(), 0, chartype);
1703 HeapFree(GetProcessHeap(), 0, levels);
1704 HeapFree(GetProcessHeap(), 0, pItems);
1705 HeapFree(GetProcessHeap(), 0, run_glyphs);
1706 HeapFree(GetProcessHeap(), 0, pwLogClust);
1707 HeapFree(GetProcessHeap(), 0, psva);
1708 ScriptFreeCache(&psc);
1709 return ret;
1712 static inline BOOL intersect_rect(RECT *dst, const RECT *src1, const RECT *src2)
1714 dst->left = max(src1->left, src2->left);
1715 dst->top = max(src1->top, src2->top);
1716 dst->right = min(src1->right, src2->right);
1717 dst->bottom = min(src1->bottom, src2->bottom);
1718 return !IsRectEmpty(dst);
1721 /***********************************************************************
1722 * get_line_width
1724 * Scale the underline / strikeout line width.
1726 static inline int get_line_width(HDC hdc, int metric_size)
1728 int width = abs(INTERNAL_YWSTODS(hdc, metric_size));
1729 if (width == 0) width = 1;
1730 if (metric_size < 0) width = -width;
1731 return width;
1734 static BOOL ext_text_out(struct pp_data *data, HANDLETABLE *htable,
1735 int handle_count, INT x, INT y, UINT flags, const RECT *rect,
1736 const WCHAR *str, UINT count, const INT *dx)
1738 HDC hdc = data->ctx->hdc;
1739 BOOL ret = FALSE;
1740 UINT align;
1741 DWORD layout;
1742 POINT pt;
1743 TEXTMETRICW tm;
1744 LOGFONTW lf;
1745 double cosEsc, sinEsc;
1746 INT char_extra;
1747 SIZE sz;
1748 RECT rc;
1749 POINT *deltas = NULL, width = {0, 0};
1750 INT breakRem;
1751 WORD *glyphs = NULL;
1752 XFORM xform;
1754 if (!(flags & (ETO_GLYPH_INDEX | ETO_IGNORELANGUAGE)) && count > 0)
1756 int glyphs_count = 0;
1757 UINT bidi_flags;
1759 bidi_flags = (GetTextAlign(hdc) & TA_RTLREADING) || (flags & ETO_RTLREADING)
1760 ? WINE_GCPW_FORCE_RTL : WINE_GCPW_FORCE_LTR;
1762 BIDI_Reorder(hdc, str, count, GCP_REORDER, bidi_flags,
1763 NULL, 0, NULL, &glyphs, &glyphs_count);
1765 flags |= ETO_IGNORELANGUAGE;
1766 if (glyphs)
1768 flags |= ETO_GLYPH_INDEX;
1769 count = glyphs_count;
1770 str = glyphs;
1774 align = GetTextAlign(hdc);
1775 breakRem = data->break_rem;
1776 layout = GetLayout(hdc);
1778 if (flags & ETO_RTLREADING) align |= TA_RTLREADING;
1779 if (layout & LAYOUT_RTL)
1781 if ((align & TA_CENTER) != TA_CENTER) align ^= TA_RIGHT;
1782 align ^= TA_RTLREADING;
1785 TRACE("%d, %d, %08x, %s, %s, %d, %p)\n", x, y, flags,
1786 wine_dbgstr_rect(rect), debugstr_wn(str, count), count, dx);
1787 TRACE("align = %x bkmode = %x mapmode = %x\n", align, GetBkMode(hdc),
1788 GetMapMode(hdc));
1790 if(align & TA_UPDATECP)
1792 GetCurrentPositionEx(hdc, &pt);
1793 x = pt.x;
1794 y = pt.y;
1797 GetTextMetricsW(hdc, &tm);
1798 GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf);
1800 if(!(tm.tmPitchAndFamily & TMPF_VECTOR)) /* Non-scalable fonts shouldn't be rotated */
1801 lf.lfEscapement = 0;
1803 GetWorldTransform(hdc, &xform);
1804 if (GetGraphicsMode(hdc) == GM_COMPATIBLE &&
1805 xform.eM11 * xform.eM22 < 0)
1807 lf.lfEscapement = -lf.lfEscapement;
1810 if(lf.lfEscapement != 0)
1812 cosEsc = cos(lf.lfEscapement * M_PI / 1800);
1813 sinEsc = sin(lf.lfEscapement * M_PI / 1800);
1815 else
1817 cosEsc = 1;
1818 sinEsc = 0;
1821 if (rect && (flags & (ETO_OPAQUE | ETO_CLIPPED)))
1823 rc = *rect;
1824 LPtoDP(hdc, (POINT*)&rc, 2);
1825 order_rect(&rc);
1826 if (flags & ETO_OPAQUE)
1827 PSDRV_ExtTextOut(data->ctx, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
1829 else flags &= ~ETO_CLIPPED;
1831 if(count == 0)
1833 ret = TRUE;
1834 goto done;
1837 pt.x = x;
1838 pt.y = y;
1839 LPtoDP(hdc, &pt, 1);
1840 x = pt.x;
1841 y = pt.y;
1843 char_extra = GetTextCharacterExtra(hdc);
1844 if (char_extra && dx)
1845 char_extra = 0; /* Printer drivers don't add char_extra if dx is supplied */
1847 if(char_extra || data->break_extra || breakRem || dx || lf.lfEscapement != 0)
1849 UINT i;
1850 POINT total = {0, 0}, desired[2];
1852 deltas = malloc(count * sizeof(*deltas));
1853 if (dx)
1855 if (flags & ETO_PDY)
1857 for (i = 0; i < count; i++)
1859 deltas[i].x = dx[i * 2] + char_extra;
1860 deltas[i].y = -dx[i * 2 + 1];
1863 else
1865 for (i = 0; i < count; i++)
1867 deltas[i].x = dx[i] + char_extra;
1868 deltas[i].y = 0;
1872 else
1874 INT *dx = malloc(count * sizeof(*dx));
1876 NtGdiGetTextExtentExW(hdc, str, count, -1, NULL, dx, &sz, !!(flags & ETO_GLYPH_INDEX));
1878 deltas[0].x = dx[0];
1879 deltas[0].y = 0;
1880 for (i = 1; i < count; i++)
1882 deltas[i].x = dx[i] - dx[i - 1];
1883 deltas[i].y = 0;
1885 free(dx);
1888 for(i = 0; i < count; i++)
1890 total.x += deltas[i].x;
1891 total.y += deltas[i].y;
1893 desired[0].x = desired[0].y = 0;
1895 desired[1].x = cosEsc * total.x + sinEsc * total.y;
1896 desired[1].y = -sinEsc * total.x + cosEsc * total.y;
1898 LPtoDP(hdc, desired, 2);
1899 desired[1].x -= desired[0].x;
1900 desired[1].y -= desired[0].y;
1902 if (GetGraphicsMode(hdc) == GM_COMPATIBLE)
1904 if (xform.eM11 < 0)
1905 desired[1].x = -desired[1].x;
1906 if (xform.eM22 < 0)
1907 desired[1].y = -desired[1].y;
1910 deltas[i].x = desired[1].x - width.x;
1911 deltas[i].y = desired[1].y - width.y;
1913 width = desired[1];
1915 flags |= ETO_PDY;
1917 else
1919 POINT desired[2];
1921 NtGdiGetTextExtentExW(hdc, str, count, 0, NULL, NULL, &sz, !!(flags & ETO_GLYPH_INDEX));
1922 desired[0].x = desired[0].y = 0;
1923 desired[1].x = sz.cx;
1924 desired[1].y = 0;
1925 LPtoDP(hdc, desired, 2);
1926 desired[1].x -= desired[0].x;
1927 desired[1].y -= desired[0].y;
1929 if (GetGraphicsMode(hdc) == GM_COMPATIBLE)
1931 if (xform.eM11 < 0)
1932 desired[1].x = -desired[1].x;
1933 if (xform.eM22 < 0)
1934 desired[1].y = -desired[1].y;
1936 width = desired[1];
1939 tm.tmAscent = abs(INTERNAL_YWSTODS(hdc, tm.tmAscent));
1940 tm.tmDescent = abs(INTERNAL_YWSTODS(hdc, tm.tmDescent));
1941 switch(align & (TA_LEFT | TA_RIGHT | TA_CENTER))
1943 case TA_LEFT:
1944 if (align & TA_UPDATECP)
1946 pt.x = x + width.x;
1947 pt.y = y + width.y;
1948 DPtoLP(hdc, &pt, 1);
1949 MoveToEx(hdc, pt.x, pt.y, NULL);
1951 break;
1953 case TA_CENTER:
1954 x -= width.x / 2;
1955 y -= width.y / 2;
1956 break;
1958 case TA_RIGHT:
1959 x -= width.x;
1960 y -= width.y;
1961 if (align & TA_UPDATECP)
1963 pt.x = x;
1964 pt.y = y;
1965 DPtoLP(hdc, &pt, 1);
1966 MoveToEx(hdc, pt.x, pt.y, NULL);
1968 break;
1971 switch(align & (TA_TOP | TA_BOTTOM | TA_BASELINE))
1973 case TA_TOP:
1974 y += tm.tmAscent * cosEsc;
1975 x += tm.tmAscent * sinEsc;
1976 break;
1978 case TA_BOTTOM:
1979 y -= tm.tmDescent * cosEsc;
1980 x -= tm.tmDescent * sinEsc;
1981 break;
1983 case TA_BASELINE:
1984 break;
1987 if (GetBkMode(hdc) != TRANSPARENT)
1989 if(!((flags & ETO_CLIPPED) && (flags & ETO_OPAQUE)))
1991 if(!(flags & ETO_OPAQUE) || !rect ||
1992 x < rc.left || x + width.x >= rc.right ||
1993 y - tm.tmAscent < rc.top || y + tm.tmDescent >= rc.bottom)
1995 RECT text_box;
1996 text_box.left = x;
1997 text_box.right = x + width.x;
1998 text_box.top = y - tm.tmAscent;
1999 text_box.bottom = y + tm.tmDescent;
2001 if (flags & ETO_CLIPPED) intersect_rect(&text_box, &text_box, &rc);
2002 if (!IsRectEmpty(&text_box))
2003 PSDRV_ExtTextOut(data->ctx, 0, 0, ETO_OPAQUE, &text_box, NULL, 0, NULL);
2008 ret = PSDRV_ExtTextOut(data->ctx, x, y, (flags & ~ETO_OPAQUE), &rc,
2009 str, count, (INT*)deltas);
2011 done:
2012 free(deltas);
2014 if (ret && (lf.lfUnderline || lf.lfStrikeOut))
2016 int underlinePos, strikeoutPos;
2017 int underlineWidth, strikeoutWidth;
2018 UINT size = NtGdiGetOutlineTextMetricsInternalW(hdc, 0, NULL, 0);
2019 OUTLINETEXTMETRICW* otm = NULL;
2020 POINT pts[5];
2021 HBRUSH hbrush = CreateSolidBrush(GetTextColor(hdc));
2022 HPEN hpen = GetStockObject(NULL_PEN);
2024 PSDRV_SelectPen(data->ctx, hpen, NULL);
2025 hpen = SelectObject(hdc, hpen);
2027 PSDRV_SelectBrush(data->ctx, hbrush, NULL);
2028 hbrush = SelectObject(hdc, hbrush);
2030 if(!size)
2032 underlinePos = 0;
2033 underlineWidth = tm.tmAscent / 20 + 1;
2034 strikeoutPos = tm.tmAscent / 2;
2035 strikeoutWidth = underlineWidth;
2037 else
2039 otm = malloc(size);
2040 NtGdiGetOutlineTextMetricsInternalW(hdc, size, otm, 0);
2041 underlinePos = abs(INTERNAL_YWSTODS(hdc, otm->otmsUnderscorePosition));
2042 if (otm->otmsUnderscorePosition < 0) underlinePos = -underlinePos;
2043 underlineWidth = get_line_width(hdc, otm->otmsUnderscoreSize);
2044 strikeoutPos = abs(INTERNAL_YWSTODS(hdc, otm->otmsStrikeoutPosition));
2045 if (otm->otmsStrikeoutPosition < 0) strikeoutPos = -strikeoutPos;
2046 strikeoutWidth = get_line_width(hdc, otm->otmsStrikeoutSize);
2047 free(otm);
2051 if (lf.lfUnderline)
2053 const INT cnt = 5;
2054 pts[0].x = x - (underlinePos + underlineWidth / 2) * sinEsc;
2055 pts[0].y = y - (underlinePos + underlineWidth / 2) * cosEsc;
2056 pts[1].x = x + width.x - (underlinePos + underlineWidth / 2) * sinEsc;
2057 pts[1].y = y + width.y - (underlinePos + underlineWidth / 2) * cosEsc;
2058 pts[2].x = pts[1].x + underlineWidth * sinEsc;
2059 pts[2].y = pts[1].y + underlineWidth * cosEsc;
2060 pts[3].x = pts[0].x + underlineWidth * sinEsc;
2061 pts[3].y = pts[0].y + underlineWidth * cosEsc;
2062 pts[4].x = pts[0].x;
2063 pts[4].y = pts[0].y;
2064 DPtoLP(hdc, pts, 5);
2065 PSDRV_PolyPolygon(data->ctx, pts, &cnt, 1);
2068 if (lf.lfStrikeOut)
2070 const INT cnt = 5;
2071 pts[0].x = x - (strikeoutPos + strikeoutWidth / 2) * sinEsc;
2072 pts[0].y = y - (strikeoutPos + strikeoutWidth / 2) * cosEsc;
2073 pts[1].x = x + width.x - (strikeoutPos + strikeoutWidth / 2) * sinEsc;
2074 pts[1].y = y + width.y - (strikeoutPos + strikeoutWidth / 2) * cosEsc;
2075 pts[2].x = pts[1].x + strikeoutWidth * sinEsc;
2076 pts[2].y = pts[1].y + strikeoutWidth * cosEsc;
2077 pts[3].x = pts[0].x + strikeoutWidth * sinEsc;
2078 pts[3].y = pts[0].y + strikeoutWidth * cosEsc;
2079 pts[4].x = pts[0].x;
2080 pts[4].y = pts[0].y;
2081 DPtoLP(hdc, pts, 5);
2082 PSDRV_PolyPolygon(data->ctx, pts, &cnt, 1);
2085 PSDRV_SelectPen(data->ctx, hpen, NULL);
2086 SelectObject(hdc, hpen);
2087 select_hbrush(data, htable, handle_count, hbrush);
2088 SelectObject(hdc, hbrush);
2089 DeleteObject(hbrush);
2092 HeapFree(GetProcessHeap(), 0, glyphs);
2093 return ret;
2096 static BOOL fill_rgn(struct pp_data *data, HANDLETABLE *htable, int handle_count, DWORD brush, HRGN rgn)
2098 struct ps_brush_pattern *pattern;
2099 HBRUSH hbrush;
2100 int ret;
2102 hbrush = get_object_handle(data, htable, brush, &pattern);
2103 PSDRV_SelectBrush(data->ctx, hbrush, pattern);
2104 ret = PSDRV_PaintRgn(data->ctx, rgn);
2105 select_hbrush(data, htable, handle_count, GetCurrentObject(data->ctx->hdc, OBJ_BRUSH));
2106 PSDRV_SelectBrush(data->ctx, hbrush, pattern);
2107 return ret;
2110 static BOOL is_path_record(int type)
2112 switch(type)
2114 case EMR_POLYBEZIER:
2115 case EMR_POLYGON:
2116 case EMR_POLYLINE:
2117 case EMR_POLYBEZIERTO:
2118 case EMR_POLYLINETO:
2119 case EMR_POLYPOLYLINE:
2120 case EMR_POLYPOLYGON:
2121 case EMR_MOVETOEX:
2122 case EMR_ANGLEARC:
2123 case EMR_ELLIPSE:
2124 case EMR_RECTANGLE:
2125 case EMR_ROUNDRECT:
2126 case EMR_ARC:
2127 case EMR_CHORD:
2128 case EMR_PIE:
2129 case EMR_LINETO:
2130 case EMR_ARCTO:
2131 case EMR_POLYDRAW:
2132 case EMR_EXTTEXTOUTA:
2133 case EMR_EXTTEXTOUTW:
2134 case EMR_POLYBEZIER16:
2135 case EMR_POLYGON16:
2136 case EMR_POLYLINE16:
2137 case EMR_POLYBEZIERTO16:
2138 case EMR_POLYLINETO16:
2139 case EMR_POLYPOLYLINE16:
2140 case EMR_POLYPOLYGON16:
2141 case EMR_POLYDRAW16:
2142 return TRUE;
2143 default:
2144 return FALSE;
2148 static int WINAPI hmf_proc(HDC hdc, HANDLETABLE *htable,
2149 const ENHMETARECORD *rec, int handle_count, LPARAM arg)
2151 struct pp_data *data = (struct pp_data *)arg;
2153 if (data->path && is_path_record(rec->iType))
2154 return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
2156 switch (rec->iType)
2158 case EMR_HEADER:
2160 const ENHMETAHEADER *header = (const ENHMETAHEADER *)rec;
2162 data->patterns = calloc(sizeof(*data->patterns), header->nHandles);
2163 return data->patterns && PSDRV_StartPage(data->ctx);
2165 case EMR_POLYBEZIER:
2167 const EMRPOLYBEZIER *p = (const EMRPOLYBEZIER *)rec;
2169 return PSDRV_PolyBezier(data->ctx, (const POINT *)p->aptl, p->cptl);
2171 case EMR_POLYGON:
2173 const EMRPOLYGON *p = (const EMRPOLYGON *)rec;
2175 return PSDRV_PolyPolygon(data->ctx, (const POINT *)p->aptl,
2176 (const INT *)&p->cptl, 1);
2178 case EMR_POLYLINE:
2180 const EMRPOLYLINE *p = (const EMRPOLYLINE *)rec;
2182 return PSDRV_PolyPolyline(data->ctx,
2183 (const POINT *)p->aptl, &p->cptl, 1);
2185 case EMR_POLYBEZIERTO:
2187 const EMRPOLYBEZIERTO *p = (const EMRPOLYBEZIERTO *)rec;
2189 return PSDRV_PolyBezierTo(data->ctx, (const POINT *)p->aptl, p->cptl) &&
2190 MoveToEx(data->ctx->hdc, p->aptl[p->cptl - 1].x, p->aptl[p->cptl - 1].y, NULL);
2192 case EMR_POLYLINETO:
2194 const EMRPOLYLINETO *p = (const EMRPOLYLINETO *)rec;
2195 POINT *pts;
2196 DWORD cnt;
2197 int ret;
2199 cnt = p->cptl + 1;
2200 pts = malloc(sizeof(*pts) * cnt);
2201 if (!pts) return 0;
2203 GetCurrentPositionEx(data->ctx->hdc, pts);
2204 memcpy(pts + 1, p->aptl, sizeof(*pts) * p->cptl);
2205 ret = PSDRV_PolyPolyline(data->ctx, pts, &cnt, 1) &&
2206 MoveToEx(data->ctx->hdc, pts[cnt - 1].x, pts[cnt - 1].y, NULL);
2207 free(pts);
2208 return ret;
2210 case EMR_POLYPOLYLINE:
2212 const EMRPOLYPOLYLINE *p = (const EMRPOLYPOLYLINE *)rec;
2214 return PSDRV_PolyPolyline(data->ctx,
2215 (const POINT *)(p->aPolyCounts + p->nPolys),
2216 p->aPolyCounts, p->nPolys);
2218 case EMR_POLYPOLYGON:
2220 const EMRPOLYPOLYGON *p = (const EMRPOLYPOLYGON *)rec;
2222 return PSDRV_PolyPolygon(data->ctx,
2223 (const POINT *)(p->aPolyCounts + p->nPolys),
2224 (const INT *)p->aPolyCounts, p->nPolys);
2226 case EMR_EOF:
2227 return PSDRV_EndPage(data->ctx);
2228 case EMR_SETPIXELV:
2230 const EMRSETPIXELV *p = (const EMRSETPIXELV *)rec;
2232 return PSDRV_SetPixel(data->ctx, p->ptlPixel.x,
2233 p->ptlPixel.y, p->crColor);
2235 case EMR_SETTEXTCOLOR:
2237 const EMRSETTEXTCOLOR *p = (const EMRSETTEXTCOLOR *)rec;
2239 SetTextColor(data->ctx->hdc, p->crColor);
2240 PSDRV_SetTextColor(data->ctx, p->crColor);
2241 return 1;
2243 case EMR_SETBKCOLOR:
2245 const EMRSETBKCOLOR *p = (const EMRSETBKCOLOR *)rec;
2247 SetBkColor(data->ctx->hdc, p->crColor);
2248 PSDRV_SetBkColor(data->ctx, p->crColor);
2249 return 1;
2251 case EMR_SAVEDC:
2253 int ret = PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
2255 if (!data->saved_dc_size)
2257 data->saved_dc = malloc(8 * sizeof(*data->saved_dc));
2258 if (data->saved_dc)
2259 data->saved_dc_size = 8;
2261 else if (data->saved_dc_size == data->saved_dc_top)
2263 void *alloc = realloc(data->saved_dc, data->saved_dc_size * 2);
2265 if (alloc)
2267 data->saved_dc = alloc;
2268 data->saved_dc_size *= 2;
2271 if (data->saved_dc_size == data->saved_dc_top)
2272 return 0;
2274 data->saved_dc[data->saved_dc_top].break_extra = data->break_extra;
2275 data->saved_dc[data->saved_dc_top].break_rem = data->break_rem;
2276 data->saved_dc_top++;
2277 return ret;
2279 case EMR_RESTOREDC:
2281 const EMRRESTOREDC *p = (const EMRRESTOREDC *)rec;
2282 HDC hdc = data->ctx->hdc;
2283 int ret = PlayEnhMetaFileRecord(hdc, htable, rec, handle_count);
2284 UINT aa_flags;
2286 if (ret)
2288 select_hbrush(data, htable, handle_count, GetCurrentObject(hdc, OBJ_BRUSH));
2289 PSDRV_SelectFont(data->ctx, GetCurrentObject(hdc, OBJ_FONT), &aa_flags);
2290 PSDRV_SelectPen(data->ctx, GetCurrentObject(hdc, OBJ_PEN), NULL);
2291 PSDRV_SetBkColor(data->ctx, GetBkColor(hdc));
2292 PSDRV_SetTextColor(data->ctx, GetTextColor(hdc));
2294 if (p->iRelative >= 0 || data->saved_dc_top + p->iRelative < 0)
2295 return 0;
2296 data->saved_dc_top += p->iRelative;
2297 data->break_extra = data->saved_dc[data->saved_dc_top].break_extra;
2298 data->break_rem = data->saved_dc[data->saved_dc_top].break_rem;
2300 return ret;
2302 case EMR_SELECTOBJECT:
2304 const EMRSELECTOBJECT *so = (const EMRSELECTOBJECT *)rec;
2305 struct ps_brush_pattern *pattern;
2306 UINT aa_flags;
2307 HGDIOBJ obj;
2309 obj = get_object_handle(data, htable, so->ihObject, &pattern);
2310 SelectObject(data->ctx->hdc, obj);
2312 switch (GetObjectType(obj))
2314 case OBJ_PEN: return PSDRV_SelectPen(data->ctx, obj, NULL) != NULL;
2315 case OBJ_BRUSH: return PSDRV_SelectBrush(data->ctx, obj, pattern) != NULL;
2316 case OBJ_FONT: return PSDRV_SelectFont(data->ctx, obj, &aa_flags) != NULL;
2317 case OBJ_EXTPEN: return PSDRV_SelectPen(data->ctx, obj, NULL) != NULL;
2318 default:
2319 FIXME("unhandled object type %ld\n", GetObjectType(obj));
2320 return 1;
2323 case EMR_DELETEOBJECT:
2325 const EMRDELETEOBJECT *p = (const EMRDELETEOBJECT *)rec;
2327 memset(&data->patterns[p->ihObject], 0, sizeof(*data->patterns));
2328 return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
2330 case EMR_ANGLEARC:
2332 const EMRANGLEARC *p = (const EMRANGLEARC *)rec;
2333 int arc_dir = SetArcDirection(data->ctx->hdc,
2334 p->eSweepAngle >= 0 ? AD_COUNTERCLOCKWISE : AD_CLOCKWISE);
2335 EMRARCTO arcto;
2336 int ret;
2338 arcto.emr.iType = EMR_ARCTO;
2339 arcto.rclBox.left = p->ptlCenter.x - p->nRadius;
2340 arcto.rclBox.top = p->ptlCenter.y - p->nRadius;
2341 arcto.rclBox.right = p->ptlCenter.x + p->nRadius;
2342 arcto.rclBox.bottom = p->ptlCenter.y + p->nRadius;
2343 arcto.ptlStart.x = GDI_ROUND(p->ptlCenter.x +
2344 cos(p->eStartAngle * M_PI / 180) * p->nRadius);
2345 arcto.ptlStart.y = GDI_ROUND(p->ptlCenter.y -
2346 sin(p->eStartAngle * M_PI / 180) * p->nRadius);
2347 arcto.ptlEnd.x = GDI_ROUND(p->ptlCenter.x +
2348 cos((p->eStartAngle + p->eSweepAngle) * M_PI / 180) * p->nRadius);
2349 arcto.ptlEnd.y = GDI_ROUND(p->ptlCenter.y -
2350 sin((p->eStartAngle + p->eSweepAngle) * M_PI / 180) * p->nRadius);
2352 ret = hmf_proc(hdc, htable, (ENHMETARECORD *)&arcto, handle_count, arg);
2353 SetArcDirection(data->ctx->hdc, arc_dir);
2354 return ret;
2356 case EMR_ELLIPSE:
2358 const EMRELLIPSE *p = (const EMRELLIPSE *)rec;
2359 const RECTL *r = &p->rclBox;
2361 return PSDRV_Ellipse(data->ctx, r->left, r->top, r->right, r->bottom);
2363 case EMR_RECTANGLE:
2365 const EMRRECTANGLE *rect = (const EMRRECTANGLE *)rec;
2367 return PSDRV_Rectangle(data->ctx, rect->rclBox.left,
2368 rect->rclBox.top, rect->rclBox.right, rect->rclBox.bottom);
2370 case EMR_ROUNDRECT:
2372 const EMRROUNDRECT *p = (const EMRROUNDRECT *)rec;
2374 return PSDRV_RoundRect(data->ctx, p->rclBox.left,
2375 p->rclBox.top, p->rclBox.right, p->rclBox.bottom,
2376 p->szlCorner.cx, p->szlCorner.cy);
2378 case EMR_ARC:
2380 const EMRARC *p = (const EMRARC *)rec;
2382 return PSDRV_Arc(data->ctx, p->rclBox.left, p->rclBox.top,
2383 p->rclBox.right, p->rclBox.bottom, p->ptlStart.x,
2384 p->ptlStart.y, p->ptlEnd.x, p->ptlEnd.y);
2386 case EMR_CHORD:
2388 const EMRCHORD *p = (const EMRCHORD *)rec;
2390 return PSDRV_Chord(data->ctx, p->rclBox.left, p->rclBox.top,
2391 p->rclBox.right, p->rclBox.bottom, p->ptlStart.x,
2392 p->ptlStart.y, p->ptlEnd.x, p->ptlEnd.y);
2394 case EMR_PIE:
2396 const EMRPIE *p = (const EMRPIE *)rec;
2398 return PSDRV_Pie(data->ctx, p->rclBox.left, p->rclBox.top,
2399 p->rclBox.right, p->rclBox.bottom, p->ptlStart.x,
2400 p->ptlStart.y, p->ptlEnd.x, p->ptlEnd.y);
2402 case EMR_LINETO:
2404 const EMRLINETO *line = (const EMRLINETO *)rec;
2406 return PSDRV_LineTo(data->ctx, line->ptl.x, line->ptl.y) &&
2407 MoveToEx(data->ctx->hdc, line->ptl.x, line->ptl.y, NULL);
2409 case EMR_ARCTO:
2411 const EMRARCTO *p = (const EMRARCTO *)rec;
2412 POINT pt;
2413 BOOL ret;
2415 ret = GetCurrentPositionEx(data->ctx->hdc, &pt);
2416 if (ret)
2418 ret = ArcTo(data->ctx->hdc, p->rclBox.left, p->rclBox.top,
2419 p->rclBox.right, p->rclBox.bottom, p->ptlStart.x,
2420 p->ptlStart.y, p->ptlStart.x, p->ptlStart.y);
2422 if (ret)
2423 ret = PSDRV_LineTo(data->ctx, pt.x, pt.y);
2424 if (ret)
2426 ret = PSDRV_Arc(data->ctx, p->rclBox.left, p->rclBox.top,
2427 p->rclBox.right, p->rclBox.bottom, p->ptlStart.x,
2428 p->ptlStart.y, p->ptlEnd.x, p->ptlEnd.y);
2430 if (ret)
2432 ret = ArcTo(data->ctx->hdc, p->rclBox.left, p->rclBox.top,
2433 p->rclBox.right, p->rclBox.bottom, p->ptlEnd.x,
2434 p->ptlEnd.y, p->ptlEnd.x, p->ptlEnd.y);
2436 return ret;
2438 case EMR_POLYDRAW:
2440 const EMRPOLYDRAW *p = (const EMRPOLYDRAW *)rec;
2441 const POINT *pts = (const POINT *)p->aptl;
2443 return poly_draw(data->ctx, pts, (BYTE *)(p->aptl + p->cptl), p->cptl) &&
2444 MoveToEx(data->ctx->hdc, pts[p->cptl - 1].x, pts[p->cptl - 1].y, NULL);
2446 case EMR_BEGINPATH:
2448 data->path = TRUE;
2449 return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
2451 case EMR_ENDPATH:
2453 data->path = FALSE;
2454 return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
2456 case EMR_FILLPATH:
2457 PSDRV_FillPath(data->ctx);
2458 return 1;
2459 case EMR_STROKEANDFILLPATH:
2460 PSDRV_StrokeAndFillPath(data->ctx);
2461 return 1;
2462 case EMR_STROKEPATH:
2463 PSDRV_StrokePath(data->ctx);
2464 return 1;
2465 case EMR_ABORTPATH:
2467 data->path = FALSE;
2468 return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
2470 case EMR_FILLRGN:
2472 const EMRFILLRGN *p = (const EMRFILLRGN *)rec;
2473 HRGN rgn;
2474 int ret;
2476 rgn = ExtCreateRegion(NULL, p->cbRgnData, (const RGNDATA *)p->RgnData);
2477 ret = fill_rgn(data, htable, handle_count, p->ihBrush, rgn);
2478 DeleteObject(rgn);
2479 return ret;
2481 case EMR_FRAMERGN:
2483 const EMRFRAMERGN *p = (const EMRFRAMERGN *)rec;
2484 HRGN rgn, frame;
2485 int ret;
2487 rgn = ExtCreateRegion(NULL, p->cbRgnData, (const RGNDATA *)p->RgnData);
2488 frame = CreateRectRgn(0, 0, 0, 0);
2490 CombineRgn(frame, rgn, 0, RGN_COPY);
2491 OffsetRgn(frame, -p->szlStroke.cx, 0);
2492 OffsetRgn(rgn, p->szlStroke.cx, 0);
2493 CombineRgn(frame, frame, rgn, RGN_AND);
2494 OffsetRgn(rgn, -p->szlStroke.cx, -p->szlStroke.cy);
2495 CombineRgn(frame, frame, rgn, RGN_AND);
2496 OffsetRgn(rgn, 0, 2*p->szlStroke.cy);
2497 CombineRgn(frame, frame, rgn, RGN_AND);
2498 OffsetRgn(rgn, 0, -p->szlStroke.cy);
2499 CombineRgn(frame, rgn, frame, RGN_DIFF);
2501 ret = fill_rgn(data, htable, handle_count, p->ihBrush, frame);
2502 DeleteObject(rgn);
2503 DeleteObject(frame);
2504 return ret;
2506 case EMR_INVERTRGN:
2508 const EMRINVERTRGN *p = (const EMRINVERTRGN *)rec;
2509 int old_rop, ret;
2510 HRGN rgn;
2512 rgn = ExtCreateRegion(NULL, p->cbRgnData, (const RGNDATA *)p->RgnData);
2513 old_rop = SetROP2(data->ctx->hdc, R2_NOT);
2514 ret = fill_rgn(data, htable, handle_count, 0x80000000 | BLACK_BRUSH, rgn);
2515 SetROP2(data->ctx->hdc, old_rop);
2516 DeleteObject(rgn);
2517 return ret;
2519 case EMR_PAINTRGN:
2521 const EMRPAINTRGN *p = (const EMRPAINTRGN *)rec;
2522 HRGN rgn = ExtCreateRegion(NULL, p->cbRgnData, (const RGNDATA *)p->RgnData);
2523 int ret;
2525 ret = PSDRV_PaintRgn(data->ctx, rgn);
2526 DeleteObject(rgn);
2527 return ret;
2529 case EMR_BITBLT:
2531 const EMRBITBLT *p = (const EMRBITBLT *)rec;
2532 const BITMAPINFO *bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiSrc);
2533 const BYTE *src_bits = (BYTE *)p + p->offBitsSrc;
2534 EMRSTRETCHBLT blt;
2537 blt.rclBounds = p->rclBounds;
2538 blt.xDest = p->xDest;
2539 blt.yDest = p->yDest;
2540 blt.cxDest = p->cxDest;
2541 blt.cyDest = p->cyDest;
2542 blt.dwRop = p->dwRop;
2543 blt.xSrc = p->xSrc;
2544 blt.ySrc = p->ySrc;
2545 blt.xformSrc = p->xformSrc;
2546 blt.crBkColorSrc = p->crBkColorSrc;
2547 blt.iUsageSrc = p->iUsageSrc;
2548 blt.offBmiSrc = p->offBmiSrc;
2549 blt.cbBmiSrc = p->cbBmiSrc;
2550 blt.offBitsSrc = p->offBitsSrc;
2551 blt.cbBitsSrc = p->cbBitsSrc;
2552 blt.cxSrc = p->cxDest;
2553 blt.cySrc = p->cyDest;
2555 return stretch_blt(data->ctx, &blt, bi, src_bits);
2557 case EMR_STRETCHBLT:
2559 const EMRSTRETCHBLT *p = (const EMRSTRETCHBLT *)rec;
2560 const BITMAPINFO *bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiSrc);
2561 const BYTE *src_bits = (BYTE *)p + p->offBitsSrc;
2563 return stretch_blt(data->ctx, p, bi, src_bits);
2565 case EMR_MASKBLT:
2567 const EMRMASKBLT *p = (const EMRMASKBLT *)rec;
2568 const BITMAPINFO *mask_bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiMask);
2569 const BITMAPINFO *src_bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiSrc);
2570 const BYTE *mask_bits = (BYTE *)p + p->offBitsMask;
2571 const BYTE *src_bits = (BYTE *)p + p->offBitsSrc;
2573 return mask_blt(data->ctx, p, src_bi, src_bits, mask_bi, mask_bits);
2575 case EMR_PLGBLT:
2577 const EMRPLGBLT *p = (const EMRPLGBLT *)rec;
2579 return plg_blt(data->ctx, p);
2581 case EMR_SETDIBITSTODEVICE:
2582 return set_di_bits_to_device(data->ctx, (const EMRSETDIBITSTODEVICE *)rec);
2583 case EMR_STRETCHDIBITS:
2584 return stretch_di_bits(data->ctx, (const EMRSTRETCHDIBITS *)rec);
2585 case EMR_EXTTEXTOUTW:
2587 const EMREXTTEXTOUTW *p = (const EMREXTTEXTOUTW *)rec;
2588 HDC hdc = data->ctx->hdc;
2589 const INT *dx = NULL;
2590 int old_mode, ret;
2591 RECT rect;
2593 rect.left = p->emrtext.rcl.left;
2594 rect.top = p->emrtext.rcl.top;
2595 rect.right = p->emrtext.rcl.right;
2596 rect.bottom = p->emrtext.rcl.bottom;
2598 old_mode = SetGraphicsMode(hdc, p->iGraphicsMode);
2599 /* Reselect the font back into the dc so that the transformation
2600 gets updated. */
2601 SelectObject(hdc, GetCurrentObject(hdc, OBJ_FONT));
2603 if (p->emrtext.offDx)
2604 dx = (const INT *)((const BYTE *)rec + p->emrtext.offDx);
2606 ret = ext_text_out(data, htable, handle_count, p->emrtext.ptlReference.x,
2607 p->emrtext.ptlReference.y, p->emrtext.fOptions, &rect,
2608 (LPCWSTR)((const BYTE *)rec + p->emrtext.offString), p->emrtext.nChars, dx);
2610 SetGraphicsMode(hdc, old_mode);
2611 return ret;
2613 case EMR_POLYBEZIER16:
2615 const EMRPOLYBEZIER16 *p = (const EMRPOLYBEZIER16 *)rec;
2616 POINT *pts;
2617 int i;
2619 pts = malloc(sizeof(*pts) * p->cpts);
2620 if (!pts) return 0;
2621 for (i = 0; i < p->cpts; i++)
2623 pts[i].x = p->apts[i].x;
2624 pts[i].y = p->apts[i].y;
2626 i = PSDRV_PolyBezier(data->ctx, pts, p->cpts);
2627 free(pts);
2628 return i;
2630 case EMR_POLYGON16:
2632 const EMRPOLYGON16 *p = (const EMRPOLYGON16 *)rec;
2633 POINT *pts;
2634 int i;
2636 pts = malloc(sizeof(*pts) * p->cpts);
2637 if (!pts) return 0;
2638 for (i = 0; i < p->cpts; i++)
2640 pts[i].x = p->apts[i].x;
2641 pts[i].y = p->apts[i].y;
2643 i = PSDRV_PolyPolygon(data->ctx, pts, (const INT *)&p->cpts, 1);
2644 free(pts);
2645 return i;
2647 case EMR_POLYLINE16:
2649 const EMRPOLYLINE16 *p = (const EMRPOLYLINE16 *)rec;
2650 POINT *pts;
2651 int i;
2653 pts = malloc(sizeof(*pts) * p->cpts);
2654 if (!pts) return 0;
2655 for (i = 0; i < p->cpts; i++)
2657 pts[i].x = p->apts[i].x;
2658 pts[i].y = p->apts[i].y;
2660 i = PSDRV_PolyPolyline(data->ctx, pts, &p->cpts, 1);
2661 free(pts);
2662 return i;
2664 case EMR_POLYBEZIERTO16:
2666 const EMRPOLYBEZIERTO16 *p = (const EMRPOLYBEZIERTO16 *)rec;
2667 POINT *pts;
2668 int i;
2670 pts = malloc(sizeof(*pts) * p->cpts);
2671 if (!pts) return 0;
2672 for (i = 0; i < p->cpts; i++)
2674 pts[i].x = p->apts[i].x;
2675 pts[i].y = p->apts[i].y;
2677 i = PSDRV_PolyBezierTo(data->ctx, pts, p->cpts) &&
2678 MoveToEx(data->ctx->hdc, pts[p->cpts - 1].x, pts[p->cpts - 1].y, NULL);
2679 free(pts);
2680 return i;
2682 case EMR_POLYLINETO16:
2684 const EMRPOLYLINETO16 *p = (const EMRPOLYLINETO16 *)rec;
2685 POINT *pts;
2686 DWORD cnt;
2687 int i;
2689 cnt = p->cpts + 1;
2690 pts = malloc(sizeof(*pts) * cnt);
2691 if (!pts) return 0;
2692 GetCurrentPositionEx(data->ctx->hdc, pts);
2693 for (i = 0; i < p->cpts; i++)
2695 pts[i + 1].x = p->apts[i].x;
2696 pts[i + 1].y = p->apts[i].y;
2698 i = PSDRV_PolyPolyline(data->ctx, pts, &cnt, 1) &&
2699 MoveToEx(data->ctx->hdc, pts[cnt - 1].x, pts[cnt - 1].y, NULL);
2700 free(pts);
2701 return i;
2703 case EMR_POLYPOLYLINE16:
2705 const EMRPOLYPOLYLINE16 *p = (const EMRPOLYPOLYLINE16 *)rec;
2706 POINT *pts;
2707 int i;
2709 pts = malloc(sizeof(*pts) * p->cpts);
2710 if (!pts) return 0;
2711 for (i = 0; i < p->cpts; i++)
2713 pts[i].x = ((const POINTS *)(p->aPolyCounts + p->nPolys))[i].x;
2714 pts[i].y = ((const POINTS *)(p->aPolyCounts + p->nPolys))[i].y;
2716 i = PSDRV_PolyPolyline(data->ctx, pts, p->aPolyCounts, p->nPolys);
2717 free(pts);
2718 return i;
2720 case EMR_POLYPOLYGON16:
2722 const EMRPOLYPOLYGON16 *p = (const EMRPOLYPOLYGON16 *)rec;
2723 POINT *pts;
2724 int i;
2726 pts = malloc(sizeof(*pts) * p->cpts);
2727 if (!pts) return 0;
2728 for (i = 0; i < p->cpts; i++)
2730 pts[i].x = ((const POINTS *)(p->aPolyCounts + p->nPolys))[i].x;
2731 pts[i].y = ((const POINTS *)(p->aPolyCounts + p->nPolys))[i].y;
2733 i = PSDRV_PolyPolygon(data->ctx, pts, (const INT *)p->aPolyCounts, p->nPolys);
2734 free(pts);
2735 return i;
2737 case EMR_POLYDRAW16:
2739 const EMRPOLYDRAW16 *p = (const EMRPOLYDRAW16 *)rec;
2740 POINT *pts;
2741 int i;
2743 pts = malloc(sizeof(*pts) * p->cpts);
2744 if (!pts) return 0;
2745 for (i = 0; i < p->cpts; i++)
2747 pts[i].x = ((const POINTS *)p->apts)[i].x;
2748 pts[i].y = ((const POINTS *)p->apts)[i].y;
2750 i = poly_draw(data->ctx, pts, (BYTE *)(p->apts + p->cpts), p->cpts) &&
2751 MoveToEx(data->ctx->hdc, pts[p->cpts - 1].x, pts[p->cpts - 1].y, NULL);
2752 free(pts);
2753 return i;
2755 case EMR_CREATEMONOBRUSH:
2757 const EMRCREATEMONOBRUSH *p = (const EMRCREATEMONOBRUSH *)rec;
2759 if (!PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count))
2760 return 0;
2761 data->patterns[p->ihBrush].usage = p->iUsage;
2762 data->patterns[p->ihBrush].info = (BITMAPINFO *)((BYTE *)p + p->offBmi);
2763 data->patterns[p->ihBrush].bits.ptr = (BYTE *)p + p->offBits;
2764 return 1;
2766 case EMR_CREATEDIBPATTERNBRUSHPT:
2768 const EMRCREATEDIBPATTERNBRUSHPT *p = (const EMRCREATEDIBPATTERNBRUSHPT *)rec;
2770 if (!PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count))
2771 return 0;
2772 data->patterns[p->ihBrush].usage = p->iUsage;
2773 data->patterns[p->ihBrush].info = (BITMAPINFO *)((BYTE *)p + p->offBmi);
2774 data->patterns[p->ihBrush].bits.ptr = (BYTE *)p + p->offBits;
2775 return 1;
2777 case EMR_EXTESCAPE:
2779 const EMREXTESCAPE *p = (const EMREXTESCAPE *)rec;
2781 PSDRV_ExtEscape(data->ctx, p->iEscape, p->cbEscData, p->EscData, 0, NULL);
2782 return 1;
2784 case EMR_GRADIENTFILL:
2786 const EMRGRADIENTFILL *p = (const EMRGRADIENTFILL *)rec;
2788 return gradient_fill(data->ctx, p->Ver, p->nVer,
2789 p->Ver + p->nVer, p->nTri, p->ulMode);
2791 case EMR_SETTEXTJUSTIFICATION:
2793 const EMRSETTEXTJUSTIFICATION *p = (const EMRSETTEXTJUSTIFICATION *)rec;
2795 if (p->break_count)
2797 data->break_extra = p->break_extra / p->break_count;
2798 data->break_rem = p->break_extra - data->break_extra * p->break_count;
2800 else
2802 data->break_extra = 0;
2803 data->break_rem = 0;
2805 return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
2808 case EMR_EXTFLOODFILL:
2809 case EMR_ALPHABLEND:
2810 break;
2812 case EMR_SETWINDOWEXTEX:
2813 case EMR_SETWINDOWORGEX:
2814 case EMR_SETVIEWPORTEXTEX:
2815 case EMR_SETVIEWPORTORGEX:
2816 case EMR_SETBRUSHORGEX:
2817 case EMR_SETMAPPERFLAGS:
2818 case EMR_SETMAPMODE:
2819 case EMR_SETBKMODE:
2820 case EMR_SETPOLYFILLMODE:
2821 case EMR_SETROP2:
2822 case EMR_SETSTRETCHBLTMODE:
2823 case EMR_SETTEXTALIGN:
2824 case EMR_OFFSETCLIPRGN:
2825 case EMR_MOVETOEX:
2826 case EMR_EXCLUDECLIPRECT:
2827 case EMR_INTERSECTCLIPRECT:
2828 case EMR_SCALEVIEWPORTEXTEX:
2829 case EMR_SCALEWINDOWEXTEX:
2830 case EMR_SETWORLDTRANSFORM:
2831 case EMR_MODIFYWORLDTRANSFORM:
2832 case EMR_CREATEPEN:
2833 case EMR_CREATEBRUSHINDIRECT:
2834 case EMR_SELECTPALETTE:
2835 case EMR_CREATEPALETTE:
2836 case EMR_SETPALETTEENTRIES:
2837 case EMR_RESIZEPALETTE:
2838 case EMR_REALIZEPALETTE:
2839 case EMR_SETARCDIRECTION:
2840 case EMR_CLOSEFIGURE:
2841 case EMR_FLATTENPATH:
2842 case EMR_WIDENPATH:
2843 case EMR_SELECTCLIPPATH:
2844 case EMR_EXTSELECTCLIPRGN:
2845 case EMR_EXTCREATEFONTINDIRECTW:
2846 case EMR_EXTCREATEPEN:
2847 case EMR_SETLAYOUT:
2848 return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
2849 default:
2850 FIXME("unsupported record: %ld\n", rec->iType);
2853 return 1;
2856 static BOOL print_metafile(struct pp_data *data, HANDLE hdata)
2858 XFORM xform = { .eM11 = 1, .eM22 = 1 };
2859 record_hdr header;
2860 HENHMETAFILE hmf;
2861 BYTE *buf;
2862 BOOL ret;
2863 DWORD r;
2865 if (!ReadPrinter(hdata, &header, sizeof(header), &r))
2866 return FALSE;
2867 if (r != sizeof(header))
2869 SetLastError(ERROR_INVALID_DATA);
2870 return FALSE;
2873 buf = malloc(header.cjSize);
2874 if (!buf)
2875 return FALSE;
2877 if (!ReadPrinter(hdata, buf, header.cjSize, &r))
2879 free(buf);
2880 return FALSE;
2882 if (r != header.cjSize)
2884 free(buf);
2885 SetLastError(ERROR_INVALID_DATA);
2886 return FALSE;
2889 hmf = SetEnhMetaFileBits(header.cjSize, buf);
2890 free(buf);
2891 if (!hmf)
2892 return FALSE;
2894 AbortPath(data->ctx->hdc);
2895 MoveToEx(data->ctx->hdc, 0, 0, NULL);
2896 SetBkColor(data->ctx->hdc, RGB(255, 255, 255));
2897 SetBkMode(data->ctx->hdc, OPAQUE);
2898 SetMapMode(data->ctx->hdc, MM_TEXT);
2899 SetPolyFillMode(data->ctx->hdc, ALTERNATE);
2900 SetROP2(data->ctx->hdc, R2_COPYPEN);
2901 SetStretchBltMode(data->ctx->hdc, BLACKONWHITE);
2902 SetTextAlign(data->ctx->hdc, TA_LEFT | TA_TOP);
2903 SetTextColor(data->ctx->hdc, 0);
2904 SetTextJustification(data->ctx->hdc, 0, 0);
2905 SetWorldTransform(data->ctx->hdc, &xform);
2906 PSDRV_SetTextColor(data->ctx, 0);
2907 PSDRV_SetBkColor(data->ctx, RGB(255, 255, 255));
2909 ret = EnumEnhMetaFile(NULL, hmf, hmf_proc, (void *)data, NULL);
2910 DeleteEnhMetaFile(hmf);
2911 free(data->patterns);
2912 data->patterns = NULL;
2913 data->path = FALSE;
2914 data->break_extra = 0;
2915 data->break_rem = 0;
2916 data->saved_dc_top = 0;
2917 return ret;
2920 BOOL WINAPI EnumPrintProcessorDatatypesW(WCHAR *server, WCHAR *name, DWORD level,
2921 BYTE *datatypes, DWORD size, DWORD *needed, DWORD *no)
2923 DATATYPES_INFO_1W *info = (DATATYPES_INFO_1W *)datatypes;
2925 TRACE("%s, %s, %ld, %p, %ld, %p, %p\n", debugstr_w(server), debugstr_w(name),
2926 level, datatypes, size, needed, no);
2928 if (!needed || !no)
2930 SetLastError(ERROR_INVALID_PARAMETER);
2931 return FALSE;
2934 *no = 0;
2935 *needed = sizeof(*info) + sizeof(emf_1003);
2937 if (level != 1 || (size && !datatypes))
2939 SetLastError(ERROR_INVALID_PARAMETER);
2940 return FALSE;
2943 if (size < *needed)
2945 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2946 return FALSE;
2949 *no = 1;
2950 info->pName = (WCHAR*)(info + 1);
2951 memcpy(info + 1, emf_1003, sizeof(emf_1003));
2952 return TRUE;
2955 HANDLE WINAPI OpenPrintProcessor(WCHAR *port, PRINTPROCESSOROPENDATA *open_data)
2957 struct pp_data *data;
2958 HANDLE hport;
2959 HDC hdc;
2961 TRACE("%s, %p\n", debugstr_w(port), open_data);
2963 if (!port || !open_data || !open_data->pDatatype)
2965 SetLastError(ERROR_INVALID_PARAMETER);
2966 return NULL;
2968 if (wcscmp(open_data->pDatatype, emf_1003))
2970 SetLastError(ERROR_INVALID_DATATYPE);
2971 return NULL;
2974 if (!OpenPrinterW(port, &hport, NULL))
2975 return NULL;
2977 data = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(*data));
2978 if (!data)
2979 return NULL;
2980 data->magic = PP_MAGIC;
2981 data->hport = hport;
2982 data->port = wcsdup(port);
2983 data->doc_name = wcsdup(open_data->pDocumentName);
2984 data->out_file = wcsdup(open_data->pOutputFile);
2986 hdc = CreateDCW(L"winspool", open_data->pPrinterName, NULL, open_data->pDevMode);
2987 if (!hdc)
2989 LocalFree(data);
2990 return NULL;
2992 SetGraphicsMode(hdc, GM_ADVANCED);
2993 data->ctx = create_print_ctx(hdc, open_data->pPrinterName, open_data->pDevMode);
2994 if (!data->ctx)
2996 DeleteDC(hdc);
2997 LocalFree(data);
2998 return NULL;
3000 return (HANDLE)data;
3003 BOOL WINAPI PrintDocumentOnPrintProcessor(HANDLE pp, WCHAR *doc_name)
3005 struct pp_data *data = get_handle_data(pp);
3006 DEVMODEW *devmode = NULL;
3007 emfspool_header header;
3008 LARGE_INTEGER pos, cur;
3009 record_hdr record;
3010 HANDLE spool_data;
3011 DOC_INFO_1W info;
3012 BOOL ret;
3013 DWORD r;
3015 TRACE("%p, %s\n", pp, debugstr_w(doc_name));
3017 if (!data)
3018 return FALSE;
3020 spool_data = GdiGetSpoolFileHandle(data->port,
3021 &data->ctx->Devmode->dmPublic, doc_name);
3022 GdiGetDevmodeForPage(spool_data, 1, &devmode, NULL);
3023 if (devmode && devmode->dmFields & DM_COPIES)
3025 data->ctx->Devmode->dmPublic.dmFields |= DM_COPIES;
3026 data->ctx->Devmode->dmPublic.dmCopies = devmode->dmCopies;
3028 if (devmode && devmode->dmFields & DM_COLLATE)
3030 data->ctx->Devmode->dmPublic.dmFields |= DM_COLLATE;
3031 data->ctx->Devmode->dmPublic.dmCollate = devmode->dmCollate;
3033 GdiDeleteSpoolFileHandle(spool_data);
3035 if (!OpenPrinterW(doc_name, &spool_data, NULL))
3036 return FALSE;
3038 info.pDocName = data->doc_name;
3039 info.pOutputFile = data->out_file;
3040 info.pDatatype = (WCHAR *)L"RAW";
3041 data->ctx->job.id = StartDocPrinterW(data->hport, 1, (BYTE *)&info);
3042 if (!data->ctx->job.id)
3044 ClosePrinter(spool_data);
3045 return FALSE;
3048 if (!(ret = ReadPrinter(spool_data, &header, sizeof(header), &r)))
3049 goto cleanup;
3050 if (r != sizeof(header))
3052 SetLastError(ERROR_INVALID_DATA);
3053 ret = FALSE;
3054 goto cleanup;
3057 if (header.dwVersion != EMFSPOOL_VERSION)
3059 FIXME("unrecognized spool file format\n");
3060 SetLastError(ERROR_INTERNAL_ERROR);
3061 goto cleanup;
3063 pos.QuadPart = header.cjSize;
3064 if (!(ret = SeekPrinter(spool_data, pos, NULL, FILE_BEGIN, FALSE)))
3065 goto cleanup;
3067 data->ctx->job.hprinter = data->hport;
3068 if (!PSDRV_WriteHeader(data->ctx, data->doc_name))
3070 WARN("Failed to write header\n");
3071 goto cleanup;
3073 data->ctx->job.OutOfPage = TRUE;
3074 data->ctx->job.PageNo = 0;
3075 data->ctx->job.quiet = FALSE;
3076 data->ctx->job.passthrough_state = passthrough_none;
3077 data->ctx->job.doc_name = strdupW(data->doc_name);
3079 while (1)
3081 if (!(ret = ReadPrinter(spool_data, &record, sizeof(record), &r)))
3082 goto cleanup;
3083 if (!r)
3084 break;
3085 if (r != sizeof(record))
3087 SetLastError(ERROR_INVALID_DATA);
3088 ret = FALSE;
3089 goto cleanup;
3092 switch (record.ulID)
3094 case EMRI_DEVMODE:
3096 DEVMODEW *devmode = NULL;
3098 if (record.cjSize)
3100 devmode = malloc(record.cjSize);
3101 if (!devmode)
3102 goto cleanup;
3103 ret = ReadPrinter(spool_data, devmode, record.cjSize, &r);
3104 if (ret && r != record.cjSize)
3106 SetLastError(ERROR_INVALID_DATA);
3107 ret = FALSE;
3111 if (ret)
3112 ret = PSDRV_ResetDC(data->ctx, devmode);
3113 if (ret && devmode && (devmode->dmFields & DM_PAPERSIZE))
3114 ret = PSDRV_WritePageSize(data->ctx);
3115 free(devmode);
3116 if (!ret)
3117 goto cleanup;
3118 break;
3120 case EMRI_METAFILE_DATA:
3121 pos.QuadPart = record.cjSize;
3122 ret = SeekPrinter(spool_data, pos, NULL, FILE_CURRENT, FALSE);
3123 if (!ret)
3124 goto cleanup;
3125 break;
3126 case EMRI_METAFILE_EXT:
3127 case EMRI_BW_METAFILE_EXT:
3128 pos.QuadPart = 0;
3129 ret = SeekPrinter(spool_data, pos, &cur, FILE_CURRENT, FALSE);
3130 if (ret)
3132 cur.QuadPart += record.cjSize;
3133 ret = ReadPrinter(spool_data, &pos, sizeof(pos), &r);
3134 if (ret && r != sizeof(pos))
3136 SetLastError(ERROR_INVALID_DATA);
3137 ret = FALSE;
3140 pos.QuadPart = -pos.QuadPart - 2 * sizeof(record);
3141 if (ret)
3142 ret = SeekPrinter(spool_data, pos, NULL, FILE_CURRENT, FALSE);
3143 if (ret)
3144 ret = print_metafile(data, spool_data);
3145 if (ret)
3146 ret = SeekPrinter(spool_data, cur, NULL, FILE_BEGIN, FALSE);
3147 if (!ret)
3148 goto cleanup;
3149 break;
3150 default:
3151 FIXME("%s not supported, skipping\n", debugstr_rec_type(record.ulID));
3152 pos.QuadPart = record.cjSize;
3153 ret = SeekPrinter(spool_data, pos, NULL, FILE_CURRENT, FALSE);
3154 if (!ret)
3155 goto cleanup;
3156 break;
3160 cleanup:
3161 if (data->ctx->job.PageNo)
3162 PSDRV_WriteFooter(data->ctx);
3163 flush_spool(data->ctx);
3165 HeapFree(GetProcessHeap(), 0, data->ctx->job.doc_name);
3166 ClosePrinter(spool_data);
3167 return EndDocPrinter(data->hport) && ret;
3170 BOOL WINAPI ControlPrintProcessor(HANDLE pp, DWORD cmd)
3172 FIXME("%p, %ld\n", pp, cmd);
3173 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3174 return FALSE;
3177 BOOL WINAPI ClosePrintProcessor(HANDLE pp)
3179 struct pp_data *data = get_handle_data(pp);
3181 TRACE("%p\n", pp);
3183 if (!data)
3184 return FALSE;
3186 ClosePrinter(data->hport);
3187 free(data->port);
3188 free(data->doc_name);
3189 free(data->out_file);
3190 DeleteDC(data->ctx->hdc);
3191 HeapFree(GetProcessHeap(), 0, data->ctx->Devmode);
3192 HeapFree(GetProcessHeap(), 0, data->ctx);
3193 free(data->saved_dc);
3195 memset(data, 0, sizeof(*data));
3196 LocalFree(data);
3197 return TRUE;
3200 HRESULT WINAPI DllRegisterServer(void)
3202 AddPrintProcessorW(NULL, (WCHAR *)L"Windows 4.0", (WCHAR *)L"wineps.drv", (WCHAR *)L"wineps");
3203 AddPrintProcessorW(NULL, NULL, (WCHAR *)L"wineps.drv", (WCHAR *)L"wineps");
3204 return S_OK;