wine/dlls/wineps.drv/unixlib.c
Fabian Maurer 9b12065fc5 wineps.drv: Use afm FamilyName instead of FullName.
Fixes a regression introduced by a6cb10bba2
The old logic also used FamilyName, and some applications depend on that

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54993
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54980
Signed-off-by: Fabian Maurer <dark.shadow4@web.de>
2023-06-12 21:21:42 +02:00

1802 lines
56 KiB
C

/*
* Unix interface for wineps.drv
*
* Copyright 1998 Huw D M Davies
* Copyright 2001 Marcus Meissner
* Copyright 2023 Piotr Caban for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#if 0
#pragma makedep unix
#endif
#include <stdarg.h>
#include <stdlib.h>
#include <math.h>
#include "windef.h"
#include "winbase.h"
#include "ntf.h"
#include "unixlib.h"
#include "ntgdi.h"
#include "ddk/winddi.h"
#include "wine/gdi_driver.h"
#include "wine/debug.h"
#include "wine/wingdi16.h"
WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
static const WCHAR timesW[] = {'T','i','m','e','s',0};
static const WCHAR helveticaW[] = {'H','e','l','v','e','t','i','c','a',0};
static const WCHAR courierW[] = {'C','o','u','r','i','e','r',0};
static const WCHAR symbolW[] = {'S','y','m','b','o','l',0};
static const WCHAR arialW[] = {'A','r','i','a','l',0};
static const WCHAR systemW[] = {'S','y','s','t','e','m',0};
static const WCHAR times_new_romanW[] = {'T','i','m','e','s',' ','N','e','w',' ','R','o','m','a','n',0};
static const WCHAR courier_newW[] = {'C','o','u','r','i','e','r',' ','N','e','w',0};
static const struct gdi_dc_funcs psdrv_funcs;
struct glyph_info
{
WCHAR wch;
int width;
};
struct font_data
{
struct list entry;
char *name;
IFIMETRICS *metrics;
int glyph_count;
struct glyph_info *glyphs;
struct glyph_info def_glyph;
};
static struct list fonts = LIST_INIT(fonts);
struct printer_info
{
struct list entry;
const WCHAR *name;
const PSDRV_DEVMODE *devmode;
};
static struct list printer_info_list = LIST_INIT(printer_info_list);
struct band_info
{
BOOL graphics_flag;
BOOL text_flag;
RECT graphics_rect;
};
typedef struct
{
struct gdi_physdev dev;
PSDRV_DEVMODE *devmode;
const struct printer_info *pi;
/* builtin font info */
BOOL builtin;
SIZE size;
const struct font_data *font;
float scale;
TEXTMETRICW tm;
int escapement;
SIZE page_size; /* Physical page size in device units */
RECT imageable_area; /* Imageable area in device units */
int horz_res; /* device caps */
int vert_res;
int horz_size;
int vert_size;
int log_pixels_x;
int log_pixels_y;
} PSDRV_PDEVICE;
static inline PSDRV_PDEVICE *get_psdrv_dev(PHYSDEV dev)
{
return (PSDRV_PDEVICE *)dev;
}
/* copied from kernelbase */
static int muldiv(int a, int b, int c)
{
LONGLONG ret;
if (!c) return -1;
/* We want to deal with a positive divisor to simplify the logic. */
if (c < 0)
{
a = -a;
c = -c;
}
/* If the result is positive, we "add" to round. else, we subtract to round. */
if ((a < 0 && b < 0) || (a >= 0 && b >= 0))
ret = (((LONGLONG)a * b) + (c / 2)) / c;
else
ret = (((LONGLONG)a * b) - (c / 2)) / c;
if (ret > 2147483647 || ret < -2147483647) return -1;
return ret;
}
static void dump_fields(int fields)
{
int add_space = 0;
#define CHECK_FIELD(flag) \
do \
{ \
if (fields & flag) \
{ \
if (add_space++) TRACE(" "); \
TRACE(#flag); \
fields &= ~flag; \
} \
} \
while (0)
CHECK_FIELD(DM_ORIENTATION);
CHECK_FIELD(DM_PAPERSIZE);
CHECK_FIELD(DM_PAPERLENGTH);
CHECK_FIELD(DM_PAPERWIDTH);
CHECK_FIELD(DM_SCALE);
CHECK_FIELD(DM_POSITION);
CHECK_FIELD(DM_NUP);
CHECK_FIELD(DM_DISPLAYORIENTATION);
CHECK_FIELD(DM_COPIES);
CHECK_FIELD(DM_DEFAULTSOURCE);
CHECK_FIELD(DM_PRINTQUALITY);
CHECK_FIELD(DM_COLOR);
CHECK_FIELD(DM_DUPLEX);
CHECK_FIELD(DM_YRESOLUTION);
CHECK_FIELD(DM_TTOPTION);
CHECK_FIELD(DM_COLLATE);
CHECK_FIELD(DM_FORMNAME);
CHECK_FIELD(DM_LOGPIXELS);
CHECK_FIELD(DM_BITSPERPEL);
CHECK_FIELD(DM_PELSWIDTH);
CHECK_FIELD(DM_PELSHEIGHT);
CHECK_FIELD(DM_DISPLAYFLAGS);
CHECK_FIELD(DM_DISPLAYFREQUENCY);
CHECK_FIELD(DM_ICMMETHOD);
CHECK_FIELD(DM_ICMINTENT);
CHECK_FIELD(DM_MEDIATYPE);
CHECK_FIELD(DM_DITHERTYPE);
CHECK_FIELD(DM_PANNINGWIDTH);
CHECK_FIELD(DM_PANNINGHEIGHT);
if (fields) TRACE(" %#x", fields);
TRACE("\n");
#undef CHECK_FIELD
}
static void dump_devmode(const DEVMODEW *dm)
{
if (!TRACE_ON(psdrv)) return;
TRACE("dmDeviceName: %s\n", debugstr_w(dm->dmDeviceName));
TRACE("dmSpecVersion: 0x%04x\n", dm->dmSpecVersion);
TRACE("dmDriverVersion: 0x%04x\n", dm->dmDriverVersion);
TRACE("dmSize: 0x%04x\n", dm->dmSize);
TRACE("dmDriverExtra: 0x%04x\n", dm->dmDriverExtra);
TRACE("dmFields: 0x%04x\n", (int)dm->dmFields);
dump_fields(dm->dmFields);
TRACE("dmOrientation: %d\n", dm->dmOrientation);
TRACE("dmPaperSize: %d\n", dm->dmPaperSize);
TRACE("dmPaperLength: %d\n", dm->dmPaperLength);
TRACE("dmPaperWidth: %d\n", dm->dmPaperWidth);
TRACE("dmScale: %d\n", dm->dmScale);
TRACE("dmCopies: %d\n", dm->dmCopies);
TRACE("dmDefaultSource: %d\n", dm->dmDefaultSource);
TRACE("dmPrintQuality: %d\n", dm->dmPrintQuality);
TRACE("dmColor: %d\n", dm->dmColor);
TRACE("dmDuplex: %d\n", dm->dmDuplex);
TRACE("dmYResolution: %d\n", dm->dmYResolution);
TRACE("dmTTOption: %d\n", dm->dmTTOption);
TRACE("dmCollate: %d\n", dm->dmCollate);
TRACE("dmFormName: %s\n", debugstr_w(dm->dmFormName));
TRACE("dmLogPixels %u\n", dm->dmLogPixels);
TRACE("dmBitsPerPel %u\n", (unsigned int)dm->dmBitsPerPel);
TRACE("dmPelsWidth %u\n", (unsigned int)dm->dmPelsWidth);
TRACE("dmPelsHeight %u\n", (unsigned int)dm->dmPelsHeight);
}
static INT get_device_caps(PHYSDEV dev, INT cap)
{
PSDRV_PDEVICE *pdev = get_psdrv_dev(dev);
TRACE("%p,%d\n", dev->hdc, cap);
switch(cap)
{
case DRIVERVERSION:
return 0;
case TECHNOLOGY:
return DT_RASPRINTER;
case HORZSIZE:
return muldiv(pdev->horz_size, 100, pdev->devmode->dmPublic.dmScale);
case VERTSIZE:
return muldiv(pdev->vert_size, 100, pdev->devmode->dmPublic.dmScale);
case HORZRES:
return pdev->horz_res;
case VERTRES:
return pdev->vert_res;
case BITSPIXEL:
/* Although Windows returns 1 for monochrome printers, we want
CreateCompatibleBitmap to provide something other than 1 bpp */
return 32;
case NUMPENS:
return 10;
case NUMFONTS:
return 39;
case NUMCOLORS:
return -1;
case PDEVICESIZE:
return sizeof(PSDRV_PDEVICE);
case TEXTCAPS:
return TC_CR_ANY | TC_VA_ABLE; /* psdrv 0x59f7 */
case RASTERCAPS:
return (RC_BITBLT | RC_BITMAP64 | RC_GDI20_OUTPUT | RC_DIBTODEV |
RC_STRETCHBLT | RC_STRETCHDIB); /* psdrv 0x6e99 */
case ASPECTX:
return pdev->log_pixels_x;
case ASPECTY:
return pdev->log_pixels_y;
case LOGPIXELSX:
return muldiv(pdev->log_pixels_x, pdev->devmode->dmPublic.dmScale, 100);
case LOGPIXELSY:
return muldiv(pdev->log_pixels_y, pdev->devmode->dmPublic.dmScale, 100);
case NUMRESERVED:
return 0;
case COLORRES:
return 0;
case PHYSICALWIDTH:
return (pdev->devmode->dmPublic.dmOrientation == DMORIENT_LANDSCAPE) ?
pdev->page_size.cy : pdev->page_size.cx;
case PHYSICALHEIGHT:
return (pdev->devmode->dmPublic.dmOrientation == DMORIENT_LANDSCAPE) ?
pdev->page_size.cx : pdev->page_size.cy;
case PHYSICALOFFSETX:
if (pdev->devmode->dmPublic.dmOrientation == DMORIENT_LANDSCAPE)
{
if (pdev->devmode->landscape_orientation == -90)
return pdev->page_size.cy - pdev->imageable_area.top;
else
return pdev->imageable_area.bottom;
}
return pdev->imageable_area.left;
case PHYSICALOFFSETY:
if (pdev->devmode->dmPublic.dmOrientation == DMORIENT_LANDSCAPE)
{
if (pdev->devmode->landscape_orientation == -90)
return pdev->page_size.cx - pdev->imageable_area.right;
else
return pdev->imageable_area.left;
}
return pdev->page_size.cy - pdev->imageable_area.top;
default:
dev = GET_NEXT_PHYSDEV(dev, pGetDeviceCaps);
return dev->funcs->pGetDeviceCaps(dev, cap);
}
}
static inline int paper_size_from_points(float size)
{
return size * 254 / 72;
}
static const struct input_slot *find_slot(const struct printer_info *pi,
const DEVMODEW *dm)
{
const struct input_slot *slot = (const struct input_slot *)pi->devmode->data;
int i;
for (i = 0; i < pi->devmode->input_slots; i++)
{
if (slot[i].win_bin == dm->dmDefaultSource)
return slot + i;
}
return NULL;
}
static const struct page_size *find_pagesize(const struct printer_info *pi,
const DEVMODEW *dm)
{
const struct page_size *page;
int i;
page = (const struct page_size *)(pi->devmode->data +
pi->devmode->input_slots * sizeof(struct input_slot) +
pi->devmode->resolutions * sizeof(struct resolution));
for (i = 0; i < pi->devmode->page_sizes; i++)
{
if (page[i].win_page == dm->dmPaperSize)
return page + i;
}
return NULL;
}
static void merge_devmodes(PSDRV_DEVMODE *dm1, const DEVMODEW *dm2,
const struct printer_info *pi)
{
/* some sanity checks here on dm2 */
if (dm2->dmFields & DM_ORIENTATION)
{
dm1->dmPublic.dmOrientation = dm2->dmOrientation;
TRACE("Changing orientation to %d (%s)\n",
dm1->dmPublic.dmOrientation,
dm1->dmPublic.dmOrientation == DMORIENT_PORTRAIT ?
"Portrait" :
(dm1->dmPublic.dmOrientation == DMORIENT_LANDSCAPE ?
"Landscape" : "unknown"));
}
/* NB PaperWidth is always < PaperLength */
if (dm2->dmFields & DM_PAPERSIZE)
{
const struct page_size *page = find_pagesize(pi, dm2);
if (page)
{
dm1->dmPublic.dmPaperSize = dm2->dmPaperSize;
dm1->dmPublic.dmPaperWidth = paper_size_from_points(page->paper_dimension.x);
dm1->dmPublic.dmPaperLength = paper_size_from_points(page->paper_dimension.y);
dm1->dmPublic.dmFields |= DM_PAPERSIZE | DM_PAPERWIDTH | DM_PAPERLENGTH;
TRACE("Changing page to %s %d x %d\n", debugstr_w(page->name),
dm1->dmPublic.dmPaperWidth,
dm1->dmPublic.dmPaperLength);
if (dm1->dmPublic.dmSize >= FIELD_OFFSET(DEVMODEW, dmFormName) + CCHFORMNAME * sizeof(WCHAR))
{
memcpy(dm1->dmPublic.dmFormName, page->name, sizeof(page->name));
dm1->dmPublic.dmFields |= DM_FORMNAME;
}
}
else
TRACE("Trying to change to unsupported pagesize %d\n", dm2->dmPaperSize);
}
else if ((dm2->dmFields & DM_PAPERLENGTH) &&
(dm2->dmFields & DM_PAPERWIDTH))
{
dm1->dmPublic.dmPaperLength = dm2->dmPaperLength;
dm1->dmPublic.dmPaperWidth = dm2->dmPaperWidth;
TRACE("Changing PaperLength|Width to %dx%d\n",
dm2->dmPaperLength,
dm2->dmPaperWidth);
dm1->dmPublic.dmFields &= ~DM_PAPERSIZE;
dm1->dmPublic.dmFields |= (DM_PAPERLENGTH | DM_PAPERWIDTH);
}
else if (dm2->dmFields & (DM_PAPERLENGTH | DM_PAPERWIDTH))
{
/* You might think that this would be allowed if dm1 is in custom size
mode, but apparently Windows reverts to standard paper mode even in
this case */
FIXME("Trying to change only paperlength or paperwidth\n");
dm1->dmPublic.dmFields &= ~(DM_PAPERLENGTH | DM_PAPERWIDTH);
dm1->dmPublic.dmFields |= DM_PAPERSIZE;
}
if (dm2->dmFields & DM_SCALE)
{
dm1->dmPublic.dmScale = dm2->dmScale;
TRACE("Changing Scale to %d\n", dm2->dmScale);
}
if (dm2->dmFields & DM_COPIES)
{
dm1->dmPublic.dmCopies = dm2->dmCopies;
TRACE("Changing Copies to %d\n", dm2->dmCopies);
}
if (dm2->dmFields & DM_DEFAULTSOURCE)
{
const struct input_slot *slot = find_slot(pi, dm2);
if (slot)
dm1->dmPublic.dmDefaultSource = dm2->dmDefaultSource;
else
TRACE("Trying to change to unsupported bin %d\n", dm2->dmDefaultSource);
}
if (dm2->dmFields & DM_DEFAULTSOURCE)
dm1->dmPublic.dmDefaultSource = dm2->dmDefaultSource;
if (dm2->dmFields & DM_PRINTQUALITY)
dm1->dmPublic.dmPrintQuality = dm2->dmPrintQuality;
if (dm2->dmFields & DM_COLOR)
dm1->dmPublic.dmColor = dm2->dmColor;
if (dm2->dmFields & DM_DUPLEX && pi->devmode->duplex)
dm1->dmPublic.dmDuplex = dm2->dmDuplex;
if (dm2->dmFields & DM_YRESOLUTION)
dm1->dmPublic.dmYResolution = dm2->dmYResolution;
if (dm2->dmFields & DM_TTOPTION)
dm1->dmPublic.dmTTOption = dm2->dmTTOption;
if (dm2->dmFields & DM_COLLATE)
dm1->dmPublic.dmCollate = dm2->dmCollate;
if (dm2->dmFields & DM_FORMNAME)
lstrcpynW(dm1->dmPublic.dmFormName, dm2->dmFormName, CCHFORMNAME);
if (dm2->dmFields & DM_BITSPERPEL)
dm1->dmPublic.dmBitsPerPel = dm2->dmBitsPerPel;
if (dm2->dmFields & DM_PELSWIDTH)
dm1->dmPublic.dmPelsWidth = dm2->dmPelsWidth;
if (dm2->dmFields & DM_PELSHEIGHT)
dm1->dmPublic.dmPelsHeight = dm2->dmPelsHeight;
if (dm2->dmFields & DM_DISPLAYFLAGS)
dm1->dmPublic.dmDisplayFlags = dm2->dmDisplayFlags;
if (dm2->dmFields & DM_DISPLAYFREQUENCY)
dm1->dmPublic.dmDisplayFrequency = dm2->dmDisplayFrequency;
if (dm2->dmFields & DM_POSITION)
dm1->dmPublic.dmPosition = dm2->dmPosition;
if (dm2->dmFields & DM_LOGPIXELS)
dm1->dmPublic.dmLogPixels = dm2->dmLogPixels;
if (dm2->dmFields & DM_ICMMETHOD)
dm1->dmPublic.dmICMMethod = dm2->dmICMMethod;
if (dm2->dmFields & DM_ICMINTENT)
dm1->dmPublic.dmICMIntent = dm2->dmICMIntent;
if (dm2->dmFields & DM_MEDIATYPE)
dm1->dmPublic.dmMediaType = dm2->dmMediaType;
if (dm2->dmFields & DM_DITHERTYPE)
dm1->dmPublic.dmDitherType = dm2->dmDitherType;
if (dm2->dmFields & DM_PANNINGWIDTH)
dm1->dmPublic.dmPanningWidth = dm2->dmPanningWidth;
if (dm2->dmFields & DM_PANNINGHEIGHT)
dm1->dmPublic.dmPanningHeight = dm2->dmPanningHeight;
}
static void update_dev_caps(PSDRV_PDEVICE *pdev)
{
INT width = 0, height = 0, resx = 0, resy = 0;
const struct resolution *res;
const struct page_size *page;
int i;
dump_devmode(&pdev->devmode->dmPublic);
if (pdev->devmode->dmPublic.dmFields & (DM_PRINTQUALITY | DM_YRESOLUTION | DM_LOGPIXELS))
{
if (pdev->devmode->dmPublic.dmFields & DM_PRINTQUALITY)
resx = resy = pdev->devmode->dmPublic.dmPrintQuality;
if (pdev->devmode->dmPublic.dmFields & DM_YRESOLUTION)
resy = pdev->devmode->dmPublic.dmYResolution;
if (pdev->devmode->dmPublic.dmFields & DM_LOGPIXELS)
resx = resy = pdev->devmode->dmPublic.dmLogPixels;
res = (const struct resolution *)(pdev->devmode->data
+ pdev->devmode->input_slots * sizeof(struct input_slot));
for (i = 0; i < pdev->devmode->resolutions; i++)
{
if (res[i].x == resx && res[i].y == resy)
{
pdev->log_pixels_x = resx;
pdev->log_pixels_y = resy;
break;
}
}
if (i == pdev->devmode->resolutions)
{
WARN("Requested resolution %dx%d is not supported by device\n", resx, resy);
pdev->log_pixels_x = pdev->devmode->default_resolution;
pdev->log_pixels_y = pdev->log_pixels_x;
}
}
else
{
WARN("Using default device resolution %d\n", pdev->devmode->default_resolution);
pdev->log_pixels_x = pdev->devmode->default_resolution;
pdev->log_pixels_y = pdev->log_pixels_x;
}
if (pdev->devmode->dmPublic.dmFields & DM_PAPERSIZE) {
page = find_pagesize(pdev->pi, &pdev->devmode->dmPublic);
if (!page)
{
FIXME("Can't find page\n");
SetRectEmpty(&pdev->imageable_area);
pdev->page_size.cx = 0;
pdev->page_size.cy = 0;
}
else
{
/* pdev sizes in device units; ppd sizes in 1/72" */
SetRect(&pdev->imageable_area,
page->imageable_area.left * pdev->log_pixels_x / 72,
page->imageable_area.top * pdev->log_pixels_y / 72,
page->imageable_area.right * pdev->log_pixels_x / 72,
page->imageable_area.bottom * pdev->log_pixels_y / 72);
pdev->page_size.cx = page->paper_dimension.x * pdev->log_pixels_x / 72;
pdev->page_size.cy = page->paper_dimension.y * pdev->log_pixels_y / 72;
}
} else if ((pdev->devmode->dmPublic.dmFields & DM_PAPERLENGTH) &&
(pdev->devmode->dmPublic.dmFields & DM_PAPERWIDTH)) {
/* pdev sizes in device units; devmode sizes in 1/10 mm */
pdev->imageable_area.left = pdev->imageable_area.bottom = 0;
pdev->imageable_area.right = pdev->page_size.cx =
pdev->devmode->dmPublic.dmPaperWidth * pdev->log_pixels_x / 254;
pdev->imageable_area.top = pdev->page_size.cy =
pdev->devmode->dmPublic.dmPaperLength * pdev->log_pixels_y / 254;
} else {
FIXME("Odd dmFields %x\n", (int)pdev->devmode->dmPublic.dmFields);
SetRectEmpty(&pdev->imageable_area);
pdev->page_size.cx = 0;
pdev->page_size.cy = 0;
}
TRACE("imageable_area = %s: page_size = %dx%d\n", wine_dbgstr_rect(&pdev->imageable_area),
(int)pdev->page_size.cx, (int)pdev->page_size.cy);
/* these are in device units */
width = pdev->imageable_area.right - pdev->imageable_area.left;
height = pdev->imageable_area.top - pdev->imageable_area.bottom;
if (pdev->devmode->dmPublic.dmOrientation == DMORIENT_PORTRAIT) {
pdev->horz_res = width;
pdev->vert_res = height;
} else {
pdev->horz_res = height;
pdev->vert_res = width;
}
/* these are in mm */
pdev->horz_size = (pdev->horz_res * 25.4) / pdev->log_pixels_x;
pdev->vert_size = (pdev->vert_res * 25.4) / pdev->log_pixels_y;
TRACE("devcaps: horz_size = %dmm, vert_size = %dmm, "
"horz_res = %d, vert_res = %d\n",
pdev->horz_size, pdev->vert_size,
pdev->horz_res, pdev->vert_res);
}
static BOOL reset_dc(PHYSDEV dev, const DEVMODEW *devmode)
{
PSDRV_PDEVICE *pdev = get_psdrv_dev(dev);
if (devmode)
{
merge_devmodes(pdev->devmode, devmode, pdev->pi);
update_dev_caps(pdev);
}
return TRUE;
}
static int cmp_glyph_info(const void *a, const void *b)
{
return (int)((const struct glyph_info *)a)->wch -
(int)((const struct glyph_info *)b)->wch;
}
const struct glyph_info *uv_metrics(WCHAR wch, const struct font_data *font)
{
const struct glyph_info *needle;
struct glyph_info key;
/*
* Ugly work-around for symbol fonts. Wine is sending characters which
* belong in the Unicode private use range (U+F020 - U+F0FF) as ASCII
* characters (U+0020 - U+00FF).
*/
if ((font->glyphs->wch & 0xff00) == 0xf000 && wch < 0x100)
wch |= 0xf000;
key.wch = wch;
needle = bsearch(&key, font->glyphs, font->glyph_count, sizeof(*font->glyphs), cmp_glyph_info);
if (!needle)
{
WARN("No glyph for U+%.4X in '%s'\n", wch, font->name);
needle = font->glyphs;
}
return needle;
}
static int ext_escape(PHYSDEV dev, int escape, int input_size, const void *input,
int output_size, void *output)
{
TRACE("%p,%d,%d,%p,%d,%p\n",
dev->hdc, escape, input_size, input, output_size, output);
switch (escape)
{
case QUERYESCSUPPORT:
if (input_size < sizeof(SHORT))
{
WARN("input_size < sizeof(SHORT) (=%d) for QUERYESCSUPPORT\n", input_size);
return 0;
}
else
{
DWORD num = (input_size < sizeof(DWORD)) ? *(const USHORT *)input : *(const DWORD *)input;
TRACE("QUERYESCSUPPORT for %d\n", (int)num);
switch (num)
{
case NEXTBAND:
/*case BANDINFO:*/
case SETCOPYCOUNT:
case GETTECHNOLOGY:
case SETLINECAP:
case SETLINEJOIN:
case SETMITERLIMIT:
case SETCHARSET:
case EXT_DEVICE_CAPS:
case SET_BOUNDS:
case EPSPRINTING:
case POSTSCRIPT_DATA:
case PASSTHROUGH:
case POSTSCRIPT_PASSTHROUGH:
case POSTSCRIPT_IGNORE:
case BEGIN_PATH:
case CLIP_TO_PATH:
case END_PATH:
/*case DRAWPATTERNRECT:*/
/* PageMaker checks for it */
case DOWNLOADHEADER:
/* PageMaker doesn't check for DOWNLOADFACE and GETFACENAME but
* uses them, they are supposed to be supported by any PS printer.
*/
case DOWNLOADFACE:
/* PageMaker checks for these as a part of process of detecting
* a "fully compatible" PS printer, but doesn't actually use them.
*/
case OPENCHANNEL:
case CLOSECHANNEL:
return TRUE;
/* Windows PS driver reports 0, but still supports this escape */
case GETFACENAME:
return FALSE; /* suppress the FIXME below */
default:
FIXME("QUERYESCSUPPORT(%d) - not supported.\n", (int)num);
return FALSE;
}
}
case OPENCHANNEL:
FIXME("OPENCHANNEL: stub\n");
return 1;
case CLOSECHANNEL:
FIXME("CLOSECHANNEL: stub\n");
return 1;
case DOWNLOADHEADER:
FIXME("DOWNLOADHEADER: stub\n");
/* should return name of the downloaded procset */
*(char *)output = 0;
return 1;
case GETFACENAME:
FIXME("GETFACENAME: stub\n");
lstrcpynA(output, "Courier", output_size);
return 1;
case DOWNLOADFACE:
FIXME("DOWNLOADFACE: stub\n");
return 1;
case MFCOMMENT:
{
FIXME("MFCOMMENT(%p, %d)\n", input, input_size);
return 1;
}
case DRAWPATTERNRECT:
{
DRAWPATRECT *dpr = (DRAWPATRECT*)input;
FIXME("DRAWPATTERNRECT(pos (%d,%d), size %dx%d, style %d, pattern %x), stub!\n",
(int)dpr->ptPosition.x, (int)dpr->ptPosition.y,
(int)dpr->ptSize.x, (int)dpr->ptSize.y,
dpr->wStyle, dpr->wPattern);
return 1;
}
case BANDINFO:
{
struct band_info *ibi = (struct band_info *)input;
struct band_info *obi = (struct band_info *)output;
FIXME("BANDINFO(graphics %d, text %d, rect %s), stub!\n", ibi->graphics_flag,
ibi->text_flag, wine_dbgstr_rect(&ibi->graphics_rect));
*obi = *ibi;
return 1;
}
case SETCOPYCOUNT:
{
const INT *NumCopies = input;
INT *ActualCopies = output;
if (input_size != sizeof(INT))
{
WARN("input_size != sizeof(INT) (=%d) for SETCOPYCOUNT\n", input_size);
return 0;
}
TRACE("SETCOPYCOUNT %d\n", *NumCopies);
*ActualCopies = 1;
return 1;
}
case GETTECHNOLOGY:
{
LPSTR p = output;
strcpy(p, "PostScript");
*(p + strlen(p) + 1) = '\0'; /* 2 '\0's at end of string */
return 1;
}
case SETLINECAP:
{
INT newCap = *(const INT *)input;
if (input_size != sizeof(INT))
{
WARN("input_size != sizeof(INT) (=%d) for SETLINECAP\n", input_size);
return 0;
}
TRACE("SETLINECAP %d\n", newCap);
return 0;
}
case SETLINEJOIN:
{
INT newJoin = *(const INT *)input;
if (input_size != sizeof(INT))
{
WARN("input_size != sizeof(INT) (=%d) for SETLINEJOIN\n", input_size);
return 0;
}
TRACE("SETLINEJOIN %d\n", newJoin);
return 0;
}
case SETMITERLIMIT:
{
INT newLimit = *(const INT *)input;
if (input_size != sizeof(INT))
{
WARN("input_size != sizeof(INT) (=%d) for SETMITERLIMIT\n", input_size);
return 0;
}
TRACE("SETMITERLIMIT %d\n", newLimit);
return 0;
}
case SETCHARSET:
/* Undocumented escape used by winword6.
Switches between ANSI and a special charset.
If *lpInData == 1 we require that
0x91 is quoteleft
0x92 is quoteright
0x93 is quotedblleft
0x94 is quotedblright
0x95 is bullet
0x96 is endash
0x97 is emdash
0xa0 is non break space - yeah right.
If *lpInData == 0 we get ANSI.
Since there's nothing else there, let's just make these the default
anyway and see what happens...
*/
return 1;
case EXT_DEVICE_CAPS:
{
UINT cap = *(const UINT *)input;
if (input_size != sizeof(UINT))
{
WARN("input_size != sizeof(UINT) (=%d) for EXT_DEVICE_CAPS\n", input_size);
return 0;
}
TRACE("EXT_DEVICE_CAPS %d\n", cap);
return 0;
}
case SET_BOUNDS:
{
const RECT *r = input;
if (input_size != sizeof(RECT))
{
WARN("input_size != sizeof(RECT) (=%d) for SET_BOUNDS\n", input_size);
return 0;
}
TRACE("SET_BOUNDS %s\n", wine_dbgstr_rect(r));
return 0;
}
case EPSPRINTING:
{
UINT epsprint = *(const UINT*)input;
/* FIXME: In this mode we do not need to send page intros and page
* ends according to the doc. But I just ignore that detail
* for now.
*/
TRACE("EPS Printing support %sable.\n",epsprint?"en":"dis");
return 1;
}
case POSTSCRIPT_DATA:
case PASSTHROUGH:
case POSTSCRIPT_PASSTHROUGH:
return 1;
case POSTSCRIPT_IGNORE:
return 1;
case GETSETPRINTORIENT:
{
/* If lpInData is present, it is a 20 byte structure, first 32
* bit LONG value is the orientation. if lpInData is NULL, it
* returns the current orientation.
*/
FIXME("GETSETPRINTORIENT not implemented (data %p)!\n",input);
return 1;
}
case BEGIN_PATH:
return 1;
case END_PATH:
return 1;
case CLIP_TO_PATH:
return 1;
case PSDRV_CHECK_WCHAR:
{
PSDRV_PDEVICE *pdev = get_psdrv_dev(dev);
WCHAR *uv = (WCHAR *)input;
WCHAR out = uv_metrics(*uv, pdev->font)->wch;
if ((out & 0xff00) == 0xf000) out &= ~0xf000;
*(WCHAR *)output = out;
return 1;
}
case PSDRV_GET_BUILTIN_FONT_INFO:
{
PSDRV_PDEVICE *pdev = get_psdrv_dev(dev);
struct font_info *font_info = (struct font_info *)output;
if (!pdev->builtin)
return 0;
lstrcpynA(font_info->font_name, pdev->font->name, sizeof(font_info->font_name));
font_info->size = pdev->size;
font_info->escapement = pdev->escapement;
return 1;
}
default:
FIXME("Unimplemented code %d\n", escape);
return 0;
}
}
static inline float gdi_round(float f)
{
return f > 0 ? f + 0.5 : f - 0.5;
}
static void scale_font(PSDRV_PDEVICE *pdev, const struct font_data *font, LONG height, TEXTMETRICW *tm)
{
SHORT ascender, descender, line_gap, avg_char_width;
USHORT units_per_em, win_ascent, win_descent;
const IFIMETRICS *m = font->metrics;
float scale;
SIZE size;
TRACE("'%s' %i\n", font->name, (int)height);
if (height < 0) /* match em height */
scale = -(height / (float)m->fwdUnitsPerEm);
else /* match cell height */
scale = height / (float)(m->fwdWinAscender + m->fwdWinDescender);
size.cx = (INT)gdi_round(scale * (float)m->fwdUnitsPerEm);
size.cy = -(INT)gdi_round(scale * (float)m->fwdUnitsPerEm);
units_per_em = (USHORT)gdi_round((float)m->fwdUnitsPerEm * scale);
ascender = (SHORT)gdi_round((float)m->fwdMacAscender * scale);
descender = (SHORT)gdi_round((float)m->fwdMacDescender * scale);
line_gap = (SHORT)gdi_round((float)m->fwdMacLineGap * scale);
win_ascent = (USHORT)gdi_round((float)m->fwdWinAscender * scale);
win_descent = (USHORT)gdi_round((float)m->fwdWinDescender * scale);
avg_char_width = (SHORT)gdi_round((float)m->fwdAveCharWidth * scale);
tm->tmAscent = (LONG)win_ascent;
tm->tmDescent = (LONG)win_descent;
tm->tmHeight = tm->tmAscent + tm->tmDescent;
tm->tmInternalLeading = tm->tmHeight - (LONG)units_per_em;
if (tm->tmInternalLeading < 0)
tm->tmInternalLeading = 0;
tm->tmExternalLeading =
(LONG)(ascender - descender + line_gap) - tm->tmHeight;
if (tm->tmExternalLeading < 0)
tm->tmExternalLeading = 0;
tm->tmAveCharWidth = (LONG)avg_char_width;
tm->tmWeight = m->usWinWeight;
tm->tmItalic = !!(m->fsSelection & FM_SEL_ITALIC);
tm->tmUnderlined = !!(m->fsSelection & FM_SEL_UNDERSCORE);
tm->tmStruckOut = !!(m->fsSelection & FM_SEL_STRIKEOUT);
tm->tmFirstChar = font->glyphs[0].wch;
tm->tmLastChar = font->glyphs[font->glyph_count - 1].wch;
tm->tmDefaultChar = 0x001f; /* Win2K does this - FIXME? */
tm->tmBreakChar = tm->tmFirstChar; /* should be 'space' */
tm->tmPitchAndFamily = TMPF_DEVICE | TMPF_VECTOR;
if (!(m->jWinPitchAndFamily & FIXED_PITCH))
tm->tmPitchAndFamily |= TMPF_FIXED_PITCH; /* yes, it's backwards */
if (m->fwdUnitsPerEm != 1000)
tm->tmPitchAndFamily |= TMPF_TRUETYPE;
tm->tmCharSet = ANSI_CHARSET; /* FIXME */
tm->tmOverhang = 0;
/*
* This is kludgy. font->scale is used in several places in the driver
* to adjust PostScript-style metrics. Since these metrics have been
* "normalized" to an em-square size of 1000, font->scale needs to be
* similarly adjusted..
*/
scale *= (float)m->fwdUnitsPerEm / 1000.0;
tm->tmMaxCharWidth = (LONG)gdi_round((m->rclFontBox.right - m->rclFontBox.left) * scale);
if (pdev)
{
pdev->scale = scale;
pdev->size = size;
}
TRACE("Selected PS font '%s' size %d weight %d.\n", font->name,
(int)size.cx, (int)tm->tmWeight);
TRACE("H = %d As = %d Des = %d IL = %d EL = %d\n", (int)tm->tmHeight,
(int)tm->tmAscent, (int)tm->tmDescent, (int)tm->tmInternalLeading,
(int)tm->tmExternalLeading);
}
static inline BOOL is_stock_font(HFONT font)
{
int i;
for (i = OEM_FIXED_FONT; i <= DEFAULT_GUI_FONT; i++)
{
if (i != DEFAULT_PALETTE && font == GetStockObject(i)) return TRUE;
}
return FALSE;
}
static struct font_data *find_font_data(const char *name)
{
struct font_data *font;
LIST_FOR_EACH_ENTRY(font, &fonts, struct font_data, entry)
{
if (!strcmp(font->name, name))
return font;
}
return NULL;
}
static struct font_data *find_builtin_font(const PSDRV_DEVMODE *devmode,
const WCHAR *facename, BOOL it, BOOL bd)
{
struct installed_font *installed_font;
BOOL best_it, best_bd, cur_it, cur_bd;
struct font_data *best = NULL, *cur;
const WCHAR *name;
int i;
installed_font = (struct installed_font *)(devmode->data +
devmode->input_slots * sizeof(struct input_slot) +
devmode->resolutions * sizeof(struct resolution) +
devmode->page_sizes * sizeof(struct page_size) +
devmode->font_subs * sizeof(struct font_sub));
for (i = 0; i < devmode->installed_fonts; i++)
{
cur = find_font_data(installed_font[i].name);
if (!cur) continue;
name = (WCHAR *)((char *)cur->metrics + cur->metrics->dpwszFamilyName);
cur_it = !!(cur->metrics->fsSelection & FM_SEL_ITALIC);
cur_bd = !!(cur->metrics->fsSelection & FM_SEL_BOLD);
if (!facename && it == cur_it && bd == cur_bd)
return cur;
if (facename && !wcscmp(facename, name) && it == cur_it && bd == cur_bd)
return cur;
if (facename && wcscmp(facename, name))
continue;
if (!best || (best_it != it && cur_it == it) ||
(best_it != it && best_bd != bd && cur_bd == bd))
{
best = cur;
best_it = cur_it;
best_bd = cur_bd;
}
}
return best;
}
static BOOL select_builtin_font(PSDRV_PDEVICE *pdev, HFONT hfont, LOGFONTW *plf)
{
struct font_data *font_data;
BOOL bd = FALSE, it = FALSE;
LONG height;
TRACE("Trying to find facename %s\n", debugstr_w(plf->lfFaceName));
if (plf->lfItalic)
it = TRUE;
if (plf->lfWeight > 550)
bd = TRUE;
/* Look for a matching font family */
font_data = find_builtin_font(pdev->devmode, plf->lfFaceName, it, bd);
if (!font_data)
{
/* Fallback for Window's font families to common PostScript families */
if (!wcscmp(plf->lfFaceName, arialW))
wcscpy(plf->lfFaceName, helveticaW);
else if (!wcscmp(plf->lfFaceName, systemW))
wcscpy(plf->lfFaceName, helveticaW);
else if (!wcscmp(plf->lfFaceName, times_new_romanW))
wcscpy(plf->lfFaceName, timesW);
else if (!wcscmp(plf->lfFaceName, courier_newW))
wcscpy(plf->lfFaceName, courierW);
font_data = find_builtin_font(pdev->devmode, plf->lfFaceName, it, bd);
}
/* If all else fails, use the first font defined for the printer */
if (!font_data)
font_data = find_builtin_font(pdev->devmode, NULL, it, bd);
TRACE("Got family %s font '%s'\n", debugstr_w((WCHAR *)((char *)font_data->metrics +
font_data->metrics->dpwszFamilyName)), font_data->name);
pdev->builtin = TRUE;
pdev->font = NULL;
pdev->font = font_data;
height = plf->lfHeight;
/* stock fonts ignore the mapping mode */
if (!is_stock_font(hfont))
{
POINT pts[2];
pts[0].x = pts[0].y = pts[1].x = 0;
pts[1].y = height;
NtGdiTransformPoints(pdev->dev.hdc, pts, pts, 2, NtGdiLPtoDP);
height = pts[1].y - pts[0].y;
}
scale_font(pdev, font_data, height, &pdev->tm);
/* Does anyone know if these are supposed to be reversed like this? */
pdev->tm.tmDigitizedAspectX = pdev->log_pixels_y;
pdev->tm.tmDigitizedAspectY = pdev->log_pixels_x;
return TRUE;
}
static HFONT select_font(PHYSDEV dev, HFONT hfont, UINT *aa_flags)
{
PSDRV_PDEVICE *pdev = get_psdrv_dev(dev);
PHYSDEV next = GET_NEXT_PHYSDEV(dev, pSelectFont);
const struct font_sub *font_sub;
HFONT ret;
LOGFONTW lf;
BOOL subst = FALSE;
int i;
if (!NtGdiExtGetObjectW(hfont, sizeof(lf), &lf)) return 0;
*aa_flags = GGO_BITMAP; /* no anti-aliasing on printer devices */
TRACE("FaceName = %s Height = %d Italic = %d Weight = %d\n",
debugstr_w(lf.lfFaceName), (int)lf.lfHeight, lf.lfItalic,
(int)lf.lfWeight);
if (lf.lfFaceName[0] == '\0')
{
switch(lf.lfPitchAndFamily & 0xf0)
{
case FF_DONTCARE:
break;
case FF_ROMAN:
case FF_SCRIPT:
wcscpy(lf.lfFaceName, timesW);
break;
case FF_SWISS:
wcscpy(lf.lfFaceName, helveticaW);
break;
case FF_MODERN:
wcscpy(lf.lfFaceName, courierW);
break;
case FF_DECORATIVE:
wcscpy(lf.lfFaceName, symbolW);
break;
}
}
if (lf.lfFaceName[0] == '\0')
{
switch(lf.lfPitchAndFamily & 0x0f)
{
case VARIABLE_PITCH:
wcscpy(lf.lfFaceName, timesW);
break;
default:
wcscpy(lf.lfFaceName, courierW);
break;
}
}
font_sub = (const struct font_sub *)(pdev->devmode->data +
pdev->devmode->input_slots * sizeof(struct input_slot) +
pdev->devmode->resolutions * sizeof(struct resolution) +
pdev->devmode->page_sizes * sizeof(struct page_size));
for (i = 0; i < pdev->devmode->font_subs; i++)
{
if (!wcsicmp(lf.lfFaceName, font_sub[i].name))
{
TRACE("substituting facename %s for %s\n",
debugstr_w(font_sub[i].sub), debugstr_w(lf.lfFaceName));
if (wcslen(font_sub[i].sub) < LF_FACESIZE)
{
wcscpy(lf.lfFaceName, font_sub[i].sub);
subst = TRUE;
}
else
{
WARN("Facename %s is too long; ignoring substitution\n",
debugstr_w(font_sub[i].sub));
}
break;
}
}
pdev->escapement = lf.lfEscapement;
if (!subst && ((ret = next->funcs->pSelectFont(next, hfont, aa_flags))))
{
pdev->builtin = FALSE;
return ret;
}
select_builtin_font(pdev, hfont, &lf);
next->funcs->pSelectFont(next, 0, aa_flags); /* tell next driver that we selected a device font */
return hfont;
}
static UINT get_font_metric(const struct font_data *font,
NEWTEXTMETRICEXW *ntmx, ENUMLOGFONTEXW *elfx)
{
/* ntmx->ntmTm is NEWTEXTMETRICW; compatible w/ TEXTMETRICW per Win32 doc */
TEXTMETRICW *tm = (TEXTMETRICW *)&(ntmx->ntmTm);
LOGFONTW *lf = &(elfx->elfLogFont);
memset(ntmx, 0, sizeof(*ntmx));
memset(elfx, 0, sizeof(*elfx));
scale_font(NULL, font, -(LONG)font->metrics->fwdUnitsPerEm, tm);
lf->lfHeight = tm->tmHeight;
lf->lfWidth = tm->tmAveCharWidth;
lf->lfWeight = tm->tmWeight;
lf->lfItalic = tm->tmItalic;
lf->lfCharSet = tm->tmCharSet;
lf->lfPitchAndFamily = font->metrics->jWinPitchAndFamily & FIXED_PITCH ? FIXED_PITCH : VARIABLE_PITCH;
lstrcpynW(lf->lfFaceName, (WCHAR *)((char *)font->metrics + font->metrics->dpwszFamilyName), LF_FACESIZE);
return DEVICE_FONTTYPE;
}
static BOOL enum_fonts(PHYSDEV dev, LPLOGFONTW plf, font_enum_proc proc, LPARAM lp)
{
PSDRV_PDEVICE *pdev = get_psdrv_dev(dev);
PHYSDEV next = GET_NEXT_PHYSDEV(dev, pEnumFonts);
PSDRV_DEVMODE *devmode = pdev->devmode;
struct installed_font *installed_font;
struct font_data *cur;
ENUMLOGFONTEXW lf;
NEWTEXTMETRICEXW tm;
const WCHAR *name;
BOOL ret;
UINT fm;
int i;
ret = next->funcs->pEnumFonts(next, plf, proc, lp);
if (!ret) return FALSE;
installed_font = (struct installed_font *)(devmode->data +
devmode->input_slots * sizeof(struct input_slot) +
devmode->resolutions * sizeof(struct resolution) +
devmode->page_sizes * sizeof(struct page_size) +
devmode->font_subs * sizeof(struct font_sub));
if (plf && plf->lfFaceName[0])
{
TRACE("lfFaceName = %s\n", debugstr_w(plf->lfFaceName));
for (i = 0; i < devmode->installed_fonts; i++)
{
cur = find_font_data(installed_font[i].name);
if (!cur) continue;
name = (WCHAR *)((char *)cur->metrics + cur->metrics->dpwszFamilyName);
if (wcsncmp(plf->lfFaceName, name, wcslen(name)))
continue;
TRACE("Got '%s'\n", cur->name);
fm = get_font_metric(cur, &tm, &lf);
if (!(ret = (*proc)(&lf.elfLogFont, (TEXTMETRICW *)&tm, fm, lp)))
break;
}
}
else
{
TRACE("lfFaceName = NULL\n");
for (i = 0; i < devmode->installed_fonts; i++)
{
cur = find_font_data(installed_font[i].name);
if (!cur) continue;
name = (WCHAR *)((char *)cur->metrics + cur->metrics->dpwszFamilyName);
TRACE("Got '%s'\n", cur->name);
fm = get_font_metric(cur, &tm, &lf);
if (!(ret = (*proc)(&lf.elfLogFont, (TEXTMETRICW *)&tm, fm, lp)))
break;
}
}
return ret;
}
static BOOL get_char_width(PHYSDEV dev, UINT first, UINT count, const WCHAR *chars, INT *buffer)
{
PSDRV_PDEVICE *pdev = get_psdrv_dev(dev);
UINT i, c;
if (!pdev->builtin)
{
dev = GET_NEXT_PHYSDEV(dev, pGetCharWidth);
return dev->funcs->pGetCharWidth(dev, first, count, chars, buffer);
}
TRACE("U+%.4X +%u\n", first, count);
for (i = 0; i < count; ++i)
{
c = chars ? chars[i] : first + i;
if (c > 0xffff)
return FALSE;
*buffer = floor(uv_metrics(c, pdev->font)->width * pdev->scale + 0.5);
TRACE("U+%.4X: %i\n", i, *buffer);
++buffer;
}
return TRUE;
}
static BOOL get_text_metrics(PHYSDEV dev, TEXTMETRICW *metrics)
{
PSDRV_PDEVICE *pdev = get_psdrv_dev(dev);
if (!pdev->builtin)
{
dev = GET_NEXT_PHYSDEV(dev, pGetTextMetrics);
return dev->funcs->pGetTextMetrics(dev, metrics);
}
memcpy(metrics, &pdev->tm, sizeof(pdev->tm));
return TRUE;
}
static BOOL get_text_extent_ex_point(PHYSDEV dev, const WCHAR *str, int count, int *dx)
{
PSDRV_PDEVICE *pdev = get_psdrv_dev(dev);
int i;
float width = 0.0;
if (!pdev->builtin)
{
dev = GET_NEXT_PHYSDEV(dev, pGetTextExtentExPoint);
return dev->funcs->pGetTextExtentExPoint(dev, str, count, dx);
}
TRACE("%s %i\n", debugstr_wn(str, count), count);
for (i = 0; i < count; ++i)
{
width += uv_metrics(str[i], pdev->font)->width;
dx[i] = width * pdev->scale;
}
return TRUE;
}
static struct printer_info *find_printer_info(const WCHAR *name)
{
struct printer_info *pi;
LIST_FOR_EACH_ENTRY(pi, &printer_info_list, struct printer_info, entry)
{
if (!wcscmp(pi->name, name))
return pi;
}
return NULL;
}
static PSDRV_PDEVICE *create_physdev(HDC hdc, const WCHAR *device,
const DEVMODEW *devmode)
{
struct printer_info *pi = find_printer_info(device);
PSDRV_PDEVICE *pdev;
if (!pi) return NULL;
if (!find_builtin_font(pi->devmode, NULL, FALSE, FALSE))
{
RASTERIZER_STATUS status;
if (!NtGdiGetRasterizerCaps(&status, sizeof(status)) ||
!(status.wFlags & TT_AVAILABLE) ||
!(status.wFlags & TT_ENABLED))
{
MESSAGE("Disabling printer %s since it has no builtin fonts and "
"there are no TrueType fonts available.\n", debugstr_w(device));
return FALSE;
}
}
pdev = malloc(sizeof(*pdev));
if (!pdev) return NULL;
pdev->devmode = malloc(pi->devmode->dmPublic.dmSize + pi->devmode->dmPublic.dmDriverExtra);
if (!pdev->devmode)
{
free(pdev);
return NULL;
}
memcpy(pdev->devmode, pi->devmode, pi->devmode->dmPublic.dmSize +
pi->devmode->dmPublic.dmDriverExtra);
pdev->pi = pi;
pdev->log_pixels_x = pdev->devmode->default_resolution;
pdev->log_pixels_y = pdev->devmode->default_resolution;
if (devmode)
{
dump_devmode(devmode);
merge_devmodes(pdev->devmode, devmode, pi);
}
update_dev_caps(pdev);
NtGdiSelectFont(hdc, GetStockObject(DEVICE_DEFAULT_FONT));
return pdev;
}
static BOOL create_dc(PHYSDEV *dev, const WCHAR *device,
const WCHAR *output, const DEVMODEW *devmode)
{
PSDRV_PDEVICE *pdev;
TRACE("(%s %s %p)\n", debugstr_w(device), debugstr_w(output), devmode);
if (!device) return FALSE;
if (!(pdev = create_physdev((*dev)->hdc, device, devmode))) return FALSE;
push_dc_driver(dev, &pdev->dev, &psdrv_funcs);
return TRUE;
}
static BOOL create_compatible_dc(PHYSDEV orig, PHYSDEV *dev)
{
PSDRV_PDEVICE *pdev, *orig_dev = get_psdrv_dev(orig);
if (!(pdev = create_physdev((*dev)->hdc, orig_dev->pi->name,
&orig_dev->devmode->dmPublic))) return FALSE;
push_dc_driver(dev, &pdev->dev, &psdrv_funcs);
return TRUE;
}
static BOOL delete_dc(PHYSDEV dev)
{
PSDRV_PDEVICE *pdev = get_psdrv_dev(dev);
TRACE("\n");
free(pdev->devmode);
free(pdev);
return TRUE;
}
static const struct gdi_dc_funcs psdrv_funcs =
{
.pCreateCompatibleDC = create_compatible_dc,
.pCreateDC = create_dc,
.pDeleteDC = delete_dc,
.pEnumFonts = enum_fonts,
.pExtEscape = ext_escape,
.pGetCharWidth = get_char_width,
.pGetDeviceCaps = get_device_caps,
.pGetTextExtentExPoint = get_text_extent_ex_point,
.pGetTextMetrics = get_text_metrics,
.pResetDC = reset_dc,
.pSelectFont = select_font,
.priority = GDI_PRIORITY_GRAPHICS_DRV
};
static BOOL check_ntf_str(const char *data, UINT64 size, const char *str)
{
if (str < data || str >= data + size)
return FALSE;
size -= str - data;
while (*str && size)
{
size--;
str++;
}
return size != 0;
}
static void free_font_data(struct font_data *font_data)
{
free(font_data->name);
free(font_data->metrics);
free(font_data->glyphs);
free(font_data);
}
static WCHAR convert_ntf_cp(unsigned short c, unsigned short cp)
{
static const WCHAR map_fff1[256] = {
0x0000, 0x02d8, 0x02c7, 0x02d9, 0x0131, 0xfb01, 0xfb02, 0x2044,
0x02dd, 0x0141, 0x0142, 0x2212, 0x02db, 0x02da, 0x017d, 0x017e,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
0x0038, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x0000,
0x20ac, 0x0000, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,
0x0000, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x0000, 0x0000, 0x0178,
0x0000, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x0000, 0x00ae, 0x00af,
0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff,
};
WCHAR ret = 0;
switch (cp)
{
case 0xfff1:
ret = c < ARRAY_SIZE(map_fff1) ? map_fff1[c] : 0;
break;
case 0xffff: /* Wine extension */
ret = c;
break;
}
if (!ret && c)
FIXME("unrecognized character %x in %x\n", c, cp);
return ret;
}
static BOOL map_glyph_to_unicode(struct font_data *font_data,
const char *data, UINT64 size, const char *name)
{
const struct ntf_header *header = (const struct ntf_header *)data;
const struct list_entry *list_elem;
const struct glyph_set *glyph_set;
const unsigned short *p;
int i, j;
list_elem = (const struct list_entry *)(data + header->glyph_set_off);
for (i = 0; i < header->glyph_set_count; i++)
{
if (!check_ntf_str(data, size, data + list_elem->name_off))
return FALSE;
if (strcmp(data + list_elem->name_off, name))
{
list_elem++;
continue;
}
if (list_elem->off + list_elem->size > size)
return FALSE;
glyph_set = (const struct glyph_set *)(data + list_elem->off);
if (font_data->glyph_count > glyph_set->glyph_count)
return FALSE;
p = (const unsigned short *)((const char *)glyph_set + glyph_set->glyph_set_off);
if (glyph_set->flags & GLYPH_SET_OMIT_CP)
{
const struct code_page *code_page = (const struct code_page *)((const char *)glyph_set + glyph_set->cp_off);
unsigned short def_cp;
if (glyph_set->cp_off + sizeof(*code_page) * glyph_set->cp_count > list_elem->size)
return FALSE;
if (glyph_set->cp_count != 1)
return FALSE;
if (glyph_set->glyph_set_off + sizeof(short) * font_data->glyph_count > list_elem->size)
return FALSE;
def_cp = code_page->cp;
for (j = 0; j < font_data->glyph_count; j++)
{
font_data->glyphs[j].wch = convert_ntf_cp(p[0], def_cp);
p++;
}
}
else
{
if (glyph_set->glyph_set_off + sizeof(short[2]) * font_data->glyph_count > list_elem->size)
return FALSE;
for (j = 0; j < font_data->glyph_count; j++)
{
font_data->glyphs[j].wch = convert_ntf_cp(p[0], p[1]);
p += 2;
}
}
return TRUE;
}
return FALSE;
}
static BOOL add_ntf_fonts(const char *data, int size)
{
const struct ntf_header *header = (const struct ntf_header *)data;
const struct width_range *width_range;
const struct list_entry *list_elem;
const struct font_mtx *font_mtx;
struct font_data *font_data;
const IFIMETRICS *metrics;
const char *name;
int i, j, k;
if (size < sizeof(*header) ||
size < header->glyph_set_off + header->glyph_set_count * sizeof(*list_elem) ||
size < header->font_mtx_off + header->font_mtx_count * sizeof(*list_elem))
return FALSE;
list_elem = (const struct list_entry *)(data + header->font_mtx_off);
for (i = 0; i < header->font_mtx_count; i++)
{
name = data + list_elem->name_off;
if (!check_ntf_str(data, size, name))
return FALSE;
TRACE("adding %s font\n", name);
if (list_elem->size + list_elem->off > size)
return FALSE;
font_mtx = (const struct font_mtx *)(data + list_elem->off);
if (list_elem->off + font_mtx->metrics_off + FIELD_OFFSET(IFIMETRICS, panose) > size)
return FALSE;
metrics = (const IFIMETRICS *)((const char *)font_mtx + font_mtx->metrics_off);
if (list_elem->off + font_mtx->metrics_off + metrics->cjThis > size)
return FALSE;
if (list_elem->off + font_mtx->width_off + sizeof(*width_range) * font_mtx->width_count > size)
return FALSE;
width_range = (const struct width_range *)((const char *)font_mtx + font_mtx->width_off);
if (!check_ntf_str(data, size, (const char *)font_mtx + font_mtx->glyph_set_name_off))
return FALSE;
if (!font_mtx->glyph_count)
{
list_elem++;
continue;
}
font_data = calloc(sizeof(*font_data), 1);
if (!font_data)
return FALSE;
font_data->glyph_count = font_mtx->glyph_count;
font_data->name = malloc(strlen(name) + 1);
font_data->metrics = malloc(metrics->cjThis);
font_data->glyphs = malloc(sizeof(*font_data->glyphs) * font_data->glyph_count);
if (!font_data->name || !font_data->metrics || !font_data->glyphs)
{
free_font_data(font_data);
return FALSE;
}
memcpy(font_data->name, name, strlen(name) + 1);
memcpy(font_data->metrics, metrics, metrics->cjThis);
for (j = 0; j < font_mtx->glyph_count; j++)
font_data->glyphs[j].width = font_mtx->def_width;
for (j = 0; j < font_mtx->width_count; j++)
{
/* Use default width */
if (width_range[j].width == 0x80000008)
continue;
for (k = 0; k < width_range[j].count; k++)
{
if (width_range[j].first + k >= font_data->glyph_count)
break;
font_data->glyphs[width_range[j].first + k].width = width_range[j].width;
}
}
if (!map_glyph_to_unicode(font_data, data, size,
(const char *)font_mtx + font_mtx->glyph_set_name_off))
{
free_font_data(font_data);
WARN("error loading %s font\n", name);
list_elem++;
continue;
}
font_data->def_glyph = font_data->glyphs[0];
qsort(font_data->glyphs, font_data->glyph_count,
sizeof(*font_data->glyphs), cmp_glyph_info);
list_add_head(&fonts, &font_data->entry);
list_elem++;
TRACE("%s font added\n", name);
}
return TRUE;
}
static NTSTATUS import_ntf(void *arg)
{
struct import_ntf_params *params = arg;
return add_ntf_fonts(params->data, params->size);
}
static NTSTATUS open_dc(void *arg)
{
UNICODE_STRING device_str, output_str;
struct open_dc_params *params = arg;
struct printer_info *pi;
pi = find_printer_info(params->device);
if (!pi)
{
pi = malloc(sizeof(*pi));
if (!pi) return FALSE;
pi->name = params->device;
pi->devmode = params->def_devmode;
list_add_head(&printer_info_list, &pi->entry);
}
device_str.Length = device_str.MaximumLength = lstrlenW(params->device) + 1;
device_str.Buffer = (WCHAR *)params->device;
if (params->output)
{
output_str.Length = output_str.MaximumLength = lstrlenW(params->output) + 1;
output_str.Buffer = (WCHAR *)params->output;
}
params->hdc = NtGdiOpenDCW(&device_str, params->devmode, params->output ? &output_str : NULL,
WINE_GDI_DRIVER_VERSION, 0, (HANDLE)&psdrv_funcs, NULL, NULL);
return TRUE;
}
static NTSTATUS free_printer_info(void *arg)
{
struct font_data *font, *font_next;
struct printer_info *pi, *next;
LIST_FOR_EACH_ENTRY_SAFE(pi, next, &printer_info_list, struct printer_info, entry)
{
free(pi);
}
LIST_FOR_EACH_ENTRY_SAFE(font, font_next, &fonts, struct font_data, entry)
{
free_font_data(font);
}
return 0;
}
const unixlib_entry_t __wine_unix_call_funcs[] =
{
free_printer_info,
import_ntf,
open_dc,
};
C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == unix_funcs_count);
#ifdef _WIN64
typedef ULONG PTR32;
static NTSTATUS wow64_import_ntf(void *args)
{
struct
{
PTR32 data;
int size;
} const *params32 = args;
struct import_ntf_params params = { ULongToPtr(params32->data), params32->size };
return import_ntf(&params);
}
static NTSTATUS wow64_open_dc(void *args)
{
struct
{
PTR32 device;
PTR32 devmode;
PTR32 output;
PTR32 def_devmode;
PTR32 hdc;
} *params32 = args;
struct open_dc_params params =
{
ULongToPtr(params32->device),
ULongToPtr(params32->devmode),
ULongToPtr(params32->output),
ULongToPtr(params32->def_devmode),
0
};
NTSTATUS ret;
ret = open_dc(&params);
params32->hdc = PtrToUlong(params.hdc);
return ret;
}
const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
{
free_printer_info,
wow64_import_ntf,
wow64_open_dc,
};
C_ASSERT(ARRAYSIZE(__wine_unix_call_wow64_funcs) == unix_funcs_count);
#endif /* _WIN64 */