wineps: Don't include gdi_driver.h from PE module.
[wine.git] / dlls / wineps.drv / printproc.c
blob34b3ec897a4e0276dd2bd8f4db832ac559f310be
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 <winspool.h>
27 #include <ddk/winsplp.h>
28 #include <usp10.h>
30 #include "psdrv.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
36 #define EMFSPOOL_VERSION 0x10000
37 #define PP_MAGIC 0x952173fe
39 struct pp_data
41 DWORD magic;
42 HANDLE hport;
43 WCHAR *doc_name;
44 WCHAR *out_file;
46 print_ctx *ctx;
48 struct ps_brush_pattern *patterns;
49 BOOL path;
50 INT break_extra;
51 INT break_rem;
53 INT saved_dc_size;
54 INT saved_dc_top;
55 struct
57 INT break_extra;
58 INT break_rem;
59 } *saved_dc;
62 typedef enum
64 EMRI_METAFILE = 1,
65 EMRI_ENGINE_FONT,
66 EMRI_DEVMODE,
67 EMRI_TYPE1_FONT,
68 EMRI_PRESTARTPAGE,
69 EMRI_DESIGNVECTOR,
70 EMRI_SUBSET_FONT,
71 EMRI_DELTA_FONT,
72 EMRI_FORM_METAFILE,
73 EMRI_BW_METAFILE,
74 EMRI_BW_FORM_METAFILE,
75 EMRI_METAFILE_DATA,
76 EMRI_METAFILE_EXT,
77 EMRI_BW_METAFILE_EXT,
78 EMRI_ENGINE_FONT_EXT,
79 EMRI_TYPE1_FONT_EXT,
80 EMRI_DESIGNVECTOR_EXT,
81 EMRI_SUBSET_FONT_EXT,
82 EMRI_DELTA_FONT_EXT,
83 EMRI_PS_JOB_DATA,
84 EMRI_EMBED_FONT_EXT,
85 } record_type;
87 typedef struct
89 unsigned int dwVersion;
90 unsigned int cjSize;
91 unsigned int dpszDocName;
92 unsigned int dpszOutput;
93 } emfspool_header;
95 typedef struct
97 unsigned int ulID;
98 unsigned int cjSize;
99 } record_hdr;
101 typedef struct
103 EMR emr;
104 INT break_extra;
105 INT break_count;
106 } EMRSETTEXTJUSTIFICATION;
108 BOOL WINAPI SeekPrinter(HANDLE, LARGE_INTEGER, LARGE_INTEGER*, DWORD, BOOL);
110 static const WCHAR emf_1003[] = L"NT EMF 1.003";
112 #define EMRICASE(x) case x: return #x
113 static const char * debugstr_rec_type(int id)
115 switch (id)
117 EMRICASE(EMRI_METAFILE);
118 EMRICASE(EMRI_ENGINE_FONT);
119 EMRICASE(EMRI_DEVMODE);
120 EMRICASE(EMRI_TYPE1_FONT);
121 EMRICASE(EMRI_PRESTARTPAGE);
122 EMRICASE(EMRI_DESIGNVECTOR);
123 EMRICASE(EMRI_SUBSET_FONT);
124 EMRICASE(EMRI_DELTA_FONT);
125 EMRICASE(EMRI_FORM_METAFILE);
126 EMRICASE(EMRI_BW_METAFILE);
127 EMRICASE(EMRI_BW_FORM_METAFILE);
128 EMRICASE(EMRI_METAFILE_DATA);
129 EMRICASE(EMRI_METAFILE_EXT);
130 EMRICASE(EMRI_BW_METAFILE_EXT);
131 EMRICASE(EMRI_ENGINE_FONT_EXT);
132 EMRICASE(EMRI_TYPE1_FONT_EXT);
133 EMRICASE(EMRI_DESIGNVECTOR_EXT);
134 EMRICASE(EMRI_SUBSET_FONT_EXT);
135 EMRICASE(EMRI_DELTA_FONT_EXT);
136 EMRICASE(EMRI_PS_JOB_DATA);
137 EMRICASE(EMRI_EMBED_FONT_EXT);
138 default:
139 FIXME("unknown record type: %d\n", id);
140 return NULL;
143 #undef EMRICASE
145 static struct pp_data* get_handle_data(HANDLE pp)
147 struct pp_data *ret = (struct pp_data *)pp;
149 if (!ret || ret->magic != PP_MAGIC)
151 SetLastError(ERROR_INVALID_HANDLE);
152 return NULL;
154 return ret;
157 static inline INT GDI_ROUND(double val)
159 return (int)floor(val + 0.5);
162 static void translate(RECT *rect, const XFORM *xform)
164 double x, y;
166 x = rect->left;
167 y = rect->top;
168 rect->left = GDI_ROUND(x * xform->eM11 + y * xform->eM21 + xform->eDx);
169 rect->top = GDI_ROUND(x * xform->eM12 + y * xform->eM22 + xform->eDy);
171 x = rect->right;
172 y = rect->bottom;
173 rect->right = GDI_ROUND(x * xform->eM11 + y * xform->eM21 + xform->eDx);
174 rect->bottom = GDI_ROUND(x * xform->eM12 + y * xform->eM22 + xform->eDy);
177 static inline void get_bounding_rect(RECT *rect, int x, int y, int width, int height)
179 rect->left = x;
180 rect->right = x + width;
181 rect->top = y;
182 rect->bottom = y + height;
183 if (rect->left > rect->right)
185 int tmp = rect->left;
186 rect->left = rect->right + 1;
187 rect->right = tmp + 1;
189 if (rect->top > rect->bottom)
191 int tmp = rect->top;
192 rect->top = rect->bottom + 1;
193 rect->bottom = tmp + 1;
197 static inline void order_rect(RECT *rect)
199 if (rect->left > rect->right)
201 int tmp = rect->left;
202 rect->left = rect->right;
203 rect->right = tmp;
205 if (rect->top > rect->bottom)
207 int tmp = rect->top;
208 rect->top = rect->bottom;
209 rect->bottom = tmp;
213 static BOOL intersect_vis_rectangles(struct ps_bitblt_coords *dst, struct ps_bitblt_coords *src)
215 RECT rect;
217 /* intersect the rectangles */
219 if ((src->width == dst->width) && (src->height == dst->height)) /* no stretching */
221 OffsetRect(&src->visrect, dst->x - src->x, dst->y - src->y);
222 if (!IntersectRect(&rect, &src->visrect, &dst->visrect)) return FALSE;
223 src->visrect = dst->visrect = rect;
224 OffsetRect(&src->visrect, src->x - dst->x, src->y - dst->y);
226 else /* stretching */
228 /* map source rectangle into destination coordinates */
229 rect = src->visrect;
230 OffsetRect(&rect,
231 -src->x - (src->width < 0 ? 1 : 0),
232 -src->y - (src->height < 0 ? 1 : 0));
233 rect.left = rect.left * dst->width / src->width;
234 rect.top = rect.top * dst->height / src->height;
235 rect.right = rect.right * dst->width / src->width;
236 rect.bottom = rect.bottom * dst->height / src->height;
237 order_rect(&rect);
239 /* when the source rectangle needs to flip and it doesn't fit in the source device
240 area, the destination area isn't flipped. So, adjust destination coordinates */
241 if (src->width < 0 && dst->width > 0 &&
242 (src->x + src->width + 1 < src->visrect.left || src->x > src->visrect.right))
243 dst->x += (dst->width - rect.right) - rect.left;
244 else if (src->width > 0 && dst->width < 0 &&
245 (src->x < src->visrect.left || src->x + src->width > src->visrect.right))
246 dst->x -= rect.right - (dst->width - rect.left);
248 if (src->height < 0 && dst->height > 0 &&
249 (src->y + src->height + 1 < src->visrect.top || src->y > src->visrect.bottom))
250 dst->y += (dst->height - rect.bottom) - rect.top;
251 else if (src->height > 0 && dst->height < 0 &&
252 (src->y < src->visrect.top || src->y + src->height > src->visrect.bottom))
253 dst->y -= rect.bottom - (dst->height - rect.top);
255 OffsetRect(&rect, dst->x, dst->y);
257 /* avoid rounding errors */
258 rect.left--;
259 rect.top--;
260 rect.right++;
261 rect.bottom++;
262 if (!IntersectRect(&dst->visrect, &rect, &dst->visrect)) return FALSE;
264 /* map destination rectangle back to source coordinates */
265 rect = dst->visrect;
266 OffsetRect(&rect,
267 -dst->x - (dst->width < 0 ? 1 : 0),
268 -dst->y - (dst->height < 0 ? 1 : 0));
269 rect.left = src->x + rect.left * src->width / dst->width;
270 rect.top = src->y + rect.top * src->height / dst->height;
271 rect.right = src->x + rect.right * src->width / dst->width;
272 rect.bottom = src->y + rect.bottom * src->height / dst->height;
273 order_rect(&rect);
275 /* avoid rounding errors */
276 rect.left--;
277 rect.top--;
278 rect.right++;
279 rect.bottom++;
280 if (!IntersectRect(&src->visrect, &rect, &src->visrect)) return FALSE;
282 return TRUE;
285 static void clip_visrect(HDC hdc, RECT *dst, const RECT *src)
287 HRGN hrgn;
289 hrgn = CreateRectRgn(0, 0, 0, 0);
290 if (GetRandomRgn(hdc, hrgn, 3) == 1)
292 GetRgnBox(hrgn, dst);
293 IntersectRect(dst, dst, src);
295 else
297 *dst = *src;
299 DeleteObject(hrgn);
302 static void get_vis_rectangles(HDC hdc, struct ps_bitblt_coords *dst,
303 const XFORM *xform, DWORD width, DWORD height, struct ps_bitblt_coords *src)
305 RECT rect;
307 rect.left = dst->log_x;
308 rect.top = dst->log_y;
309 rect.right = dst->log_x + dst->log_width;
310 rect.bottom = dst->log_y + dst->log_height;
311 LPtoDP(hdc, (POINT *)&rect, 2);
312 dst->x = rect.left;
313 dst->y = rect.top;
314 dst->width = rect.right - rect.left;
315 dst->height = rect.bottom - rect.top;
316 if (dst->layout & LAYOUT_RTL && dst->layout & LAYOUT_BITMAPORIENTATIONPRESERVED)
318 dst->x += dst->width;
319 dst->width = -dst->width;
321 get_bounding_rect(&rect, dst->x, dst->y, dst->width, dst->height);
322 clip_visrect(hdc, &dst->visrect, &rect);
324 if (!src) return;
326 rect.left = src->log_x;
327 rect.top = src->log_y;
328 rect.right = src->log_x + src->log_width;
329 rect.bottom = src->log_y + src->log_height;
330 translate(&rect, xform);
331 src->x = rect.left;
332 src->y = rect.top;
333 src->width = rect.right - rect.left;
334 src->height = rect.bottom - rect.top;
335 get_bounding_rect(&rect, src->x, src->y, src->width, src->height);
336 if (rect.left < 0) rect.left = 0;
337 if (rect.top < 0) rect.top = 0;
338 if (rect.right > width) rect.right = width;
339 if (rect.bottom > height) rect.bottom = height;
340 src->visrect = rect;
342 intersect_vis_rectangles(dst, src);
345 static int stretch_blt(print_ctx *ctx, const EMRSTRETCHBLT *blt,
346 const BITMAPINFO *bi, const BYTE *src_bits)
348 char dst_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
349 BITMAPINFO *dst_info = (BITMAPINFO *)dst_buffer;
350 struct ps_bitblt_coords src, dst;
351 struct ps_image_bits bits;
352 DWORD err;
354 dst.log_x = blt->xDest;
355 dst.log_y = blt->yDest;
356 dst.log_width = blt->cxDest;
357 dst.log_height = blt->cyDest;
358 dst.layout = GetLayout(ctx->hdc);
359 if (blt->dwRop & NOMIRRORBITMAP)
360 dst.layout |= LAYOUT_BITMAPORIENTATIONPRESERVED;
362 if (!blt->cbBmiSrc)
364 get_vis_rectangles(ctx->hdc, &dst, NULL, 0, 0, NULL);
365 return PSDRV_PatBlt(ctx, &dst, blt->dwRop);
368 src.log_x = blt->xSrc;
369 src.log_y = blt->ySrc;
370 src.log_width = blt->cxSrc;
371 src.log_height = blt->cySrc;
372 src.layout = 0;
374 get_vis_rectangles(ctx->hdc, &dst, &blt->xformSrc,
375 bi->bmiHeader.biWidth, abs(bi->bmiHeader.biHeight), &src);
377 memcpy(dst_info, bi, blt->cbBmiSrc);
378 memset(&bits, 0, sizeof(bits));
379 bits.ptr = (BYTE *)src_bits;
380 err = PSDRV_PutImage(ctx, 0, dst_info, &bits, &src, &dst, blt->dwRop);
381 if (err == ERROR_BAD_FORMAT)
383 HDC hdc = CreateCompatibleDC(NULL);
384 HBITMAP bitmap;
386 bits.is_copy = TRUE;
387 bitmap = CreateDIBSection(hdc, dst_info, DIB_RGB_COLORS, &bits.ptr, NULL, 0);
388 SetDIBits(hdc, bitmap, 0, bi->bmiHeader.biHeight, src_bits, bi, blt->iUsageSrc);
390 err = PSDRV_PutImage(ctx, 0, dst_info, &bits, &src, &dst, blt->dwRop);
391 DeleteObject(bitmap);
392 DeleteObject(hdc);
395 if (err != ERROR_SUCCESS)
396 FIXME("PutImage returned %ld\n", err);
397 return !err;
400 #define FRGND_ROP3(ROP4) ((ROP4) & 0x00FFFFFF)
401 #define BKGND_ROP3(ROP4) (ROP3Table[((ROP4)>>24) & 0xFF])
403 static int mask_blt(print_ctx *ctx, const EMRMASKBLT *p, const BITMAPINFO *src_bi,
404 const BYTE *src_bits, const BITMAPINFO *mask_bi, const BYTE *mask_bits)
406 HBITMAP bmp1, old_bmp1, bmp2, old_bmp2, bmp_src, old_bmp_src;
407 char bmp2_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
408 BITMAPINFO *bmp2_info = (BITMAPINFO *)bmp2_buffer;
409 HBRUSH brush_mask, brush_dest, old_brush;
410 HDC hdc_src, hdc1, hdc2;
411 BYTE *bits;
412 EMRSTRETCHBLT blt;
414 static const DWORD ROP3Table[256] =
416 0x00000042, 0x00010289,
417 0x00020C89, 0x000300AA,
418 0x00040C88, 0x000500A9,
419 0x00060865, 0x000702C5,
420 0x00080F08, 0x00090245,
421 0x000A0329, 0x000B0B2A,
422 0x000C0324, 0x000D0B25,
423 0x000E08A5, 0x000F0001,
424 0x00100C85, 0x001100A6,
425 0x00120868, 0x001302C8,
426 0x00140869, 0x001502C9,
427 0x00165CCA, 0x00171D54,
428 0x00180D59, 0x00191CC8,
429 0x001A06C5, 0x001B0768,
430 0x001C06CA, 0x001D0766,
431 0x001E01A5, 0x001F0385,
432 0x00200F09, 0x00210248,
433 0x00220326, 0x00230B24,
434 0x00240D55, 0x00251CC5,
435 0x002606C8, 0x00271868,
436 0x00280369, 0x002916CA,
437 0x002A0CC9, 0x002B1D58,
438 0x002C0784, 0x002D060A,
439 0x002E064A, 0x002F0E2A,
440 0x0030032A, 0x00310B28,
441 0x00320688, 0x00330008,
442 0x003406C4, 0x00351864,
443 0x003601A8, 0x00370388,
444 0x0038078A, 0x00390604,
445 0x003A0644, 0x003B0E24,
446 0x003C004A, 0x003D18A4,
447 0x003E1B24, 0x003F00EA,
448 0x00400F0A, 0x00410249,
449 0x00420D5D, 0x00431CC4,
450 0x00440328, 0x00450B29,
451 0x004606C6, 0x0047076A,
452 0x00480368, 0x004916C5,
453 0x004A0789, 0x004B0605,
454 0x004C0CC8, 0x004D1954,
455 0x004E0645, 0x004F0E25,
456 0x00500325, 0x00510B26,
457 0x005206C9, 0x00530764,
458 0x005408A9, 0x00550009,
459 0x005601A9, 0x00570389,
460 0x00580785, 0x00590609,
461 0x005A0049, 0x005B18A9,
462 0x005C0649, 0x005D0E29,
463 0x005E1B29, 0x005F00E9,
464 0x00600365, 0x006116C6,
465 0x00620786, 0x00630608,
466 0x00640788, 0x00650606,
467 0x00660046, 0x006718A8,
468 0x006858A6, 0x00690145,
469 0x006A01E9, 0x006B178A,
470 0x006C01E8, 0x006D1785,
471 0x006E1E28, 0x006F0C65,
472 0x00700CC5, 0x00711D5C,
473 0x00720648, 0x00730E28,
474 0x00740646, 0x00750E26,
475 0x00761B28, 0x007700E6,
476 0x007801E5, 0x00791786,
477 0x007A1E29, 0x007B0C68,
478 0x007C1E24, 0x007D0C69,
479 0x007E0955, 0x007F03C9,
480 0x008003E9, 0x00810975,
481 0x00820C49, 0x00831E04,
482 0x00840C48, 0x00851E05,
483 0x008617A6, 0x008701C5,
484 0x008800C6, 0x00891B08,
485 0x008A0E06, 0x008B0666,
486 0x008C0E08, 0x008D0668,
487 0x008E1D7C, 0x008F0CE5,
488 0x00900C45, 0x00911E08,
489 0x009217A9, 0x009301C4,
490 0x009417AA, 0x009501C9,
491 0x00960169, 0x0097588A,
492 0x00981888, 0x00990066,
493 0x009A0709, 0x009B07A8,
494 0x009C0704, 0x009D07A6,
495 0x009E16E6, 0x009F0345,
496 0x00A000C9, 0x00A11B05,
497 0x00A20E09, 0x00A30669,
498 0x00A41885, 0x00A50065,
499 0x00A60706, 0x00A707A5,
500 0x00A803A9, 0x00A90189,
501 0x00AA0029, 0x00AB0889,
502 0x00AC0744, 0x00AD06E9,
503 0x00AE0B06, 0x00AF0229,
504 0x00B00E05, 0x00B10665,
505 0x00B21974, 0x00B30CE8,
506 0x00B4070A, 0x00B507A9,
507 0x00B616E9, 0x00B70348,
508 0x00B8074A, 0x00B906E6,
509 0x00BA0B09, 0x00BB0226,
510 0x00BC1CE4, 0x00BD0D7D,
511 0x00BE0269, 0x00BF08C9,
512 0x00C000CA, 0x00C11B04,
513 0x00C21884, 0x00C3006A,
514 0x00C40E04, 0x00C50664,
515 0x00C60708, 0x00C707AA,
516 0x00C803A8, 0x00C90184,
517 0x00CA0749, 0x00CB06E4,
518 0x00CC0020, 0x00CD0888,
519 0x00CE0B08, 0x00CF0224,
520 0x00D00E0A, 0x00D1066A,
521 0x00D20705, 0x00D307A4,
522 0x00D41D78, 0x00D50CE9,
523 0x00D616EA, 0x00D70349,
524 0x00D80745, 0x00D906E8,
525 0x00DA1CE9, 0x00DB0D75,
526 0x00DC0B04, 0x00DD0228,
527 0x00DE0268, 0x00DF08C8,
528 0x00E003A5, 0x00E10185,
529 0x00E20746, 0x00E306EA,
530 0x00E40748, 0x00E506E5,
531 0x00E61CE8, 0x00E70D79,
532 0x00E81D74, 0x00E95CE6,
533 0x00EA02E9, 0x00EB0849,
534 0x00EC02E8, 0x00ED0848,
535 0x00EE0086, 0x00EF0A08,
536 0x00F00021, 0x00F10885,
537 0x00F20B05, 0x00F3022A,
538 0x00F40B0A, 0x00F50225,
539 0x00F60265, 0x00F708C5,
540 0x00F802E5, 0x00F90845,
541 0x00FA0089, 0x00FB0A09,
542 0x00FC008A, 0x00FD0A0A,
543 0x00FE02A9, 0x00FF0062,
546 if (!p->cbBmiMask)
548 blt.rclBounds = p->rclBounds;
549 blt.xDest = p->xDest;
550 blt.yDest = p->yDest;
551 blt.cxDest = p->cxDest;
552 blt.cyDest = p->cyDest;
553 blt.dwRop = FRGND_ROP3(p->dwRop);
554 blt.xSrc = p->xSrc;
555 blt.ySrc = p->ySrc;
556 blt.xformSrc = p->xformSrc;
557 blt.crBkColorSrc = p->crBkColorSrc;
558 blt.iUsageSrc = p->iUsageSrc;
559 blt.offBmiSrc = 0;
560 blt.cbBmiSrc = p->cbBmiSrc;
561 blt.offBitsSrc = 0;
562 blt.cbBitsSrc = p->cbBitsSrc;
563 blt.cxSrc = p->cxDest;
564 blt.cySrc = p->cyDest;
566 return stretch_blt(ctx, &blt, src_bi, src_bits);
569 hdc_src = CreateCompatibleDC(NULL);
570 SetGraphicsMode(hdc_src, GM_ADVANCED);
571 SetWorldTransform(hdc_src, &p->xformSrc);
572 brush_dest = CreateSolidBrush(p->crBkColorSrc);
573 old_brush = SelectObject(hdc_src, brush_dest);
574 PatBlt(hdc_src, p->rclBounds.left, p->rclBounds.top,
575 p->rclBounds.right - p->rclBounds.left,
576 p->rclBounds.bottom - p->rclBounds.top, PATCOPY);
577 SelectObject(hdc_src, old_brush);
578 DeleteObject(brush_dest);
580 bmp_src = CreateDIBSection(hdc_src, src_bi, p->iUsageSrc, (void **)&bits, NULL, 0);
581 memcpy(bits, src_bits, p->cbBitsSrc);
582 old_bmp_src = SelectObject(hdc_src, bmp_src);
584 bmp1 = CreateBitmap(mask_bi->bmiHeader.biWidth, mask_bi->bmiHeader.biHeight, 1, 1, NULL);
585 SetDIBits(ctx->hdc, bmp1, 0, mask_bi->bmiHeader.biHeight, mask_bits, mask_bi, p->iUsageMask);
586 brush_mask = CreatePatternBrush(bmp1);
587 DeleteObject(bmp1);
588 brush_dest = SelectObject(ctx->hdc, GetStockObject(NULL_BRUSH));
590 /* make bitmap */
591 hdc1 = CreateCompatibleDC(NULL);
592 bmp1 = CreateBitmap(p->cxDest, p->cyDest, 1, 32, NULL);
593 old_bmp1 = SelectObject(hdc1, bmp1);
595 /* draw using bkgnd rop */
596 old_brush = SelectObject(hdc1, brush_dest);
597 BitBlt(hdc1, 0, 0, p->cxDest, p->cyDest, hdc_src, p->xSrc, p->ySrc, BKGND_ROP3(p->dwRop));
598 SelectObject(hdc1, old_brush);
600 /* make bitmap */
601 hdc2 = CreateCompatibleDC(NULL);
602 bmp2_info->bmiHeader.biSize = sizeof(bmp2_info->bmiHeader);
603 bmp2_info->bmiHeader.biWidth = p->cxDest;
604 bmp2_info->bmiHeader.biHeight = p->cyDest;
605 bmp2_info->bmiHeader.biPlanes = 1;
606 bmp2_info->bmiHeader.biBitCount = 32;
607 bmp2_info->bmiHeader.biCompression = BI_RGB;
608 bmp2_info->bmiHeader.biSizeImage = p->cxDest * p->cyDest * bmp2_info->bmiHeader.biBitCount / 8;
609 bmp2 = CreateDIBSection(hdc2, bmp2_info, DIB_RGB_COLORS, (void **)&bits, NULL, 0);
610 old_bmp2 = SelectObject(hdc2, bmp2);
612 /* draw using foregnd rop */
613 old_brush = SelectObject(hdc2, brush_dest);
614 BitBlt(hdc2, 0, 0, p->cxDest, p->cyDest, hdc_src, p->xSrc, p->ySrc, FRGND_ROP3(p->dwRop));
616 /* combine both using the mask as a pattern brush */
617 SelectObject(hdc2, brush_mask);
618 SetBrushOrgEx(hdc2, -p->xMask, -p->yMask, NULL);
619 /* (D & P) | (S & ~P) */
620 BitBlt(hdc2, 0, 0, p->cxDest, p->cyDest, hdc1, 0, 0, 0xac0744);
621 SelectObject(hdc2, old_brush);
623 /* blit to dst */
624 blt.rclBounds = p->rclBounds;
625 blt.xDest = p->xDest;
626 blt.yDest = p->yDest;
627 blt.cxDest = p->cxDest;
628 blt.cyDest = p->cyDest;
629 blt.dwRop = SRCCOPY;
630 blt.xSrc = 0;
631 blt.ySrc = 0;
632 GetTransform(hdc2, 0x204, &blt.xformSrc);
633 blt.crBkColorSrc = p->crBkColorSrc;
634 blt.iUsageSrc = DIB_RGB_COLORS;
635 blt.offBmiSrc = 0;
636 blt.cbBmiSrc = bmp2_info->bmiHeader.biSize;
637 blt.offBitsSrc = 0;
638 blt.cbBitsSrc = bmp2_info->bmiHeader.biSizeImage;
639 blt.cxSrc = p->cxDest;
640 blt.cySrc = p->cyDest;
642 stretch_blt(ctx, &blt, bmp2_info, bits);
644 /* restore all objects */
645 SelectObject(ctx->hdc, brush_dest);
646 SelectObject(hdc1, old_bmp1);
647 SelectObject(hdc2, old_bmp2);
648 SelectObject(hdc_src, old_bmp_src);
650 /* delete all temp objects */
651 DeleteObject(bmp1);
652 DeleteObject(bmp2);
653 DeleteObject(bmp_src);
654 DeleteObject(brush_mask);
656 DeleteObject(hdc1);
657 DeleteObject(hdc2);
658 DeleteObject(hdc_src);
660 return TRUE;
663 static void combine_transform(XFORM *result, const XFORM *xform1, const XFORM *xform2)
665 XFORM r;
667 /* Create the result in a temporary XFORM, since result may be
668 * equal to xform1 or xform2 */
669 r.eM11 = xform1->eM11 * xform2->eM11 + xform1->eM12 * xform2->eM21;
670 r.eM12 = xform1->eM11 * xform2->eM12 + xform1->eM12 * xform2->eM22;
671 r.eM21 = xform1->eM21 * xform2->eM11 + xform1->eM22 * xform2->eM21;
672 r.eM22 = xform1->eM21 * xform2->eM12 + xform1->eM22 * xform2->eM22;
673 r.eDx = xform1->eDx * xform2->eM11 + xform1->eDy * xform2->eM21 + xform2->eDx;
674 r.eDy = xform1->eDx * xform2->eM12 + xform1->eDy * xform2->eM22 + xform2->eDy;
676 *result = r;
679 static int plg_blt(print_ctx *ctx, const EMRPLGBLT *p)
681 const BITMAPINFO *src_bi, *mask_bi;
682 const BYTE *src_bits, *mask_bits;
683 XFORM xf, xform_dest;
684 EMRMASKBLT maskblt;
685 /* rect coords */
686 POINT rect[3];
687 /* parallelogram coords */
688 POINT plg[3];
689 double det;
691 memcpy(plg, p->aptlDest, sizeof(plg));
692 rect[0].x = p->xSrc;
693 rect[0].y = p->ySrc;
694 rect[1].x = p->xSrc + p->cxSrc;
695 rect[1].y = p->ySrc;
696 rect[2].x = p->xSrc;
697 rect[2].y = p->ySrc + p->cySrc;
698 /* calc XFORM matrix to transform hdcDest -> hdcSrc (parallelogram to rectangle) */
699 /* determinant */
700 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);
702 if (fabs(det) < 1e-5)
703 return TRUE;
705 TRACE("%ld,%ld,%ldx%ld -> %ld,%ld,%ld,%ld,%ld,%ld\n", p->xSrc, p->ySrc, p->cxSrc, p->cySrc,
706 plg[0].x, plg[0].y, plg[1].x, plg[1].y, plg[2].x, plg[2].y);
708 /* X components */
709 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;
710 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;
711 xf.eDx = (rect[0].x*(rect[1].y*plg[2].x - rect[2].y*plg[1].x) -
712 rect[1].x*(rect[0].y*plg[2].x - rect[2].y*plg[0].x) +
713 rect[2].x*(rect[0].y*plg[1].x - rect[1].y*plg[0].x)
714 ) / det;
716 /* Y components */
717 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;
718 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;
719 xf.eDy = (rect[0].x*(rect[1].y*plg[2].y - rect[2].y*plg[1].y) -
720 rect[1].x*(rect[0].y*plg[2].y - rect[2].y*plg[0].y) +
721 rect[2].x*(rect[0].y*plg[1].y - rect[1].y*plg[0].y)
722 ) / det;
724 combine_transform(&xf, &xf, &p->xformSrc);
726 GetTransform(ctx->hdc, 0x203, &xform_dest);
727 SetWorldTransform(ctx->hdc, &xf);
728 /* now destination and source DCs use same coords */
729 maskblt.rclBounds = p->rclBounds;
730 maskblt.xDest = p->xSrc;
731 maskblt.yDest = p->ySrc;
732 maskblt.cxDest = p->cxSrc;
733 maskblt.cyDest = p->cySrc;
734 maskblt.dwRop = SRCCOPY;
735 maskblt.xSrc = p->xSrc;
736 maskblt.ySrc = p->ySrc;
737 maskblt.xformSrc = p->xformSrc;
738 maskblt.crBkColorSrc = p->crBkColorSrc;
739 maskblt.iUsageSrc = p->iUsageSrc;
740 maskblt.offBmiSrc = 0;
741 maskblt.cbBmiSrc = p->cbBmiSrc;
742 maskblt.offBitsSrc = 0;
743 maskblt.cbBitsSrc = p->cbBitsSrc;
744 maskblt.xMask = p->xMask;
745 maskblt.yMask = p->yMask;
746 maskblt.iUsageMask = p->iUsageMask;
747 maskblt.offBmiMask = 0;
748 maskblt.cbBmiMask = p->cbBmiMask;
749 maskblt.offBitsMask = 0;
750 maskblt.cbBitsMask = p->cbBitsMask;
751 src_bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiSrc);
752 src_bits = (BYTE *)p + p->offBitsSrc;
753 mask_bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiMask);
754 mask_bits = (BYTE *)p + p->offBitsMask;
755 mask_blt(ctx, &maskblt, src_bi, src_bits, mask_bi, mask_bits);
756 SetWorldTransform(ctx->hdc, &xform_dest);
757 return TRUE;
760 static inline int get_dib_stride( int width, int bpp )
762 return ((width * bpp + 31) >> 3) & ~3;
765 static inline int get_dib_image_size( const BITMAPINFO *info )
767 return get_dib_stride( info->bmiHeader.biWidth, info->bmiHeader.biBitCount )
768 * abs( info->bmiHeader.biHeight );
771 static int set_di_bits_to_device(print_ctx *ctx, const EMRSETDIBITSTODEVICE *p)
773 const BITMAPINFO *info = (const BITMAPINFO *)((BYTE *)p + p->offBmiSrc);
774 char bi_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
775 BITMAPINFO *bi = (BITMAPINFO *)bi_buffer;
776 HBITMAP bitmap, old_bitmap;
777 int width, height, ret;
778 BYTE *bits;
780 width = min(p->cxSrc, info->bmiHeader.biWidth);
781 height = min(p->cySrc, abs(info->bmiHeader.biHeight));
783 memset(bi_buffer, 0, sizeof(bi_buffer));
784 bi->bmiHeader.biSize = sizeof(bi->bmiHeader);
785 bi->bmiHeader.biWidth = width;
786 bi->bmiHeader.biHeight = height;
787 bi->bmiHeader.biPlanes = 1;
788 if (p->iUsageSrc == DIB_PAL_COLORS && (info->bmiHeader.biBitCount == 1 ||
789 info->bmiHeader.biBitCount == 4 || info->bmiHeader.biBitCount == 8))
791 PALETTEENTRY pal[256];
792 HPALETTE hpal;
793 UINT i, size;
795 bi->bmiHeader.biBitCount = info->bmiHeader.biBitCount;
796 bi->bmiHeader.biClrUsed = 1 << info->bmiHeader.biBitCount;
797 bi->bmiHeader.biClrImportant = bi->bmiHeader.biClrUsed;
799 hpal = GetCurrentObject(ctx->hdc, OBJ_PAL);
800 size = GetPaletteEntries(hpal, 0, bi->bmiHeader.biClrUsed, pal);
801 for (i = 0; i < size; i++)
803 bi->bmiColors[i].rgbBlue = pal[i].peBlue;
804 bi->bmiColors[i].rgbGreen = pal[i].peGreen;
805 bi->bmiColors[i].rgbRed = pal[i].peRed;
808 else
810 bi->bmiHeader.biBitCount = 24;
812 bi->bmiHeader.biCompression = BI_RGB;
813 bitmap = CreateDIBSection(ctx->hdc, bi, DIB_RGB_COLORS, (void **)&bits, NULL, 0);
814 if (!bitmap)
815 return 1;
816 old_bitmap = SelectObject(ctx->hdc, bitmap);
818 ret = SetDIBitsToDevice(ctx->hdc, 0, 0, width, height, p->xSrc, p->ySrc,
819 p->iStartScan, p->cScans, (const BYTE*)p + p->offBitsSrc, info, p->iUsageSrc);
820 SelectObject(ctx->hdc, old_bitmap);
821 if (ret)
823 EMRSTRETCHBLT blt;
825 memset(&blt, 0, sizeof(blt));
826 blt.rclBounds = p->rclBounds;
827 blt.xDest = p->xDest;
828 blt.yDest = p->yDest + p->cySrc - height;
829 blt.cxDest = width;
830 blt.cyDest = ret;
831 blt.dwRop = SRCCOPY;
832 blt.xformSrc.eM11 = 1;
833 blt.xformSrc.eM22 = 1;
834 blt.iUsageSrc = DIB_RGB_COLORS;
835 blt.cbBmiSrc = sizeof(bi_buffer);
836 blt.cbBitsSrc = get_dib_image_size(bi);
837 blt.cxSrc = blt.cxDest;
838 blt.cySrc = blt.cyDest;
839 stretch_blt(ctx, &blt, bi, bits);
842 DeleteObject(bitmap);
843 return 1;
846 static int stretch_di_bits(print_ctx *ctx, const EMRSTRETCHDIBITS *p)
848 char bi_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
849 const BYTE *bits = (BYTE *)p + p->offBitsSrc;
850 BITMAPINFO *bi = (BITMAPINFO *)bi_buffer;
851 EMRSTRETCHBLT blt;
853 memcpy(bi, (BYTE *)p + p->offBmiSrc, p->cbBmiSrc);
854 memset(bi_buffer + p->cbBmiSrc, 0, sizeof(bi_buffer) - p->cbBmiSrc);
856 if (p->iUsageSrc == DIB_PAL_COLORS && (bi->bmiHeader.biBitCount == 1 ||
857 bi->bmiHeader.biBitCount == 4 || bi->bmiHeader.biBitCount == 8))
859 PALETTEENTRY pal[256];
860 HPALETTE hpal;
861 UINT i, size;
863 hpal = GetCurrentObject(ctx->hdc, OBJ_PAL);
864 size = GetPaletteEntries(hpal, 0, 1 << bi->bmiHeader.biBitCount, pal);
865 for (i = 0; i < size; i++)
867 bi->bmiColors[i].rgbBlue = pal[i].peBlue;
868 bi->bmiColors[i].rgbGreen = pal[i].peGreen;
869 bi->bmiColors[i].rgbRed = pal[i].peRed;
873 memset(&blt, 0, sizeof(blt));
874 blt.rclBounds = p->rclBounds;
875 blt.xDest = p->xDest;
876 blt.yDest = p->yDest;
877 blt.cxDest = p->cxDest;
878 blt.cyDest = p->cyDest;
879 blt.dwRop = p->dwRop;
880 blt.xSrc = p->xSrc;
881 blt.ySrc = abs(bi->bmiHeader.biHeight) - p->ySrc - p->cySrc;
882 blt.xformSrc.eM11 = 1;
883 blt.xformSrc.eM22 = 1;
884 blt.iUsageSrc = p->iUsageSrc;
885 blt.cbBmiSrc = sizeof(bi_buffer);
886 blt.cbBitsSrc = p->cbBitsSrc;
887 blt.cxSrc = p->cxSrc;
888 blt.cySrc = p->cySrc;
889 return stretch_blt(ctx, &blt, bi, bits);
892 static int poly_draw(print_ctx *ctx, const POINT *points, const BYTE *types, DWORD count)
894 POINT first, cur, pts[4];
895 DWORD i, num_pts;
897 /* check for valid point types */
898 for (i = 0; i < count; i++)
900 switch (types[i])
902 case PT_MOVETO:
903 case PT_LINETO | PT_CLOSEFIGURE:
904 case PT_LINETO:
905 break;
906 case PT_BEZIERTO:
907 if (i + 2 >= count) return FALSE;
908 if (types[i + 1] != PT_BEZIERTO) return FALSE;
909 if ((types[i + 2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO) return FALSE;
910 i += 2;
911 break;
912 default:
913 return FALSE;
917 GetCurrentPositionEx(ctx->hdc, &cur);
918 first = cur;
920 for (i = 0; i < count; i++)
922 switch (types[i])
924 case PT_MOVETO:
925 first = points[i];
926 break;
927 case PT_LINETO:
928 case (PT_LINETO | PT_CLOSEFIGURE):
929 pts[0] = cur;
930 pts[1] = points[i];
931 num_pts = 2;
932 if (!PSDRV_PolyPolyline(ctx, pts, &num_pts, 1))
933 return FALSE;
934 break;
935 case PT_BEZIERTO:
936 pts[0] = cur;
937 pts[1] = points[i];
938 pts[2] = points[i + 1];
939 pts[3] = points[i + 2];
940 if (!PSDRV_PolyBezier(ctx, pts, 4))
941 return FALSE;
942 i += 2;
943 break;
946 cur = points[i];
947 if (types[i] & PT_CLOSEFIGURE)
949 pts[0] = cur;
950 pts[1] = first;
951 num_pts = 2;
952 if (!PSDRV_PolyPolyline(ctx, pts, &num_pts, 1))
953 return FALSE;
957 return TRUE;
960 static inline void reset_bounds(RECT *bounds)
962 bounds->left = bounds->top = INT_MAX;
963 bounds->right = bounds->bottom = INT_MIN;
966 static BOOL gradient_fill(print_ctx *ctx, const TRIVERTEX *vert_array, DWORD nvert,
967 const void *grad_array, DWORD ngrad, ULONG mode)
969 char buffer[FIELD_OFFSET(BITMAPINFO, bmiColors[256])];
970 BITMAPINFO *info = (BITMAPINFO *)buffer;
971 struct ps_bitblt_coords src, dst;
972 struct ps_image_bits bits;
973 HBITMAP bmp, old_bmp;
974 BOOL ret = FALSE;
975 TRIVERTEX *pts;
976 unsigned int i;
977 HDC hdc_src;
978 HRGN rgn;
980 if (!(pts = malloc(nvert * sizeof(*pts)))) return FALSE;
981 memcpy(pts, vert_array, sizeof(*pts) * nvert);
982 for (i = 0; i < nvert; i++)
983 LPtoDP(ctx->hdc, (POINT *)&pts[i], 1);
985 /* compute bounding rect of all the rectangles/triangles */
986 reset_bounds(&dst.visrect);
987 for (i = 0; i < ngrad * (mode == GRADIENT_FILL_TRIANGLE ? 3 : 2); i++)
989 ULONG v = ((ULONG *)grad_array)[i];
990 dst.visrect.left = min(dst.visrect.left, pts[v].x);
991 dst.visrect.top = min(dst.visrect.top, pts[v].y);
992 dst.visrect.right = max(dst.visrect.right, pts[v].x);
993 dst.visrect.bottom = max(dst.visrect.bottom, pts[v].y);
996 dst.x = dst.visrect.left;
997 dst.y = dst.visrect.top;
998 dst.width = dst.visrect.right - dst.visrect.left;
999 dst.height = dst.visrect.bottom - dst.visrect.top;
1000 clip_visrect(ctx->hdc, &dst.visrect, &dst.visrect);
1002 info->bmiHeader.biSize = sizeof(info->bmiHeader);
1003 info->bmiHeader.biPlanes = 1;
1004 info->bmiHeader.biBitCount = 24;
1005 info->bmiHeader.biCompression = BI_RGB;
1006 info->bmiHeader.biXPelsPerMeter = 0;
1007 info->bmiHeader.biYPelsPerMeter = 0;
1008 info->bmiHeader.biClrUsed = 0;
1009 info->bmiHeader.biClrImportant = 0;
1010 info->bmiHeader.biWidth = dst.visrect.right - dst.visrect.left;
1011 info->bmiHeader.biHeight = dst.visrect.bottom - dst.visrect.top;
1012 info->bmiHeader.biSizeImage = 0;
1013 memset(&bits, 0, sizeof(bits));
1014 hdc_src = CreateCompatibleDC(NULL);
1015 if (!hdc_src)
1017 free(pts);
1018 return FALSE;
1020 bmp = CreateDIBSection(hdc_src, info, DIB_RGB_COLORS, &bits.ptr, NULL, 0);
1021 if (!bmp)
1023 DeleteObject(hdc_src);
1024 free(pts);
1025 return FALSE;
1027 old_bmp = SelectObject(hdc_src, bmp);
1029 /* make src and points relative to the bitmap */
1030 src = dst;
1031 src.x -= dst.visrect.left;
1032 src.y -= dst.visrect.top;
1033 OffsetRect(&src.visrect, -dst.visrect.left, -dst.visrect.top);
1034 for (i = 0; i < nvert; i++)
1036 pts[i].x -= dst.visrect.left;
1037 pts[i].y -= dst.visrect.top;
1039 ret = GdiGradientFill(hdc_src, pts, nvert, (void *)grad_array, ngrad, mode);
1040 SelectObject(hdc_src, old_bmp);
1041 DeleteObject(hdc_src);
1043 rgn = CreateRectRgn(0, 0, 0, 0);
1044 if (mode == GRADIENT_FILL_TRIANGLE)
1046 const GRADIENT_TRIANGLE *gt = grad_array;
1047 POINT triangle[3];
1048 HRGN tmp;
1050 for (i = 0; i < ngrad; i++)
1052 triangle[0].x = pts[gt[i].Vertex1].x;
1053 triangle[0].y = pts[gt[i].Vertex1].y;
1054 triangle[1].x = pts[gt[i].Vertex2].x;
1055 triangle[1].y = pts[gt[i].Vertex2].y;
1056 triangle[2].x = pts[gt[i].Vertex3].x;
1057 triangle[2].y = pts[gt[i].Vertex3].y;
1058 tmp = CreatePolygonRgn(triangle, 3, ALTERNATE);
1059 CombineRgn(rgn, rgn, tmp, RGN_OR);
1060 DeleteObject(tmp);
1063 else
1065 const GRADIENT_RECT *gr = grad_array;
1066 HRGN tmp = CreateRectRgn(0, 0, 0, 0);
1068 for (i = 0; i < ngrad; i++)
1070 SetRectRgn(tmp, pts[gr[i].UpperLeft].x, pts[gr[i].UpperLeft].y,
1071 pts[gr[i].LowerRight].x, pts[gr[i].LowerRight].y);
1072 CombineRgn(rgn, rgn, tmp, RGN_OR);
1074 DeleteObject(tmp);
1076 free(pts);
1078 OffsetRgn(rgn, dst.visrect.left, dst.visrect.top);
1079 if (ret)
1080 ret = (PSDRV_PutImage(ctx, rgn, info, &bits, &src, &dst, SRCCOPY) == ERROR_SUCCESS);
1081 DeleteObject(rgn);
1082 DeleteObject(bmp);
1083 return ret;
1086 static HGDIOBJ get_object_handle(struct pp_data *data, HANDLETABLE *handletable,
1087 DWORD i, struct ps_brush_pattern **pattern)
1089 if (i & 0x80000000)
1091 *pattern = NULL;
1092 return GetStockObject(i & 0x7fffffff);
1094 *pattern = data->patterns + i;
1095 return handletable->objectHandle[i];
1098 static BOOL select_hbrush(struct pp_data *data, HANDLETABLE *htable, int handle_count, HBRUSH brush)
1100 struct ps_brush_pattern *pattern = NULL;
1101 int i;
1103 for (i = 0; i < handle_count; i++)
1105 if (htable->objectHandle[i] == brush)
1107 pattern = data->patterns + i;
1108 break;
1112 return PSDRV_SelectBrush(data->ctx, brush, pattern) != NULL;
1115 /* Performs a device to world transformation on the specified size (which
1116 * is in integer format).
1118 static inline INT INTERNAL_YWSTODS(HDC hdc, INT height)
1120 POINT pt[2];
1121 pt[0].x = pt[0].y = 0;
1122 pt[1].x = 0;
1123 pt[1].y = height;
1124 LPtoDP(hdc, pt, 2);
1125 return pt[1].y - pt[0].y;
1128 extern const unsigned short bidi_direction_table[] DECLSPEC_HIDDEN;
1130 /*------------------------------------------------------------------------
1131 Bidirectional Character Types
1133 as defined by the Unicode Bidirectional Algorithm Table 3-7.
1135 Note:
1137 The list of bidirectional character types here is not grouped the
1138 same way as the table 3-7, since the numeric values for the types
1139 are chosen to keep the state and action tables compact.
1140 ------------------------------------------------------------------------*/
1141 enum directions
1143 /* input types */
1144 /* ON MUST be zero, code relies on ON = N = 0 */
1145 ON = 0, /* Other Neutral */
1146 L, /* Left Letter */
1147 R, /* Right Letter */
1148 AN, /* Arabic Number */
1149 EN, /* European Number */
1150 AL, /* Arabic Letter (Right-to-left) */
1151 NSM, /* Non-spacing Mark */
1152 CS, /* Common Separator */
1153 ES, /* European Separator */
1154 ET, /* European Terminator (post/prefix e.g. $ and %) */
1156 /* resolved types */
1157 BN, /* Boundary neutral (type of RLE etc after explicit levels) */
1159 /* input types, */
1160 S, /* Segment Separator (TAB) // used only in L1 */
1161 WS, /* White space // used only in L1 */
1162 B, /* Paragraph Separator (aka as PS) */
1164 /* types for explicit controls */
1165 RLO, /* these are used only in X1-X9 */
1166 RLE,
1167 LRO,
1168 LRE,
1169 PDF,
1171 LRI, /* Isolate formatting characters new with 6.3 */
1172 RLI,
1173 FSI,
1174 PDI,
1176 /* resolved types, also resolved directions */
1177 NI = ON, /* alias, where ON, WS and S are treated the same */
1180 static inline unsigned short get_table_entry_32(const unsigned short *table, UINT ch)
1182 return table[table[table[table[ch >> 12] + ((ch >> 8) & 0x0f)] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
1185 /* Convert the libwine information to the direction enum */
1186 static void classify(LPCWSTR lpString, WORD *chartype, DWORD uCount)
1188 unsigned i;
1190 for (i = 0; i < uCount; ++i)
1191 chartype[i] = get_table_entry_32(bidi_direction_table, lpString[i]);
1194 /* Set a run of cval values at locations all prior to, but not including */
1195 /* iStart, to the new value nval. */
1196 static void SetDeferredRun(BYTE *pval, int cval, int iStart, int nval)
1198 int i = iStart - 1;
1199 for (; i >= iStart - cval; i--)
1201 pval[i] = nval;
1205 /* THE PARAGRAPH LEVEL */
1207 /*------------------------------------------------------------------------
1208 Function: resolveParagraphs
1210 Resolves the input strings into blocks over which the algorithm
1211 is then applied.
1213 Implements Rule P1 of the Unicode Bidi Algorithm
1215 Input: Text string
1216 Character count
1218 Output: revised character count
1220 Note: This is a very simplistic function. In effect it restricts
1221 the action of the algorithm to the first paragraph in the input
1222 where a paragraph ends at the end of the first block separator
1223 or at the end of the input text.
1225 ------------------------------------------------------------------------*/
1227 static int resolveParagraphs(WORD *types, int cch)
1229 /* skip characters not of type B */
1230 int ich = 0;
1231 for(; ich < cch && types[ich] != B; ich++);
1232 /* stop after first B, make it a BN for use in the next steps */
1233 if (ich < cch && types[ich] == B)
1234 types[ich++] = BN;
1235 return ich;
1238 /* REORDER */
1239 /*------------------------------------------------------------------------
1240 Function: resolveLines
1242 Breaks a paragraph into lines
1244 Input: Array of line break flags
1245 Character count
1246 In/Out: Array of characters
1248 Returns the count of characters on the first line
1250 Note: This function only breaks lines at hard line breaks. Other
1251 line breaks can be passed in. If pbrk[n] is TRUE, then a break
1252 occurs after the character in pszInput[n]. Breaks before the first
1253 character are not allowed.
1254 ------------------------------------------------------------------------*/
1255 static int resolveLines(LPCWSTR pszInput, const BOOL * pbrk, int cch)
1257 /* skip characters not of type LS */
1258 int ich = 0;
1259 for(; ich < cch; ich++)
1261 if (pszInput[ich] == (WCHAR)'\n' || (pbrk && pbrk[ich]))
1263 ich++;
1264 break;
1268 return ich;
1271 /*------------------------------------------------------------------------
1272 Function: resolveWhiteSpace
1274 Resolves levels for WS and S
1275 Implements rule L1 of the Unicode bidi Algorithm.
1277 Input: Base embedding level
1278 Character count
1279 Array of direction classes (for one line of text)
1281 In/Out: Array of embedding levels (for one line of text)
1283 Note: this should be applied a line at a time. The default driver
1284 code supplied in this file assumes a single line of text; for
1285 a real implementation, cch and the initial pointer values
1286 would have to be adjusted.
1287 ------------------------------------------------------------------------*/
1288 static void resolveWhitespace(int baselevel, const WORD *pcls, BYTE *plevel, int cch)
1290 int cchrun = 0;
1291 BYTE oldlevel = baselevel;
1293 int ich = 0;
1294 for (; ich < cch; ich++)
1296 switch(pcls[ich])
1298 default:
1299 cchrun = 0; /* any other character breaks the run */
1300 break;
1301 case WS:
1302 cchrun++;
1303 break;
1305 case RLE:
1306 case LRE:
1307 case LRO:
1308 case RLO:
1309 case PDF:
1310 case LRI:
1311 case RLI:
1312 case FSI:
1313 case PDI:
1314 case BN:
1315 plevel[ich] = oldlevel;
1316 cchrun++;
1317 break;
1319 case S:
1320 case B:
1321 /* reset levels for WS before eot */
1322 SetDeferredRun(plevel, cchrun, ich, baselevel);
1323 cchrun = 0;
1324 plevel[ich] = baselevel;
1325 break;
1327 oldlevel = plevel[ich];
1329 /* reset level before eot */
1330 SetDeferredRun(plevel, cchrun, ich, baselevel);
1333 /*------------------------------------------------------------------------
1334 Function: BidiLines
1336 Implements the Line-by-Line phases of the Unicode Bidi Algorithm
1338 Input: Count of characters
1339 Array of character directions
1341 Inp/Out: Input text
1342 Array of levels
1344 ------------------------------------------------------------------------*/
1345 static void BidiLines(int baselevel, LPWSTR pszOutLine, LPCWSTR pszLine, const WORD * pclsLine,
1346 BYTE * plevelLine, int cchPara, const BOOL * pbrk)
1348 int cchLine = 0;
1349 int done = 0;
1350 int *run;
1352 run = HeapAlloc(GetProcessHeap(), 0, cchPara * sizeof(int));
1353 if (!run)
1355 WARN("Out of memory\n");
1356 return;
1361 /* break lines at LS */
1362 cchLine = resolveLines(pszLine, pbrk, cchPara);
1364 /* resolve whitespace */
1365 resolveWhitespace(baselevel, pclsLine, plevelLine, cchLine);
1367 if (pszOutLine)
1369 int i;
1370 /* reorder each line in place */
1371 ScriptLayout(cchLine, plevelLine, NULL, run);
1372 for (i = 0; i < cchLine; i++)
1373 pszOutLine[done+run[i]] = pszLine[i];
1376 pszLine += cchLine;
1377 plevelLine += cchLine;
1378 pbrk += pbrk ? cchLine : 0;
1379 pclsLine += cchLine;
1380 cchPara -= cchLine;
1381 done += cchLine;
1383 } while (cchPara);
1385 HeapFree(GetProcessHeap(), 0, run);
1388 #define WINE_GCPW_FORCE_LTR 0
1389 #define WINE_GCPW_FORCE_RTL 1
1390 #define WINE_GCPW_DIR_MASK 3
1392 static BOOL BIDI_Reorder(HDC hDC, /* [in] Display DC */
1393 LPCWSTR lpString, /* [in] The string for which information is to be returned */
1394 INT uCount, /* [in] Number of WCHARs in string. */
1395 DWORD dwFlags, /* [in] GetCharacterPlacement compatible flags */
1396 DWORD dwWineGCP_Flags, /* [in] Wine internal flags - Force paragraph direction */
1397 LPWSTR lpOutString, /* [out] Reordered string */
1398 INT uCountOut, /* [in] Size of output buffer */
1399 UINT *lpOrder, /* [out] Logical -> Visual order map */
1400 WORD **lpGlyphs, /* [out] reordered, mirrored, shaped glyphs to display */
1401 INT *cGlyphs) /* [out] number of glyphs generated */
1403 WORD *chartype = NULL;
1404 BYTE *levels = NULL;
1405 INT i, done;
1406 unsigned glyph_i;
1407 BOOL is_complex, ret = FALSE;
1409 int maxItems;
1410 int nItems;
1411 SCRIPT_CONTROL Control;
1412 SCRIPT_STATE State;
1413 SCRIPT_ITEM *pItems = NULL;
1414 HRESULT res;
1415 SCRIPT_CACHE psc = NULL;
1416 WORD *run_glyphs = NULL;
1417 WORD *pwLogClust = NULL;
1418 SCRIPT_VISATTR *psva = NULL;
1419 DWORD cMaxGlyphs = 0;
1420 BOOL doGlyphs = TRUE;
1422 TRACE("%s, %d, 0x%08lx lpOutString=%p, lpOrder=%p\n",
1423 debugstr_wn(lpString, uCount), uCount, dwFlags,
1424 lpOutString, lpOrder);
1426 memset(&Control, 0, sizeof(Control));
1427 memset(&State, 0, sizeof(State));
1428 if (lpGlyphs)
1429 *lpGlyphs = NULL;
1431 if (!(dwFlags & GCP_REORDER))
1433 FIXME("Asked to reorder without reorder flag set\n");
1434 return FALSE;
1437 if (lpOutString && uCountOut < uCount)
1439 FIXME("lpOutString too small\n");
1440 return FALSE;
1443 chartype = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(WORD));
1444 if (!chartype)
1446 WARN("Out of memory\n");
1447 return FALSE;
1450 if (lpOutString)
1451 memcpy(lpOutString, lpString, uCount * sizeof(WCHAR));
1453 is_complex = FALSE;
1454 for (i = 0; i < uCount && !is_complex; i++)
1456 if ((lpString[i] >= 0x900 && lpString[i] <= 0xfff) ||
1457 (lpString[i] >= 0x1cd0 && lpString[i] <= 0x1cff) ||
1458 (lpString[i] >= 0xa840 && lpString[i] <= 0xa8ff))
1459 is_complex = TRUE;
1462 /* Verify reordering will be required */
1463 if (WINE_GCPW_FORCE_RTL == (dwWineGCP_Flags & WINE_GCPW_DIR_MASK))
1464 State.uBidiLevel = 1;
1465 else if (!is_complex)
1467 done = 1;
1468 classify(lpString, chartype, uCount);
1469 for (i = 0; i < uCount; i++)
1470 switch (chartype[i])
1472 case R:
1473 case AL:
1474 case RLE:
1475 case RLO:
1476 done = 0;
1477 break;
1479 if (done)
1481 HeapFree(GetProcessHeap(), 0, chartype);
1482 if (lpOrder)
1484 for (i = 0; i < uCount; i++)
1485 lpOrder[i] = i;
1487 return TRUE;
1491 levels = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(BYTE));
1492 if (!levels)
1494 WARN("Out of memory\n");
1495 goto cleanup;
1498 maxItems = 5;
1499 pItems = HeapAlloc(GetProcessHeap(),0, maxItems * sizeof(SCRIPT_ITEM));
1500 if (!pItems)
1502 WARN("Out of memory\n");
1503 goto cleanup;
1506 if (lpGlyphs)
1508 cMaxGlyphs = 1.5 * uCount + 16;
1509 run_glyphs = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * cMaxGlyphs);
1510 if (!run_glyphs)
1512 WARN("Out of memory\n");
1513 goto cleanup;
1515 pwLogClust = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * uCount);
1516 if (!pwLogClust)
1518 WARN("Out of memory\n");
1519 goto cleanup;
1521 psva = HeapAlloc(GetProcessHeap(),0,sizeof(SCRIPT_VISATTR) * cMaxGlyphs);
1522 if (!psva)
1524 WARN("Out of memory\n");
1525 goto cleanup;
1529 done = 0;
1530 glyph_i = 0;
1531 while (done < uCount)
1533 INT j;
1534 classify(lpString + done, chartype, uCount - done);
1535 /* limit text to first block */
1536 i = resolveParagraphs(chartype, uCount - done);
1537 for (j = 0; j < i; ++j)
1538 switch(chartype[j])
1540 case B:
1541 case S:
1542 case WS:
1543 case ON: chartype[j] = NI;
1544 default: continue;
1547 res = ScriptItemize(lpString + done, i, maxItems, &Control, &State, pItems, &nItems);
1548 while (res == E_OUTOFMEMORY)
1550 SCRIPT_ITEM *new_pItems = HeapReAlloc(GetProcessHeap(), 0, pItems, sizeof(*pItems) * maxItems * 2);
1551 if (!new_pItems)
1553 WARN("Out of memory\n");
1554 goto cleanup;
1556 pItems = new_pItems;
1557 maxItems *= 2;
1558 res = ScriptItemize(lpString + done, i, maxItems, &Control, &State, pItems, &nItems);
1561 if (lpOutString || lpOrder)
1562 for (j = 0; j < nItems; j++)
1564 int k;
1565 for (k = pItems[j].iCharPos; k < pItems[j+1].iCharPos; k++)
1566 levels[k] = pItems[j].a.s.uBidiLevel;
1569 if (lpOutString)
1571 /* assign directional types again, but for WS, S this time */
1572 classify(lpString + done, chartype, i);
1574 BidiLines(State.uBidiLevel, lpOutString + done, lpString + done,
1575 chartype, levels, i, 0);
1578 if (lpOrder)
1580 int k, lastgood;
1581 for (j = lastgood = 0; j < i; ++j)
1582 if (levels[j] != levels[lastgood])
1584 --j;
1585 if (levels[lastgood] & 1)
1586 for (k = j; k >= lastgood; --k)
1587 lpOrder[done + k] = done + j - k;
1588 else
1589 for (k = lastgood; k <= j; ++k)
1590 lpOrder[done + k] = done + k;
1591 lastgood = ++j;
1593 if (levels[lastgood] & 1)
1594 for (k = j - 1; k >= lastgood; --k)
1595 lpOrder[done + k] = done + j - 1 - k;
1596 else
1597 for (k = lastgood; k < j; ++k)
1598 lpOrder[done + k] = done + k;
1601 if (lpGlyphs && doGlyphs)
1603 BYTE *runOrder;
1604 int *visOrder;
1605 SCRIPT_ITEM *curItem;
1607 runOrder = HeapAlloc(GetProcessHeap(), 0, maxItems * sizeof(*runOrder));
1608 visOrder = HeapAlloc(GetProcessHeap(), 0, maxItems * sizeof(*visOrder));
1609 if (!runOrder || !visOrder)
1611 WARN("Out of memory\n");
1612 HeapFree(GetProcessHeap(), 0, runOrder);
1613 HeapFree(GetProcessHeap(), 0, visOrder);
1614 goto cleanup;
1617 for (j = 0; j < nItems; j++)
1618 runOrder[j] = pItems[j].a.s.uBidiLevel;
1620 ScriptLayout(nItems, runOrder, visOrder, NULL);
1622 for (j = 0; j < nItems; j++)
1624 int k;
1625 int cChars,cOutGlyphs;
1626 curItem = &pItems[visOrder[j]];
1628 cChars = pItems[visOrder[j]+1].iCharPos - curItem->iCharPos;
1630 res = ScriptShape(hDC, &psc, lpString + done + curItem->iCharPos, cChars, cMaxGlyphs, &curItem->a, run_glyphs, pwLogClust, psva, &cOutGlyphs);
1631 while (res == E_OUTOFMEMORY)
1633 WORD *new_run_glyphs = HeapReAlloc(GetProcessHeap(), 0, run_glyphs, sizeof(*run_glyphs) * cMaxGlyphs * 2);
1634 SCRIPT_VISATTR *new_psva = HeapReAlloc(GetProcessHeap(), 0, psva, sizeof(*psva) * cMaxGlyphs * 2);
1635 if (!new_run_glyphs || !new_psva)
1637 WARN("Out of memory\n");
1638 HeapFree(GetProcessHeap(), 0, runOrder);
1639 HeapFree(GetProcessHeap(), 0, visOrder);
1640 HeapFree(GetProcessHeap(), 0, *lpGlyphs);
1641 *lpGlyphs = NULL;
1642 if (new_run_glyphs)
1643 run_glyphs = new_run_glyphs;
1644 if (new_psva)
1645 psva = new_psva;
1646 goto cleanup;
1648 run_glyphs = new_run_glyphs;
1649 psva = new_psva;
1650 cMaxGlyphs *= 2;
1651 res = ScriptShape(hDC, &psc, lpString + done + curItem->iCharPos, cChars, cMaxGlyphs, &curItem->a, run_glyphs, pwLogClust, psva, &cOutGlyphs);
1653 if (res)
1655 if (res == USP_E_SCRIPT_NOT_IN_FONT)
1656 TRACE("Unable to shape with currently selected font\n");
1657 else
1658 FIXME("Unable to shape string (%lx)\n",res);
1659 j = nItems;
1660 doGlyphs = FALSE;
1661 HeapFree(GetProcessHeap(), 0, *lpGlyphs);
1662 *lpGlyphs = NULL;
1664 else
1666 WORD *new_glyphs;
1667 if (*lpGlyphs)
1668 new_glyphs = HeapReAlloc(GetProcessHeap(), 0, *lpGlyphs, sizeof(**lpGlyphs) * (glyph_i + cOutGlyphs));
1669 else
1670 new_glyphs = HeapAlloc(GetProcessHeap(), 0, sizeof(**lpGlyphs) * (glyph_i + cOutGlyphs));
1671 if (!new_glyphs)
1673 WARN("Out of memory\n");
1674 HeapFree(GetProcessHeap(), 0, runOrder);
1675 HeapFree(GetProcessHeap(), 0, visOrder);
1676 HeapFree(GetProcessHeap(), 0, *lpGlyphs);
1677 *lpGlyphs = NULL;
1678 goto cleanup;
1680 *lpGlyphs = new_glyphs;
1681 for (k = 0; k < cOutGlyphs; k++)
1682 (*lpGlyphs)[glyph_i+k] = run_glyphs[k];
1683 glyph_i += cOutGlyphs;
1686 HeapFree(GetProcessHeap(), 0, runOrder);
1687 HeapFree(GetProcessHeap(), 0, visOrder);
1690 done += i;
1692 if (cGlyphs)
1693 *cGlyphs = glyph_i;
1695 ret = TRUE;
1696 cleanup:
1697 HeapFree(GetProcessHeap(), 0, chartype);
1698 HeapFree(GetProcessHeap(), 0, levels);
1699 HeapFree(GetProcessHeap(), 0, pItems);
1700 HeapFree(GetProcessHeap(), 0, run_glyphs);
1701 HeapFree(GetProcessHeap(), 0, pwLogClust);
1702 HeapFree(GetProcessHeap(), 0, psva);
1703 ScriptFreeCache(&psc);
1704 return ret;
1707 static inline BOOL intersect_rect(RECT *dst, const RECT *src1, const RECT *src2)
1709 dst->left = max(src1->left, src2->left);
1710 dst->top = max(src1->top, src2->top);
1711 dst->right = min(src1->right, src2->right);
1712 dst->bottom = min(src1->bottom, src2->bottom);
1713 return !IsRectEmpty(dst);
1716 /***********************************************************************
1717 * get_line_width
1719 * Scale the underline / strikeout line width.
1721 static inline int get_line_width(HDC hdc, int metric_size)
1723 int width = abs(INTERNAL_YWSTODS(hdc, metric_size));
1724 if (width == 0) width = 1;
1725 if (metric_size < 0) width = -width;
1726 return width;
1729 static BOOL ext_text_out(struct pp_data *data, HANDLETABLE *htable,
1730 int handle_count, INT x, INT y, UINT flags, const RECT *rect,
1731 const WCHAR *str, UINT count, const INT *dx)
1733 HDC hdc = data->ctx->hdc;
1734 BOOL ret = FALSE;
1735 UINT align;
1736 DWORD layout;
1737 POINT pt;
1738 TEXTMETRICW tm;
1739 LOGFONTW lf;
1740 double cosEsc, sinEsc;
1741 INT char_extra;
1742 SIZE sz;
1743 RECT rc;
1744 POINT *deltas = NULL, width = {0, 0};
1745 INT breakRem;
1746 WORD *glyphs = NULL;
1747 XFORM xform;
1749 if (!(flags & (ETO_GLYPH_INDEX | ETO_IGNORELANGUAGE)) && count > 0)
1751 int glyphs_count = 0;
1752 UINT bidi_flags;
1754 bidi_flags = (GetTextAlign(hdc) & TA_RTLREADING) || (flags & ETO_RTLREADING)
1755 ? WINE_GCPW_FORCE_RTL : WINE_GCPW_FORCE_LTR;
1757 BIDI_Reorder(hdc, str, count, GCP_REORDER, bidi_flags,
1758 NULL, 0, NULL, &glyphs, &glyphs_count);
1760 flags |= ETO_IGNORELANGUAGE;
1761 if (glyphs)
1763 flags |= ETO_GLYPH_INDEX;
1764 count = glyphs_count;
1765 str = glyphs;
1769 align = GetTextAlign(hdc);
1770 breakRem = data->break_rem;
1771 layout = GetLayout(hdc);
1773 if (flags & ETO_RTLREADING) align |= TA_RTLREADING;
1774 if (layout & LAYOUT_RTL)
1776 if ((align & TA_CENTER) != TA_CENTER) align ^= TA_RIGHT;
1777 align ^= TA_RTLREADING;
1780 TRACE("%d, %d, %08x, %s, %s, %d, %p)\n", x, y, flags,
1781 wine_dbgstr_rect(rect), debugstr_wn(str, count), count, dx);
1782 TRACE("align = %x bkmode = %x mapmode = %x\n", align, GetBkMode(hdc),
1783 GetMapMode(hdc));
1785 if(align & TA_UPDATECP)
1787 GetCurrentPositionEx(hdc, &pt);
1788 x = pt.x;
1789 y = pt.y;
1792 GetTextMetricsW(hdc, &tm);
1793 GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf);
1795 if(!(tm.tmPitchAndFamily & TMPF_VECTOR)) /* Non-scalable fonts shouldn't be rotated */
1796 lf.lfEscapement = 0;
1798 GetWorldTransform(hdc, &xform);
1799 if (GetGraphicsMode(hdc) == GM_COMPATIBLE &&
1800 xform.eM11 * xform.eM22 < 0)
1802 lf.lfEscapement = -lf.lfEscapement;
1805 if(lf.lfEscapement != 0)
1807 cosEsc = cos(lf.lfEscapement * M_PI / 1800);
1808 sinEsc = sin(lf.lfEscapement * M_PI / 1800);
1810 else
1812 cosEsc = 1;
1813 sinEsc = 0;
1816 if (rect && (flags & (ETO_OPAQUE | ETO_CLIPPED)))
1818 rc = *rect;
1819 LPtoDP(hdc, (POINT*)&rc, 2);
1820 order_rect(&rc);
1821 if (flags & ETO_OPAQUE)
1822 PSDRV_ExtTextOut(data->ctx, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
1824 else flags &= ~ETO_CLIPPED;
1826 if(count == 0)
1828 ret = TRUE;
1829 goto done;
1832 pt.x = x;
1833 pt.y = y;
1834 LPtoDP(hdc, &pt, 1);
1835 x = pt.x;
1836 y = pt.y;
1838 char_extra = GetTextCharacterExtra(hdc);
1839 if (char_extra && dx)
1840 char_extra = 0; /* Printer drivers don't add char_extra if dx is supplied */
1842 if(char_extra || data->break_extra || breakRem || dx || lf.lfEscapement != 0)
1844 UINT i;
1845 POINT total = {0, 0}, desired[2];
1847 deltas = malloc(count * sizeof(*deltas));
1848 if (dx)
1850 if (flags & ETO_PDY)
1852 for (i = 0; i < count; i++)
1854 deltas[i].x = dx[i * 2] + char_extra;
1855 deltas[i].y = -dx[i * 2 + 1];
1858 else
1860 for (i = 0; i < count; i++)
1862 deltas[i].x = dx[i] + char_extra;
1863 deltas[i].y = 0;
1867 else
1869 INT *dx = malloc(count * sizeof(*dx));
1871 NtGdiGetTextExtentExW(hdc, str, count, -1, NULL, dx, &sz, !!(flags & ETO_GLYPH_INDEX));
1873 deltas[0].x = dx[0];
1874 deltas[0].y = 0;
1875 for (i = 1; i < count; i++)
1877 deltas[i].x = dx[i] - dx[i - 1];
1878 deltas[i].y = 0;
1880 free(dx);
1883 for(i = 0; i < count; i++)
1885 total.x += deltas[i].x;
1886 total.y += deltas[i].y;
1888 desired[0].x = desired[0].y = 0;
1890 desired[1].x = cosEsc * total.x + sinEsc * total.y;
1891 desired[1].y = -sinEsc * total.x + cosEsc * total.y;
1893 LPtoDP(hdc, desired, 2);
1894 desired[1].x -= desired[0].x;
1895 desired[1].y -= desired[0].y;
1897 if (GetGraphicsMode(hdc) == GM_COMPATIBLE)
1899 if (xform.eM11 < 0)
1900 desired[1].x = -desired[1].x;
1901 if (xform.eM22 < 0)
1902 desired[1].y = -desired[1].y;
1905 deltas[i].x = desired[1].x - width.x;
1906 deltas[i].y = desired[1].y - width.y;
1908 width = desired[1];
1910 flags |= ETO_PDY;
1912 else
1914 POINT desired[2];
1916 NtGdiGetTextExtentExW(hdc, str, count, 0, NULL, NULL, &sz, !!(flags & ETO_GLYPH_INDEX));
1917 desired[0].x = desired[0].y = 0;
1918 desired[1].x = sz.cx;
1919 desired[1].y = 0;
1920 LPtoDP(hdc, desired, 2);
1921 desired[1].x -= desired[0].x;
1922 desired[1].y -= desired[0].y;
1924 if (GetGraphicsMode(hdc) == GM_COMPATIBLE)
1926 if (xform.eM11 < 0)
1927 desired[1].x = -desired[1].x;
1928 if (xform.eM22 < 0)
1929 desired[1].y = -desired[1].y;
1931 width = desired[1];
1934 tm.tmAscent = abs(INTERNAL_YWSTODS(hdc, tm.tmAscent));
1935 tm.tmDescent = abs(INTERNAL_YWSTODS(hdc, tm.tmDescent));
1936 switch(align & (TA_LEFT | TA_RIGHT | TA_CENTER))
1938 case TA_LEFT:
1939 if (align & TA_UPDATECP)
1941 pt.x = x + width.x;
1942 pt.y = y + width.y;
1943 DPtoLP(hdc, &pt, 1);
1944 MoveToEx(hdc, pt.x, pt.y, NULL);
1946 break;
1948 case TA_CENTER:
1949 x -= width.x / 2;
1950 y -= width.y / 2;
1951 break;
1953 case TA_RIGHT:
1954 x -= width.x;
1955 y -= width.y;
1956 if (align & TA_UPDATECP)
1958 pt.x = x;
1959 pt.y = y;
1960 DPtoLP(hdc, &pt, 1);
1961 MoveToEx(hdc, pt.x, pt.y, NULL);
1963 break;
1966 switch(align & (TA_TOP | TA_BOTTOM | TA_BASELINE))
1968 case TA_TOP:
1969 y += tm.tmAscent * cosEsc;
1970 x += tm.tmAscent * sinEsc;
1971 break;
1973 case TA_BOTTOM:
1974 y -= tm.tmDescent * cosEsc;
1975 x -= tm.tmDescent * sinEsc;
1976 break;
1978 case TA_BASELINE:
1979 break;
1982 if (GetBkMode(hdc) != TRANSPARENT)
1984 if(!((flags & ETO_CLIPPED) && (flags & ETO_OPAQUE)))
1986 if(!(flags & ETO_OPAQUE) || !rect ||
1987 x < rc.left || x + width.x >= rc.right ||
1988 y - tm.tmAscent < rc.top || y + tm.tmDescent >= rc.bottom)
1990 RECT text_box;
1991 text_box.left = x;
1992 text_box.right = x + width.x;
1993 text_box.top = y - tm.tmAscent;
1994 text_box.bottom = y + tm.tmDescent;
1996 if (flags & ETO_CLIPPED) intersect_rect(&text_box, &text_box, &rc);
1997 if (!IsRectEmpty(&text_box))
1998 PSDRV_ExtTextOut(data->ctx, 0, 0, ETO_OPAQUE, &text_box, NULL, 0, NULL);
2003 ret = PSDRV_ExtTextOut(data->ctx, x, y, (flags & ~ETO_OPAQUE), &rc,
2004 str, count, (INT*)deltas);
2006 done:
2007 free(deltas);
2009 if (ret && (lf.lfUnderline || lf.lfStrikeOut))
2011 int underlinePos, strikeoutPos;
2012 int underlineWidth, strikeoutWidth;
2013 UINT size = NtGdiGetOutlineTextMetricsInternalW(hdc, 0, NULL, 0);
2014 OUTLINETEXTMETRICW* otm = NULL;
2015 POINT pts[5];
2016 HBRUSH hbrush = CreateSolidBrush(GetTextColor(hdc));
2017 HPEN hpen = GetStockObject(NULL_PEN);
2019 PSDRV_SelectPen(data->ctx, hpen, NULL);
2020 hpen = SelectObject(hdc, hpen);
2022 PSDRV_SelectBrush(data->ctx, hbrush, NULL);
2023 hbrush = SelectObject(hdc, hbrush);
2025 if(!size)
2027 underlinePos = 0;
2028 underlineWidth = tm.tmAscent / 20 + 1;
2029 strikeoutPos = tm.tmAscent / 2;
2030 strikeoutWidth = underlineWidth;
2032 else
2034 otm = malloc(size);
2035 NtGdiGetOutlineTextMetricsInternalW(hdc, size, otm, 0);
2036 underlinePos = abs(INTERNAL_YWSTODS(hdc, otm->otmsUnderscorePosition));
2037 if (otm->otmsUnderscorePosition < 0) underlinePos = -underlinePos;
2038 underlineWidth = get_line_width(hdc, otm->otmsUnderscoreSize);
2039 strikeoutPos = abs(INTERNAL_YWSTODS(hdc, otm->otmsStrikeoutPosition));
2040 if (otm->otmsStrikeoutPosition < 0) strikeoutPos = -strikeoutPos;
2041 strikeoutWidth = get_line_width(hdc, otm->otmsStrikeoutSize);
2042 free(otm);
2046 if (lf.lfUnderline)
2048 const INT cnt = 5;
2049 pts[0].x = x - (underlinePos + underlineWidth / 2) * sinEsc;
2050 pts[0].y = y - (underlinePos + underlineWidth / 2) * cosEsc;
2051 pts[1].x = x + width.x - (underlinePos + underlineWidth / 2) * sinEsc;
2052 pts[1].y = y + width.y - (underlinePos + underlineWidth / 2) * cosEsc;
2053 pts[2].x = pts[1].x + underlineWidth * sinEsc;
2054 pts[2].y = pts[1].y + underlineWidth * cosEsc;
2055 pts[3].x = pts[0].x + underlineWidth * sinEsc;
2056 pts[3].y = pts[0].y + underlineWidth * cosEsc;
2057 pts[4].x = pts[0].x;
2058 pts[4].y = pts[0].y;
2059 DPtoLP(hdc, pts, 5);
2060 PSDRV_PolyPolygon(data->ctx, pts, &cnt, 1);
2063 if (lf.lfStrikeOut)
2065 const INT cnt = 5;
2066 pts[0].x = x - (strikeoutPos + strikeoutWidth / 2) * sinEsc;
2067 pts[0].y = y - (strikeoutPos + strikeoutWidth / 2) * cosEsc;
2068 pts[1].x = x + width.x - (strikeoutPos + strikeoutWidth / 2) * sinEsc;
2069 pts[1].y = y + width.y - (strikeoutPos + strikeoutWidth / 2) * cosEsc;
2070 pts[2].x = pts[1].x + strikeoutWidth * sinEsc;
2071 pts[2].y = pts[1].y + strikeoutWidth * cosEsc;
2072 pts[3].x = pts[0].x + strikeoutWidth * sinEsc;
2073 pts[3].y = pts[0].y + strikeoutWidth * cosEsc;
2074 pts[4].x = pts[0].x;
2075 pts[4].y = pts[0].y;
2076 DPtoLP(hdc, pts, 5);
2077 PSDRV_PolyPolygon(data->ctx, pts, &cnt, 1);
2080 PSDRV_SelectPen(data->ctx, hpen, NULL);
2081 SelectObject(hdc, hpen);
2082 select_hbrush(data, htable, handle_count, hbrush);
2083 SelectObject(hdc, hbrush);
2084 DeleteObject(hbrush);
2087 HeapFree(GetProcessHeap(), 0, glyphs);
2088 return ret;
2091 static BOOL fill_rgn(struct pp_data *data, HANDLETABLE *htable, int handle_count, DWORD brush, HRGN rgn)
2093 struct ps_brush_pattern *pattern;
2094 HBRUSH hbrush;
2095 int ret;
2097 hbrush = get_object_handle(data, htable, brush, &pattern);
2098 PSDRV_SelectBrush(data->ctx, hbrush, pattern);
2099 ret = PSDRV_PaintRgn(data->ctx, rgn);
2100 select_hbrush(data, htable, handle_count, GetCurrentObject(data->ctx->hdc, OBJ_BRUSH));
2101 PSDRV_SelectBrush(data->ctx, hbrush, pattern);
2102 return ret;
2105 static BOOL is_path_record(int type)
2107 switch(type)
2109 case EMR_POLYBEZIER:
2110 case EMR_POLYGON:
2111 case EMR_POLYLINE:
2112 case EMR_POLYBEZIERTO:
2113 case EMR_POLYLINETO:
2114 case EMR_POLYPOLYLINE:
2115 case EMR_POLYPOLYGON:
2116 case EMR_MOVETOEX:
2117 case EMR_ANGLEARC:
2118 case EMR_ELLIPSE:
2119 case EMR_RECTANGLE:
2120 case EMR_ROUNDRECT:
2121 case EMR_ARC:
2122 case EMR_CHORD:
2123 case EMR_PIE:
2124 case EMR_LINETO:
2125 case EMR_ARCTO:
2126 case EMR_POLYDRAW:
2127 case EMR_EXTTEXTOUTA:
2128 case EMR_EXTTEXTOUTW:
2129 case EMR_POLYBEZIER16:
2130 case EMR_POLYGON16:
2131 case EMR_POLYLINE16:
2132 case EMR_POLYBEZIERTO16:
2133 case EMR_POLYLINETO16:
2134 case EMR_POLYPOLYLINE16:
2135 case EMR_POLYPOLYGON16:
2136 case EMR_POLYDRAW16:
2137 return TRUE;
2138 default:
2139 return FALSE;
2143 static int WINAPI hmf_proc(HDC hdc, HANDLETABLE *htable,
2144 const ENHMETARECORD *rec, int handle_count, LPARAM arg)
2146 struct pp_data *data = (struct pp_data *)arg;
2148 if (data->path && is_path_record(rec->iType))
2149 return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
2151 switch (rec->iType)
2153 case EMR_HEADER:
2155 const ENHMETAHEADER *header = (const ENHMETAHEADER *)rec;
2157 data->patterns = calloc(sizeof(*data->patterns), header->nHandles);
2158 return data->patterns && PSDRV_StartPage(data->ctx);
2160 case EMR_POLYBEZIER:
2162 const EMRPOLYBEZIER *p = (const EMRPOLYBEZIER *)rec;
2164 return PSDRV_PolyBezier(data->ctx, (const POINT *)p->aptl, p->cptl);
2166 case EMR_POLYGON:
2168 const EMRPOLYGON *p = (const EMRPOLYGON *)rec;
2170 return PSDRV_PolyPolygon(data->ctx, (const POINT *)p->aptl,
2171 (const INT *)&p->cptl, 1);
2173 case EMR_POLYLINE:
2175 const EMRPOLYLINE *p = (const EMRPOLYLINE *)rec;
2177 return PSDRV_PolyPolyline(data->ctx,
2178 (const POINT *)p->aptl, &p->cptl, 1);
2180 case EMR_POLYBEZIERTO:
2182 const EMRPOLYBEZIERTO *p = (const EMRPOLYBEZIERTO *)rec;
2184 return PSDRV_PolyBezierTo(data->ctx, (const POINT *)p->aptl, p->cptl) &&
2185 MoveToEx(data->ctx->hdc, p->aptl[p->cptl - 1].x, p->aptl[p->cptl - 1].y, NULL);
2187 case EMR_POLYLINETO:
2189 const EMRPOLYLINETO *p = (const EMRPOLYLINETO *)rec;
2190 POINT *pts;
2191 DWORD cnt;
2192 int ret;
2194 cnt = p->cptl + 1;
2195 pts = malloc(sizeof(*pts) * cnt);
2196 if (!pts) return 0;
2198 GetCurrentPositionEx(data->ctx->hdc, pts);
2199 memcpy(pts + 1, p->aptl, sizeof(*pts) * p->cptl);
2200 ret = PSDRV_PolyPolyline(data->ctx, pts, &cnt, 1) &&
2201 MoveToEx(data->ctx->hdc, pts[cnt - 1].x, pts[cnt - 1].y, NULL);
2202 free(pts);
2203 return ret;
2205 case EMR_POLYPOLYLINE:
2207 const EMRPOLYPOLYLINE *p = (const EMRPOLYPOLYLINE *)rec;
2209 return PSDRV_PolyPolyline(data->ctx,
2210 (const POINT *)(p->aPolyCounts + p->nPolys),
2211 p->aPolyCounts, p->nPolys);
2213 case EMR_POLYPOLYGON:
2215 const EMRPOLYPOLYGON *p = (const EMRPOLYPOLYGON *)rec;
2217 return PSDRV_PolyPolygon(data->ctx,
2218 (const POINT *)(p->aPolyCounts + p->nPolys),
2219 (const INT *)p->aPolyCounts, p->nPolys);
2221 case EMR_EOF:
2222 return PSDRV_EndPage(data->ctx);
2223 case EMR_SETPIXELV:
2225 const EMRSETPIXELV *p = (const EMRSETPIXELV *)rec;
2227 return PSDRV_SetPixel(data->ctx, p->ptlPixel.x,
2228 p->ptlPixel.y, p->crColor);
2230 case EMR_SETTEXTCOLOR:
2232 const EMRSETTEXTCOLOR *p = (const EMRSETTEXTCOLOR *)rec;
2234 SetTextColor(data->ctx->hdc, p->crColor);
2235 PSDRV_SetTextColor(data->ctx, p->crColor);
2236 return 1;
2238 case EMR_SETBKCOLOR:
2240 const EMRSETBKCOLOR *p = (const EMRSETBKCOLOR *)rec;
2242 SetBkColor(data->ctx->hdc, p->crColor);
2243 PSDRV_SetBkColor(data->ctx, p->crColor);
2244 return 1;
2246 case EMR_SAVEDC:
2248 int ret = PlayEnhMetaFileRecord(hdc, htable, rec, handle_count);
2250 if (!data->saved_dc_size)
2252 data->saved_dc = malloc(8 * sizeof(*data->saved_dc));
2253 if (data->saved_dc)
2254 data->saved_dc_size = 8;
2256 else if (data->saved_dc_size == data->saved_dc_top)
2258 void *alloc = realloc(data->saved_dc, data->saved_dc_size * 2);
2260 if (alloc)
2262 data->saved_dc = alloc;
2263 data->saved_dc_size *= 2;
2266 if (data->saved_dc_size == data->saved_dc_top)
2267 return 0;
2269 data->saved_dc[data->saved_dc_top].break_extra = data->break_extra;
2270 data->saved_dc[data->saved_dc_top].break_rem = data->break_rem;
2271 data->saved_dc_top++;
2272 return ret;
2274 case EMR_RESTOREDC:
2276 const EMRRESTOREDC *p = (const EMRRESTOREDC *)rec;
2277 HDC hdc = data->ctx->hdc;
2278 int ret = PlayEnhMetaFileRecord(hdc, htable, rec, handle_count);
2279 UINT aa_flags;
2281 if (ret)
2283 select_hbrush(data, htable, handle_count, GetCurrentObject(hdc, OBJ_BRUSH));
2284 PSDRV_SelectFont(data->ctx, GetCurrentObject(hdc, OBJ_FONT), &aa_flags);
2285 PSDRV_SelectPen(data->ctx, GetCurrentObject(hdc, OBJ_PEN), NULL);
2286 PSDRV_SetBkColor(data->ctx, GetBkColor(hdc));
2287 PSDRV_SetTextColor(data->ctx, GetTextColor(hdc));
2289 if (p->iRelative >= 0 || data->saved_dc_top + p->iRelative < 0)
2290 return 0;
2291 data->saved_dc_top += p->iRelative;
2292 data->break_extra = data->saved_dc[data->saved_dc_top].break_extra;
2293 data->break_rem = data->saved_dc[data->saved_dc_top].break_rem;
2295 return ret;
2297 case EMR_SELECTOBJECT:
2299 const EMRSELECTOBJECT *so = (const EMRSELECTOBJECT *)rec;
2300 struct ps_brush_pattern *pattern;
2301 UINT aa_flags;
2302 HGDIOBJ obj;
2304 obj = get_object_handle(data, htable, so->ihObject, &pattern);
2305 SelectObject(data->ctx->hdc, obj);
2307 switch (GetObjectType(obj))
2309 case OBJ_PEN: return PSDRV_SelectPen(data->ctx, obj, NULL) != NULL;
2310 case OBJ_BRUSH: return PSDRV_SelectBrush(data->ctx, obj, pattern) != NULL;
2311 case OBJ_FONT: return PSDRV_SelectFont(data->ctx, obj, &aa_flags) != NULL;
2312 default:
2313 FIXME("unhandled object type %ld\n", GetObjectType(obj));
2314 return 1;
2317 case EMR_DELETEOBJECT:
2319 const EMRDELETEOBJECT *p = (const EMRDELETEOBJECT *)rec;
2321 memset(&data->patterns[p->ihObject], 0, sizeof(*data->patterns));
2322 return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
2324 case EMR_ANGLEARC:
2326 const EMRANGLEARC *p = (const EMRANGLEARC *)rec;
2327 int arc_dir = SetArcDirection(data->ctx->hdc,
2328 p->eSweepAngle >= 0 ? AD_COUNTERCLOCKWISE : AD_CLOCKWISE);
2329 EMRARCTO arcto;
2330 int ret;
2332 arcto.emr.iType = EMR_ARCTO;
2333 arcto.rclBox.left = p->ptlCenter.x - p->nRadius;
2334 arcto.rclBox.top = p->ptlCenter.y - p->nRadius;
2335 arcto.rclBox.right = p->ptlCenter.x + p->nRadius;
2336 arcto.rclBox.bottom = p->ptlCenter.y + p->nRadius;
2337 arcto.ptlStart.x = GDI_ROUND(p->ptlCenter.x +
2338 cos(p->eStartAngle * M_PI / 180) * p->nRadius);
2339 arcto.ptlStart.y = GDI_ROUND(p->ptlCenter.y -
2340 sin(p->eStartAngle * M_PI / 180) * p->nRadius);
2341 arcto.ptlEnd.x = GDI_ROUND(p->ptlCenter.x +
2342 cos((p->eStartAngle + p->eSweepAngle) * M_PI / 180) * p->nRadius);
2343 arcto.ptlEnd.y = GDI_ROUND(p->ptlCenter.y -
2344 sin((p->eStartAngle + p->eSweepAngle) * M_PI / 180) * p->nRadius);
2346 ret = hmf_proc(hdc, htable, (ENHMETARECORD *)&arcto, handle_count, arg);
2347 SetArcDirection(data->ctx->hdc, arc_dir);
2348 return ret;
2350 case EMR_ELLIPSE:
2352 const EMRELLIPSE *p = (const EMRELLIPSE *)rec;
2353 const RECTL *r = &p->rclBox;
2355 return PSDRV_Ellipse(data->ctx, r->left, r->top, r->right, r->bottom);
2357 case EMR_RECTANGLE:
2359 const EMRRECTANGLE *rect = (const EMRRECTANGLE *)rec;
2361 return PSDRV_Rectangle(data->ctx, rect->rclBox.left,
2362 rect->rclBox.top, rect->rclBox.right, rect->rclBox.bottom);
2364 case EMR_ROUNDRECT:
2366 const EMRROUNDRECT *p = (const EMRROUNDRECT *)rec;
2368 return PSDRV_RoundRect(data->ctx, p->rclBox.left,
2369 p->rclBox.top, p->rclBox.right, p->rclBox.bottom,
2370 p->szlCorner.cx, p->szlCorner.cy);
2372 case EMR_ARC:
2374 const EMRARC *p = (const EMRARC *)rec;
2376 return PSDRV_Arc(data->ctx, p->rclBox.left, p->rclBox.top,
2377 p->rclBox.right, p->rclBox.bottom, p->ptlStart.x,
2378 p->ptlStart.y, p->ptlEnd.x, p->ptlEnd.y);
2380 case EMR_CHORD:
2382 const EMRCHORD *p = (const EMRCHORD *)rec;
2384 return PSDRV_Chord(data->ctx, p->rclBox.left, p->rclBox.top,
2385 p->rclBox.right, p->rclBox.bottom, p->ptlStart.x,
2386 p->ptlStart.y, p->ptlEnd.x, p->ptlEnd.y);
2388 case EMR_PIE:
2390 const EMRPIE *p = (const EMRPIE *)rec;
2392 return PSDRV_Pie(data->ctx, p->rclBox.left, p->rclBox.top,
2393 p->rclBox.right, p->rclBox.bottom, p->ptlStart.x,
2394 p->ptlStart.y, p->ptlEnd.x, p->ptlEnd.y);
2396 case EMR_LINETO:
2398 const EMRLINETO *line = (const EMRLINETO *)rec;
2400 return PSDRV_LineTo(data->ctx, line->ptl.x, line->ptl.y) &&
2401 MoveToEx(data->ctx->hdc, line->ptl.x, line->ptl.y, NULL);
2403 case EMR_ARCTO:
2405 const EMRARCTO *p = (const EMRARCTO *)rec;
2406 POINT pt;
2407 BOOL ret;
2409 ret = GetCurrentPositionEx(data->ctx->hdc, &pt);
2410 if (ret)
2412 ret = ArcTo(data->ctx->hdc, p->rclBox.left, p->rclBox.top,
2413 p->rclBox.right, p->rclBox.bottom, p->ptlStart.x,
2414 p->ptlStart.y, p->ptlStart.x, p->ptlStart.y);
2416 if (ret)
2417 ret = PSDRV_LineTo(data->ctx, pt.x, pt.y);
2418 if (ret)
2420 ret = PSDRV_Arc(data->ctx, p->rclBox.left, p->rclBox.top,
2421 p->rclBox.right, p->rclBox.bottom, p->ptlStart.x,
2422 p->ptlStart.y, p->ptlEnd.x, p->ptlEnd.y);
2424 if (ret)
2426 ret = ArcTo(data->ctx->hdc, p->rclBox.left, p->rclBox.top,
2427 p->rclBox.right, p->rclBox.bottom, p->ptlEnd.x,
2428 p->ptlEnd.y, p->ptlEnd.x, p->ptlEnd.y);
2430 return ret;
2432 case EMR_POLYDRAW:
2434 const EMRPOLYDRAW *p = (const EMRPOLYDRAW *)rec;
2435 const POINT *pts = (const POINT *)p->aptl;
2437 return poly_draw(data->ctx, pts, (BYTE *)(p->aptl + p->cptl), p->cptl) &&
2438 MoveToEx(data->ctx->hdc, pts[p->cptl - 1].x, pts[p->cptl - 1].y, NULL);
2440 case EMR_BEGINPATH:
2442 data->path = TRUE;
2443 return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
2445 case EMR_ENDPATH:
2447 data->path = FALSE;
2448 return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
2450 case EMR_FILLPATH:
2451 PSDRV_FillPath(data->ctx);
2452 return 1;
2453 case EMR_STROKEANDFILLPATH:
2454 PSDRV_StrokeAndFillPath(data->ctx);
2455 return 1;
2456 case EMR_STROKEPATH:
2457 PSDRV_StrokePath(data->ctx);
2458 return 1;
2459 case EMR_ABORTPATH:
2461 data->path = FALSE;
2462 return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
2464 case EMR_FILLRGN:
2466 const EMRFILLRGN *p = (const EMRFILLRGN *)rec;
2467 HRGN rgn;
2468 int ret;
2470 rgn = ExtCreateRegion(NULL, p->cbRgnData, (const RGNDATA *)p->RgnData);
2471 ret = fill_rgn(data, htable, handle_count, p->ihBrush, rgn);
2472 DeleteObject(rgn);
2473 return ret;
2475 case EMR_FRAMERGN:
2477 const EMRFRAMERGN *p = (const EMRFRAMERGN *)rec;
2478 HRGN rgn, frame;
2479 int ret;
2481 rgn = ExtCreateRegion(NULL, p->cbRgnData, (const RGNDATA *)p->RgnData);
2482 frame = CreateRectRgn(0, 0, 0, 0);
2484 CombineRgn(frame, rgn, 0, RGN_COPY);
2485 OffsetRgn(frame, -p->szlStroke.cx, 0);
2486 OffsetRgn(rgn, p->szlStroke.cx, 0);
2487 CombineRgn(frame, frame, rgn, RGN_AND);
2488 OffsetRgn(rgn, -p->szlStroke.cx, -p->szlStroke.cy);
2489 CombineRgn(frame, frame, rgn, RGN_AND);
2490 OffsetRgn(rgn, 0, 2*p->szlStroke.cy);
2491 CombineRgn(frame, frame, rgn, RGN_AND);
2492 OffsetRgn(rgn, 0, -p->szlStroke.cy);
2493 CombineRgn(frame, rgn, frame, RGN_DIFF);
2495 ret = fill_rgn(data, htable, handle_count, p->ihBrush, frame);
2496 DeleteObject(rgn);
2497 DeleteObject(frame);
2498 return ret;
2500 case EMR_INVERTRGN:
2502 const EMRINVERTRGN *p = (const EMRINVERTRGN *)rec;
2503 int old_rop, ret;
2504 HRGN rgn;
2506 rgn = ExtCreateRegion(NULL, p->cbRgnData, (const RGNDATA *)p->RgnData);
2507 old_rop = SetROP2(data->ctx->hdc, R2_NOT);
2508 ret = fill_rgn(data, htable, handle_count, 0x80000000 | BLACK_BRUSH, rgn);
2509 SetROP2(data->ctx->hdc, old_rop);
2510 DeleteObject(rgn);
2511 return ret;
2513 case EMR_PAINTRGN:
2515 const EMRPAINTRGN *p = (const EMRPAINTRGN *)rec;
2516 HRGN rgn = ExtCreateRegion(NULL, p->cbRgnData, (const RGNDATA *)p->RgnData);
2517 int ret;
2519 ret = PSDRV_PaintRgn(data->ctx, rgn);
2520 DeleteObject(rgn);
2521 return ret;
2523 case EMR_BITBLT:
2525 const EMRBITBLT *p = (const EMRBITBLT *)rec;
2526 const BITMAPINFO *bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiSrc);
2527 const BYTE *src_bits = (BYTE *)p + p->offBitsSrc;
2528 EMRSTRETCHBLT blt;
2531 blt.rclBounds = p->rclBounds;
2532 blt.xDest = p->xDest;
2533 blt.yDest = p->yDest;
2534 blt.cxDest = p->cxDest;
2535 blt.cyDest = p->cyDest;
2536 blt.dwRop = p->dwRop;
2537 blt.xSrc = p->xSrc;
2538 blt.ySrc = p->ySrc;
2539 blt.xformSrc = p->xformSrc;
2540 blt.crBkColorSrc = p->crBkColorSrc;
2541 blt.iUsageSrc = p->iUsageSrc;
2542 blt.offBmiSrc = p->offBmiSrc;
2543 blt.cbBmiSrc = p->cbBmiSrc;
2544 blt.offBitsSrc = p->offBitsSrc;
2545 blt.cbBitsSrc = p->cbBitsSrc;
2546 blt.cxSrc = p->cxDest;
2547 blt.cySrc = p->cyDest;
2549 return stretch_blt(data->ctx, &blt, bi, src_bits);
2551 case EMR_STRETCHBLT:
2553 const EMRSTRETCHBLT *p = (const EMRSTRETCHBLT *)rec;
2554 const BITMAPINFO *bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiSrc);
2555 const BYTE *src_bits = (BYTE *)p + p->offBitsSrc;
2557 return stretch_blt(data->ctx, p, bi, src_bits);
2559 case EMR_MASKBLT:
2561 const EMRMASKBLT *p = (const EMRMASKBLT *)rec;
2562 const BITMAPINFO *mask_bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiMask);
2563 const BITMAPINFO *src_bi = (const BITMAPINFO *)((BYTE *)p + p->offBmiSrc);
2564 const BYTE *mask_bits = (BYTE *)p + p->offBitsMask;
2565 const BYTE *src_bits = (BYTE *)p + p->offBitsSrc;
2567 return mask_blt(data->ctx, p, src_bi, src_bits, mask_bi, mask_bits);
2569 case EMR_PLGBLT:
2571 const EMRPLGBLT *p = (const EMRPLGBLT *)rec;
2573 return plg_blt(data->ctx, p);
2575 case EMR_SETDIBITSTODEVICE:
2576 return set_di_bits_to_device(data->ctx, (const EMRSETDIBITSTODEVICE *)rec);
2577 case EMR_STRETCHDIBITS:
2578 return stretch_di_bits(data->ctx, (const EMRSTRETCHDIBITS *)rec);
2579 case EMR_EXTTEXTOUTW:
2581 const EMREXTTEXTOUTW *p = (const EMREXTTEXTOUTW *)rec;
2582 HDC hdc = data->ctx->hdc;
2583 const INT *dx = NULL;
2584 int old_mode, ret;
2585 RECT rect;
2587 rect.left = p->emrtext.rcl.left;
2588 rect.top = p->emrtext.rcl.top;
2589 rect.right = p->emrtext.rcl.right;
2590 rect.bottom = p->emrtext.rcl.bottom;
2592 old_mode = SetGraphicsMode(hdc, p->iGraphicsMode);
2593 /* Reselect the font back into the dc so that the transformation
2594 gets updated. */
2595 SelectObject(hdc, GetCurrentObject(hdc, OBJ_FONT));
2597 if (p->emrtext.offDx)
2598 dx = (const INT *)((const BYTE *)rec + p->emrtext.offDx);
2600 ret = ext_text_out(data, htable, handle_count, p->emrtext.ptlReference.x,
2601 p->emrtext.ptlReference.y, p->emrtext.fOptions, &rect,
2602 (LPCWSTR)((const BYTE *)rec + p->emrtext.offString), p->emrtext.nChars, dx);
2604 SetGraphicsMode(hdc, old_mode);
2605 return ret;
2607 case EMR_POLYBEZIER16:
2609 const EMRPOLYBEZIER16 *p = (const EMRPOLYBEZIER16 *)rec;
2610 POINT *pts;
2611 int i;
2613 pts = malloc(sizeof(*pts) * p->cpts);
2614 if (!pts) return 0;
2615 for (i = 0; i < p->cpts; i++)
2617 pts[i].x = p->apts[i].x;
2618 pts[i].y = p->apts[i].y;
2620 i = PSDRV_PolyBezier(data->ctx, pts, p->cpts);
2621 free(pts);
2622 return i;
2624 case EMR_POLYGON16:
2626 const EMRPOLYGON16 *p = (const EMRPOLYGON16 *)rec;
2627 POINT *pts;
2628 int i;
2630 pts = malloc(sizeof(*pts) * p->cpts);
2631 if (!pts) return 0;
2632 for (i = 0; i < p->cpts; i++)
2634 pts[i].x = p->apts[i].x;
2635 pts[i].y = p->apts[i].y;
2637 i = PSDRV_PolyPolygon(data->ctx, pts, (const INT *)&p->cpts, 1);
2638 free(pts);
2639 return i;
2641 case EMR_POLYLINE16:
2643 const EMRPOLYLINE16 *p = (const EMRPOLYLINE16 *)rec;
2644 POINT *pts;
2645 int i;
2647 pts = malloc(sizeof(*pts) * p->cpts);
2648 if (!pts) return 0;
2649 for (i = 0; i < p->cpts; i++)
2651 pts[i].x = p->apts[i].x;
2652 pts[i].y = p->apts[i].y;
2654 i = PSDRV_PolyPolyline(data->ctx, pts, &p->cpts, 1);
2655 free(pts);
2656 return i;
2658 case EMR_POLYBEZIERTO16:
2660 const EMRPOLYBEZIERTO16 *p = (const EMRPOLYBEZIERTO16 *)rec;
2661 POINT *pts;
2662 int i;
2664 pts = malloc(sizeof(*pts) * p->cpts);
2665 if (!pts) return 0;
2666 for (i = 0; i < p->cpts; i++)
2668 pts[i].x = p->apts[i].x;
2669 pts[i].y = p->apts[i].y;
2671 i = PSDRV_PolyBezierTo(data->ctx, pts, p->cpts) &&
2672 MoveToEx(data->ctx->hdc, pts[p->cpts - 1].x, pts[p->cpts - 1].y, NULL);
2673 free(pts);
2674 return i;
2676 case EMR_POLYLINETO16:
2678 const EMRPOLYLINETO16 *p = (const EMRPOLYLINETO16 *)rec;
2679 POINT *pts;
2680 DWORD cnt;
2681 int i;
2683 cnt = p->cpts + 1;
2684 pts = malloc(sizeof(*pts) * cnt);
2685 if (!pts) return 0;
2686 GetCurrentPositionEx(data->ctx->hdc, pts);
2687 for (i = 0; i < p->cpts; i++)
2689 pts[i + 1].x = p->apts[i].x;
2690 pts[i + 1].y = p->apts[i].y;
2692 i = PSDRV_PolyPolyline(data->ctx, pts, &cnt, 1) &&
2693 MoveToEx(data->ctx->hdc, pts[cnt - 1].x, pts[cnt - 1].y, NULL);
2694 free(pts);
2695 return i;
2697 case EMR_POLYPOLYLINE16:
2699 const EMRPOLYPOLYLINE16 *p = (const EMRPOLYPOLYLINE16 *)rec;
2700 POINT *pts;
2701 int i;
2703 pts = malloc(sizeof(*pts) * p->cpts);
2704 if (!pts) return 0;
2705 for (i = 0; i < p->cpts; i++)
2707 pts[i].x = ((const POINTS *)(p->aPolyCounts + p->nPolys))[i].x;
2708 pts[i].y = ((const POINTS *)(p->aPolyCounts + p->nPolys))[i].y;
2710 i = PSDRV_PolyPolyline(data->ctx, pts, p->aPolyCounts, p->nPolys);
2711 free(pts);
2712 return i;
2714 case EMR_POLYPOLYGON16:
2716 const EMRPOLYPOLYGON16 *p = (const EMRPOLYPOLYGON16 *)rec;
2717 POINT *pts;
2718 int i;
2720 pts = malloc(sizeof(*pts) * p->cpts);
2721 if (!pts) return 0;
2722 for (i = 0; i < p->cpts; i++)
2724 pts[i].x = ((const POINTS *)(p->aPolyCounts + p->nPolys))[i].x;
2725 pts[i].y = ((const POINTS *)(p->aPolyCounts + p->nPolys))[i].y;
2727 i = PSDRV_PolyPolygon(data->ctx, pts, (const INT *)p->aPolyCounts, p->nPolys);
2728 free(pts);
2729 return i;
2731 case EMR_POLYDRAW16:
2733 const EMRPOLYDRAW16 *p = (const EMRPOLYDRAW16 *)rec;
2734 POINT *pts;
2735 int i;
2737 pts = malloc(sizeof(*pts) * p->cpts);
2738 if (!pts) return 0;
2739 for (i = 0; i < p->cpts; i++)
2741 pts[i].x = p->apts[i].x;
2742 pts[i].y = p->apts[i].y;
2744 i = poly_draw(data->ctx, pts, (BYTE *)(p->apts + p->cpts), p->cpts) &&
2745 MoveToEx(data->ctx->hdc, pts[p->cpts - 1].x, pts[p->cpts - 1].y, NULL);
2746 free(pts);
2747 return i;
2749 case EMR_CREATEMONOBRUSH:
2751 const EMRCREATEMONOBRUSH *p = (const EMRCREATEMONOBRUSH *)rec;
2753 if (!PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count))
2754 return 0;
2755 data->patterns[p->ihBrush].usage = p->iUsage;
2756 data->patterns[p->ihBrush].info = (BITMAPINFO *)((BYTE *)p + p->offBmi);
2757 data->patterns[p->ihBrush].bits.ptr = (BYTE *)p + p->offBits;
2758 return 1;
2760 case EMR_CREATEDIBPATTERNBRUSHPT:
2762 const EMRCREATEDIBPATTERNBRUSHPT *p = (const EMRCREATEDIBPATTERNBRUSHPT *)rec;
2764 if (!PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count))
2765 return 0;
2766 data->patterns[p->ihBrush].usage = p->iUsage;
2767 data->patterns[p->ihBrush].info = (BITMAPINFO *)((BYTE *)p + p->offBmi);
2768 data->patterns[p->ihBrush].bits.ptr = (BYTE *)p + p->offBits;
2769 return 1;
2771 case EMR_EXTESCAPE:
2773 const struct EMREXTESCAPE
2775 EMR emr;
2776 DWORD escape;
2777 DWORD size;
2778 BYTE data[1];
2779 } *p = (const struct EMREXTESCAPE *)rec;
2781 PSDRV_ExtEscape(data->ctx, p->escape, p->size, p->data, 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 data->break_extra = p->break_extra / p->break_count;
2796 data->break_rem = p->break_extra - data->break_extra * p->break_count;
2797 return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
2800 case EMR_EXTFLOODFILL:
2801 case EMR_ALPHABLEND:
2802 break;
2804 case EMR_SETWINDOWEXTEX:
2805 case EMR_SETWINDOWORGEX:
2806 case EMR_SETVIEWPORTEXTEX:
2807 case EMR_SETVIEWPORTORGEX:
2808 case EMR_SETBRUSHORGEX:
2809 case EMR_SETMAPPERFLAGS:
2810 case EMR_SETMAPMODE:
2811 case EMR_SETBKMODE:
2812 case EMR_SETPOLYFILLMODE:
2813 case EMR_SETROP2:
2814 case EMR_SETSTRETCHBLTMODE:
2815 case EMR_SETTEXTALIGN:
2816 case EMR_OFFSETCLIPRGN:
2817 case EMR_MOVETOEX:
2818 case EMR_EXCLUDECLIPRECT:
2819 case EMR_INTERSECTCLIPRECT:
2820 case EMR_SCALEVIEWPORTEXTEX:
2821 case EMR_SCALEWINDOWEXTEX:
2822 case EMR_SETWORLDTRANSFORM:
2823 case EMR_MODIFYWORLDTRANSFORM:
2824 case EMR_CREATEPEN:
2825 case EMR_CREATEBRUSHINDIRECT:
2826 case EMR_SELECTPALETTE:
2827 case EMR_CREATEPALETTE:
2828 case EMR_SETPALETTEENTRIES:
2829 case EMR_RESIZEPALETTE:
2830 case EMR_REALIZEPALETTE:
2831 case EMR_SETARCDIRECTION:
2832 case EMR_CLOSEFIGURE:
2833 case EMR_FLATTENPATH:
2834 case EMR_WIDENPATH:
2835 case EMR_SELECTCLIPPATH:
2836 case EMR_EXTSELECTCLIPRGN:
2837 case EMR_EXTCREATEFONTINDIRECTW:
2838 case EMR_SETLAYOUT:
2839 return PlayEnhMetaFileRecord(data->ctx->hdc, htable, rec, handle_count);
2840 default:
2841 FIXME("unsupported record: %ld\n", rec->iType);
2844 return 1;
2847 static BOOL print_metafile(struct pp_data *data, HANDLE hdata)
2849 XFORM xform = { .eM11 = 1, .eM22 = 1 };
2850 record_hdr header;
2851 HENHMETAFILE hmf;
2852 BYTE *buf;
2853 BOOL ret;
2854 DWORD r;
2856 if (!ReadPrinter(hdata, &header, sizeof(header), &r))
2857 return FALSE;
2858 if (r != sizeof(header))
2860 SetLastError(ERROR_INVALID_DATA);
2861 return FALSE;
2864 buf = malloc(header.cjSize);
2865 if (!buf)
2866 return FALSE;
2868 if (!ReadPrinter(hdata, buf, header.cjSize, &r))
2870 free(buf);
2871 return FALSE;
2873 if (r != header.cjSize)
2875 free(buf);
2876 SetLastError(ERROR_INVALID_DATA);
2877 return FALSE;
2880 hmf = SetEnhMetaFileBits(header.cjSize, buf);
2881 free(buf);
2882 if (!hmf)
2883 return FALSE;
2885 AbortPath(data->ctx->hdc);
2886 MoveToEx(data->ctx->hdc, 0, 0, NULL);
2887 SetBkColor(data->ctx->hdc, RGB(255, 255, 255));
2888 SetBkMode(data->ctx->hdc, OPAQUE);
2889 SetMapMode(data->ctx->hdc, MM_TEXT);
2890 SetPolyFillMode(data->ctx->hdc, ALTERNATE);
2891 SetROP2(data->ctx->hdc, R2_COPYPEN);
2892 SetStretchBltMode(data->ctx->hdc, BLACKONWHITE);
2893 SetTextAlign(data->ctx->hdc, TA_LEFT | TA_TOP);
2894 SetTextColor(data->ctx->hdc, 0);
2895 SetTextJustification(data->ctx->hdc, 0, 0);
2896 SetWorldTransform(data->ctx->hdc, &xform);
2897 PSDRV_SetTextColor(data->ctx, 0);
2898 PSDRV_SetBkColor(data->ctx, RGB(255, 255, 255));
2900 ret = EnumEnhMetaFile(NULL, hmf, hmf_proc, (void *)data, NULL);
2901 DeleteEnhMetaFile(hmf);
2902 free(data->patterns);
2903 data->patterns = NULL;
2904 data->path = FALSE;
2905 data->break_extra = 0;
2906 data->break_rem = 0;
2907 data->saved_dc_top = 0;
2908 return ret;
2911 BOOL WINAPI EnumPrintProcessorDatatypesW(WCHAR *server, WCHAR *name, DWORD level,
2912 BYTE *datatypes, DWORD size, DWORD *needed, DWORD *no)
2914 DATATYPES_INFO_1W *info = (DATATYPES_INFO_1W *)datatypes;
2916 TRACE("%s, %s, %ld, %p, %ld, %p, %p\n", debugstr_w(server), debugstr_w(name),
2917 level, datatypes, size, needed, no);
2919 if (!needed || !no)
2921 SetLastError(ERROR_INVALID_PARAMETER);
2922 return FALSE;
2925 *no = 0;
2926 *needed = sizeof(*info) + sizeof(emf_1003);
2928 if (level != 1 || (size && !datatypes))
2930 SetLastError(ERROR_INVALID_PARAMETER);
2931 return FALSE;
2934 if (size < *needed)
2936 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2937 return FALSE;
2940 *no = 1;
2941 info->pName = (WCHAR*)(info + 1);
2942 memcpy(info + 1, emf_1003, sizeof(emf_1003));
2943 return TRUE;
2946 HANDLE WINAPI OpenPrintProcessor(WCHAR *port, PRINTPROCESSOROPENDATA *open_data)
2948 struct pp_data *data;
2949 HANDLE hport;
2950 HDC hdc;
2952 TRACE("%s, %p\n", debugstr_w(port), open_data);
2954 if (!port || !open_data || !open_data->pDatatype)
2956 SetLastError(ERROR_INVALID_PARAMETER);
2957 return NULL;
2959 if (wcscmp(open_data->pDatatype, emf_1003))
2961 SetLastError(ERROR_INVALID_DATATYPE);
2962 return NULL;
2965 if (!OpenPrinterW(port, &hport, NULL))
2966 return NULL;
2968 data = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(*data));
2969 if (!data)
2970 return NULL;
2971 data->magic = PP_MAGIC;
2972 data->hport = hport;
2973 data->doc_name = wcsdup(open_data->pDocumentName);
2974 data->out_file = wcsdup(open_data->pOutputFile);
2976 hdc = CreateDCW(L"winspool", open_data->pPrinterName, NULL, open_data->pDevMode);
2977 if (!hdc)
2979 LocalFree(data);
2980 return NULL;
2982 SetGraphicsMode(hdc, GM_ADVANCED);
2983 data->ctx = create_print_ctx(hdc, open_data->pPrinterName, open_data->pDevMode);
2984 if (!data->ctx)
2986 DeleteDC(hdc);
2987 LocalFree(data);
2988 return NULL;
2990 return (HANDLE)data;
2993 BOOL WINAPI PrintDocumentOnPrintProcessor(HANDLE pp, WCHAR *doc_name)
2995 struct pp_data *data = get_handle_data(pp);
2996 emfspool_header header;
2997 LARGE_INTEGER pos, cur;
2998 record_hdr record;
2999 HANDLE spool_data;
3000 DOC_INFO_1W info;
3001 BOOL ret;
3002 DWORD r;
3004 TRACE("%p, %s\n", pp, debugstr_w(doc_name));
3006 if (!data)
3007 return FALSE;
3009 if (!OpenPrinterW(doc_name, &spool_data, NULL))
3010 return FALSE;
3012 info.pDocName = data->doc_name;
3013 info.pOutputFile = data->out_file;
3014 info.pDatatype = (WCHAR *)L"RAW";
3015 data->ctx->job.id = StartDocPrinterW(data->hport, 1, (BYTE *)&info);
3016 if (!data->ctx->job.id)
3018 ClosePrinter(spool_data);
3019 return FALSE;
3022 if (!(ret = ReadPrinter(spool_data, &header, sizeof(header), &r)))
3023 goto cleanup;
3024 if (r != sizeof(header))
3026 SetLastError(ERROR_INVALID_DATA);
3027 ret = FALSE;
3028 goto cleanup;
3031 if (header.dwVersion != EMFSPOOL_VERSION)
3033 FIXME("unrecognized spool file format\n");
3034 SetLastError(ERROR_INTERNAL_ERROR);
3035 goto cleanup;
3037 pos.QuadPart = header.cjSize;
3038 if (!(ret = SeekPrinter(spool_data, pos, NULL, FILE_BEGIN, FALSE)))
3039 goto cleanup;
3041 data->ctx->job.hprinter = data->hport;
3042 if (!PSDRV_WriteHeader(data->ctx, data->doc_name))
3044 WARN("Failed to write header\n");
3045 goto cleanup;
3047 data->ctx->job.OutOfPage = TRUE;
3048 data->ctx->job.PageNo = 0;
3049 data->ctx->job.quiet = FALSE;
3050 data->ctx->job.passthrough_state = passthrough_none;
3051 data->ctx->job.doc_name = strdupW(data->doc_name);
3053 while (1)
3055 if (!(ret = ReadPrinter(spool_data, &record, sizeof(record), &r)))
3056 goto cleanup;
3057 if (!r)
3058 break;
3059 if (r != sizeof(record))
3061 SetLastError(ERROR_INVALID_DATA);
3062 ret = FALSE;
3063 goto cleanup;
3066 switch (record.ulID)
3068 case EMRI_DEVMODE:
3070 DEVMODEW *devmode = NULL;
3072 if (record.cjSize)
3074 devmode = malloc(record.cjSize);
3075 if (!devmode)
3076 goto cleanup;
3077 ret = ReadPrinter(spool_data, devmode, record.cjSize, &r);
3078 if (ret && r != record.cjSize)
3080 SetLastError(ERROR_INVALID_DATA);
3081 ret = FALSE;
3085 if (ret)
3086 ret = PSDRV_ResetDC(data->ctx, devmode);
3087 free(devmode);
3088 if (!ret)
3089 goto cleanup;
3090 break;
3092 case EMRI_METAFILE_DATA:
3093 pos.QuadPart = record.cjSize;
3094 ret = SeekPrinter(spool_data, pos, NULL, FILE_CURRENT, FALSE);
3095 if (!ret)
3096 goto cleanup;
3097 break;
3098 case EMRI_METAFILE_EXT:
3099 case EMRI_BW_METAFILE_EXT:
3100 pos.QuadPart = 0;
3101 ret = SeekPrinter(spool_data, pos, &cur, FILE_CURRENT, FALSE);
3102 if (ret)
3104 cur.QuadPart += record.cjSize;
3105 ret = ReadPrinter(spool_data, &pos, sizeof(pos), &r);
3106 if (ret && r != sizeof(pos))
3108 SetLastError(ERROR_INVALID_DATA);
3109 ret = FALSE;
3112 pos.QuadPart = -pos.QuadPart - 2 * sizeof(record);
3113 if (ret)
3114 ret = SeekPrinter(spool_data, pos, NULL, FILE_CURRENT, FALSE);
3115 if (ret)
3116 ret = print_metafile(data, spool_data);
3117 if (ret)
3118 ret = SeekPrinter(spool_data, cur, NULL, FILE_BEGIN, FALSE);
3119 if (!ret)
3120 goto cleanup;
3121 break;
3122 default:
3123 FIXME("%s not supported, skipping\n", debugstr_rec_type(record.ulID));
3124 pos.QuadPart = record.cjSize;
3125 ret = SeekPrinter(spool_data, pos, NULL, FILE_CURRENT, FALSE);
3126 if (!ret)
3127 goto cleanup;
3128 break;
3132 cleanup:
3133 if (data->ctx->job.PageNo)
3134 PSDRV_WriteFooter(data->ctx);
3136 HeapFree(GetProcessHeap(), 0, data->ctx->job.doc_name);
3137 ClosePrinter(spool_data);
3138 return EndDocPrinter(data->hport) && ret;
3141 BOOL WINAPI ControlPrintProcessor(HANDLE pp, DWORD cmd)
3143 FIXME("%p, %ld\n", pp, cmd);
3144 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3145 return FALSE;
3148 BOOL WINAPI ClosePrintProcessor(HANDLE pp)
3150 struct pp_data *data = get_handle_data(pp);
3152 TRACE("%p\n", pp);
3154 if (!data)
3155 return FALSE;
3157 ClosePrinter(data->hport);
3158 free(data->doc_name);
3159 free(data->out_file);
3160 DeleteDC(data->ctx->hdc);
3161 HeapFree(GetProcessHeap(), 0, data->ctx->Devmode);
3162 HeapFree(GetProcessHeap(), 0, data->ctx);
3163 free(data->saved_dc);
3165 memset(data, 0, sizeof(*data));
3166 LocalFree(data);
3167 return TRUE;
3170 HRESULT WINAPI DllRegisterServer(void)
3172 AddPrintProcessorW(NULL, (WCHAR *)L"Windows 4.0", (WCHAR *)L"wineps.drv", (WCHAR *)L"wineps");
3173 AddPrintProcessorW(NULL, NULL, (WCHAR *)L"wineps.drv", (WCHAR *)L"wineps");
3174 return S_OK;