/* * 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 #include #include #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(¶ms); } 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(¶ms); 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 */