553 lines
16 KiB
C
553 lines
16 KiB
C
/*
|
|
* PostScript driver font functions
|
|
*
|
|
* Copyright 1998 Huw D M Davies
|
|
*
|
|
*/
|
|
#include <string.h>
|
|
#include <stdlib.h> /* for bsearch() */
|
|
#include "winspool.h"
|
|
#include "psdrv.h"
|
|
#include "debugtools.h"
|
|
#include "winerror.h"
|
|
|
|
DEFAULT_DEBUG_CHANNEL(psdrv);
|
|
|
|
/*******************************************************************************
|
|
* ScaleFont
|
|
*
|
|
* Scale font to requested lfHeight
|
|
*
|
|
*/
|
|
inline static float round(float f)
|
|
{
|
|
return (f > 0) ? (f + 0.5) : (f - 0.5);
|
|
}
|
|
|
|
static void ScaleFont(DC *dc, LOGFONTW *lf, PSDRV_PDEVICE *physDev)
|
|
{
|
|
PSFONT *font = &(physDev->font);
|
|
const WINMETRICS *wm = &(font->afm->WinMetrics);
|
|
TEXTMETRICW *tm = &(font->tm);
|
|
LONG lfHeight_ds;
|
|
USHORT usUnitsPerEm, usWinAscent, usWinDescent;
|
|
SHORT sAscender, sDescender, sLineGap, sTypoAscender;
|
|
SHORT sTypoDescender, sTypoLineGap, sAvgCharWidth;
|
|
|
|
TRACE("'%s' %li\n", font->afm->FontName, lf->lfHeight);
|
|
|
|
lfHeight_ds = INTERNAL_YWSTODS(dc, lf->lfHeight); /* world->viewport */
|
|
|
|
if (lfHeight_ds < 0) /* match em height */
|
|
{
|
|
font->scale = - ((float)lfHeight_ds / (float)(wm->usUnitsPerEm));
|
|
}
|
|
else /* match cell height */
|
|
{
|
|
font->scale = (float)lfHeight_ds /
|
|
(float)(wm->usWinAscent + wm->usWinDescent);
|
|
}
|
|
|
|
physDev->font.size = (INT)round(font->scale * (float)wm->usUnitsPerEm);
|
|
physDev->font.escapement = lf->lfEscapement;
|
|
physDev->font.set = FALSE;
|
|
|
|
usUnitsPerEm = (USHORT)round((float)(wm->usUnitsPerEm) * font->scale);
|
|
sAscender = (SHORT)round((float)(wm->sAscender) * font->scale);
|
|
sDescender = (SHORT)round((float)(wm->sDescender) * font->scale);
|
|
sLineGap = (SHORT)round((float)(wm->sLineGap) * font->scale);
|
|
sTypoAscender = (SHORT)round((float)(wm->sTypoAscender) * font->scale);
|
|
sTypoDescender = (SHORT)round((float)(wm->sTypoDescender) * font->scale);
|
|
sTypoLineGap = (SHORT)round((float)(wm->sTypoLineGap) * font->scale);
|
|
usWinAscent = (USHORT)round((float)(wm->usWinAscent) * font->scale);
|
|
usWinDescent = (USHORT)round((float)(wm->usWinDescent) * font->scale);
|
|
sAvgCharWidth = (SHORT)round((float)(wm->sAvgCharWidth) * font->scale);
|
|
|
|
tm->tmAscent = (LONG)usWinAscent;
|
|
tm->tmDescent = (LONG)usWinDescent;
|
|
tm->tmHeight = tm->tmAscent + tm->tmDescent;
|
|
|
|
tm->tmInternalLeading = tm->tmHeight - (LONG)usUnitsPerEm;
|
|
if (tm->tmInternalLeading < 0)
|
|
tm->tmInternalLeading = 0;
|
|
|
|
tm->tmExternalLeading =
|
|
(LONG)(sAscender - sDescender + sLineGap) - tm->tmHeight;
|
|
if (tm->tmExternalLeading < 0)
|
|
tm->tmExternalLeading = 0;
|
|
|
|
/*
|
|
* Character widths are stored as PostScript metrics, which assume an
|
|
* em square size of 1000.
|
|
*/
|
|
|
|
tm->tmAveCharWidth = (LONG)sAvgCharWidth;
|
|
|
|
tm->tmMaxCharWidth = (LONG)round(
|
|
(font->afm->FontBBox.urx - font->afm->FontBBox.llx) *
|
|
font->scale * (float)(wm->usUnitsPerEm) / 1000.0);
|
|
|
|
tm->tmWeight = font->afm->Weight;
|
|
tm->tmItalic = (font->afm->ItalicAngle != 0.0);
|
|
tm->tmUnderlined = 0;
|
|
tm->tmStruckOut = 0;
|
|
tm->tmFirstChar = (WCHAR)(font->afm->Metrics[0].UV);
|
|
tm->tmLastChar =
|
|
(WCHAR)(font->afm->Metrics[font->afm->NumofMetrics - 1].UV);
|
|
tm->tmDefaultChar = 0x001f; /* Win2K does this - FIXME? */
|
|
tm->tmBreakChar = tm->tmFirstChar; /* should be 'space' */
|
|
|
|
/* Assume that a font with an em square size of 1000 is a PostScript font */
|
|
|
|
tm->tmPitchAndFamily = (font->afm->IsFixedPitch ? 0 : TMPF_FIXED_PITCH) |
|
|
((wm->usUnitsPerEm == 1000) ? TMPF_DEVICE : TMPF_TRUETYPE) |
|
|
TMPF_VECTOR; /* TMPF_VECTOR always set per Win32 API doc */
|
|
|
|
tm->tmCharSet = ANSI_CHARSET; /* FIXME */
|
|
tm->tmOverhang = 0;
|
|
tm->tmDigitizedAspectX = physDev->logPixelsY;
|
|
tm->tmDigitizedAspectY = physDev->logPixelsX;
|
|
|
|
/*
|
|
* 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..
|
|
*/
|
|
|
|
font->scale *= (float)wm->usUnitsPerEm / 1000.0;
|
|
|
|
TRACE("Selected PS font '%s' size %d weight %ld.\n",
|
|
physDev->font.afm->FontName, physDev->font.size,
|
|
physDev->font.tm.tmWeight );
|
|
TRACE("H = %ld As = %ld Des = %ld IL = %ld EL = %ld\n",
|
|
physDev->font.tm.tmHeight, physDev->font.tm.tmAscent,
|
|
physDev->font.tm.tmDescent, physDev->font.tm.tmInternalLeading,
|
|
physDev->font.tm.tmExternalLeading);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* PSDRV_FONT_SelectObject
|
|
*/
|
|
HFONT PSDRV_FONT_SelectObject( DC * dc, HFONT hfont )
|
|
{
|
|
LOGFONTW lf;
|
|
HFONT16 prevfont = dc->hFont;
|
|
PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
|
|
BOOL bd = FALSE, it = FALSE;
|
|
AFMLISTENTRY *afmle;
|
|
FONTFAMILY *family;
|
|
char FaceName[LF_FACESIZE];
|
|
|
|
if (!GetObjectW( hfont, sizeof(lf), &lf )) return 0;
|
|
|
|
TRACE("FaceName = %s Height = %ld Italic = %d Weight = %ld\n",
|
|
debugstr_w(lf.lfFaceName), lf.lfHeight, lf.lfItalic,
|
|
lf.lfWeight);
|
|
|
|
dc->hFont = hfont;
|
|
|
|
if(lf.lfItalic)
|
|
it = TRUE;
|
|
if(lf.lfWeight > 550)
|
|
bd = TRUE;
|
|
WideCharToMultiByte(CP_ACP, 0, lf.lfFaceName, -1,
|
|
FaceName, sizeof(FaceName), NULL, NULL);
|
|
|
|
if(FaceName[0] == '\0') {
|
|
switch(lf.lfPitchAndFamily & 0xf0) {
|
|
case FF_DONTCARE:
|
|
break;
|
|
case FF_ROMAN:
|
|
case FF_SCRIPT:
|
|
strcpy(FaceName, "Times");
|
|
break;
|
|
case FF_SWISS:
|
|
strcpy(FaceName, "Helvetica");
|
|
break;
|
|
case FF_MODERN:
|
|
strcpy(FaceName, "Courier");
|
|
break;
|
|
case FF_DECORATIVE:
|
|
strcpy(FaceName, "Symbol");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(FaceName[0] == '\0') {
|
|
switch(lf.lfPitchAndFamily & 0x0f) {
|
|
case VARIABLE_PITCH:
|
|
strcpy(FaceName, "Times");
|
|
break;
|
|
default:
|
|
strcpy(FaceName, "Courier");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (physDev->pi->FontSubTableSize != 0)
|
|
{
|
|
DWORD i;
|
|
|
|
for (i = 0; i < physDev->pi->FontSubTableSize; ++i)
|
|
{
|
|
if (!strcasecmp (FaceName,
|
|
physDev->pi->FontSubTable[i].pValueName))
|
|
{
|
|
TRACE ("substituting facename '%s' for '%s'\n",
|
|
(LPSTR) physDev->pi->FontSubTable[i].pData, FaceName);
|
|
if (strlen ((LPSTR) physDev->pi->FontSubTable[i].pData) <
|
|
LF_FACESIZE)
|
|
strcpy (FaceName,
|
|
(LPSTR) physDev->pi->FontSubTable[i].pData);
|
|
else
|
|
WARN ("Facename '%s' is too long; ignoring substitution\n",
|
|
(LPSTR) physDev->pi->FontSubTable[i].pData);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
TRACE("Trying to find facename '%s'\n", FaceName);
|
|
|
|
/* Look for a matching font family */
|
|
for(family = physDev->pi->Fonts; family; family = family->next) {
|
|
if(!strcasecmp(FaceName, family->FamilyName))
|
|
break;
|
|
}
|
|
if(!family) {
|
|
/* Fallback for Window's font families to common PostScript families */
|
|
if(!strcmp(FaceName, "Arial"))
|
|
strcpy(FaceName, "Helvetica");
|
|
else if(!strcmp(FaceName, "System"))
|
|
strcpy(FaceName, "Helvetica");
|
|
else if(!strcmp(FaceName, "Times New Roman"))
|
|
strcpy(FaceName, "Times");
|
|
else if(!strcmp(FaceName, "Courier New"))
|
|
strcpy(FaceName, "Courier");
|
|
|
|
for(family = physDev->pi->Fonts; family; family = family->next) {
|
|
if(!strcmp(FaceName, family->FamilyName))
|
|
break;
|
|
}
|
|
}
|
|
/* If all else fails, use the first font defined for the printer */
|
|
if(!family)
|
|
family = physDev->pi->Fonts;
|
|
|
|
TRACE("Got family '%s'\n", family->FamilyName);
|
|
|
|
for(afmle = family->afmlist; afmle; afmle = afmle->next) {
|
|
if( (bd == (afmle->afm->Weight == FW_BOLD)) &&
|
|
(it == (afmle->afm->ItalicAngle != 0.0)) )
|
|
break;
|
|
}
|
|
if(!afmle)
|
|
afmle = family->afmlist; /* not ideal */
|
|
|
|
TRACE("Got font '%s'\n", afmle->afm->FontName);
|
|
|
|
physDev->font.afm = afmle->afm;
|
|
ScaleFont(dc, &lf, physDev);
|
|
|
|
return prevfont;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* PSDRV_GetTextMetrics
|
|
*/
|
|
BOOL PSDRV_GetTextMetrics(DC *dc, TEXTMETRICW *metrics)
|
|
{
|
|
PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
|
|
|
|
memcpy(metrics, &(physDev->font.tm), sizeof(physDev->font.tm));
|
|
return TRUE;
|
|
}
|
|
|
|
#if 0
|
|
/***********************************************************************
|
|
* PSDRV_UnicodeToANSI
|
|
*/
|
|
char PSDRV_UnicodeToANSI(int u)
|
|
{
|
|
if((u & 0xff) == u)
|
|
return u;
|
|
switch(u) {
|
|
case 0x2013: /* endash */
|
|
return 0x96;
|
|
case 0x2014: /* emdash */
|
|
return 0x97;
|
|
case 0x2018: /* quoteleft */
|
|
return 0x91;
|
|
case 0x2019: /* quoteright */
|
|
return 0x92;
|
|
case 0x201c: /* quotedblleft */
|
|
return 0x93;
|
|
case 0x201d: /* quotedblright */
|
|
return 0x94;
|
|
case 0x2022: /* bullet */
|
|
return 0x95;
|
|
default:
|
|
WARN("Umapped unicode char U%04x\n", u);
|
|
return 0xff;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************************
|
|
* PSDRV_UVMetrics
|
|
*
|
|
* Find the AFMMETRICS for a given UV. Returns first glyph in the font
|
|
* (space?) if the font does not have a glyph for the given UV.
|
|
*/
|
|
static int MetricsByUV(const void *a, const void *b)
|
|
{
|
|
return (int)(((const AFMMETRICS *)a)->UV - ((const AFMMETRICS *)b)->UV);
|
|
}
|
|
|
|
const AFMMETRICS *PSDRV_UVMetrics(LONG UV, const AFM *afm)
|
|
{
|
|
AFMMETRICS key;
|
|
const AFMMETRICS *needle;
|
|
|
|
/*
|
|
* 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 ((afm->Metrics->UV & 0xff00) == 0xf000 && UV < 0x100)
|
|
UV |= 0xf000;
|
|
|
|
key.UV = UV;
|
|
|
|
needle = bsearch(&key, afm->Metrics, afm->NumofMetrics, sizeof(AFMMETRICS),
|
|
MetricsByUV);
|
|
|
|
if (needle == NULL)
|
|
{
|
|
WARN("No glyph for U+%.4lX in %s\n", UV, afm->FontName);
|
|
needle = afm->Metrics;
|
|
}
|
|
|
|
return needle;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* PSDRV_GetTextExtentPoint
|
|
*/
|
|
#if 0
|
|
BOOL PSDRV_GetTextExtentPoint( DC *dc, LPCWSTR str, INT count,
|
|
LPSIZE size )
|
|
{
|
|
PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
|
|
INT i;
|
|
float width;
|
|
|
|
width = 0.0;
|
|
|
|
for(i = 0; i < count && str[i]; i++) {
|
|
char c = PSDRV_UnicodeToANSI(str[i]);
|
|
width += physDev->font.afm->CharWidths[(int)(unsigned char)c];
|
|
/* TRACE(psdrv, "Width after %dth char '%c' = %f\n", i, str[i], width);*/
|
|
}
|
|
width *= physDev->font.scale;
|
|
TRACE("Width after scale (%f) is %f\n", physDev->font.scale, width);
|
|
|
|
size->cx = GDI_ROUND((FLOAT)width * dc->xformVport2World.eM11);
|
|
size->cy = GDI_ROUND((FLOAT)physDev->font.tm.tmHeight * dc->xformVport2World.eM22);
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
BOOL PSDRV_GetTextExtentPoint(DC *dc, LPCWSTR str, INT count, LPSIZE size)
|
|
{
|
|
PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
|
|
int i;
|
|
float width = 0.0;
|
|
|
|
TRACE("%s %i\n", debugstr_wn(str, count), count);
|
|
|
|
for (i = 0; i < count && str[i] != '\0'; ++i)
|
|
width += PSDRV_UVMetrics(str[i], physDev->font.afm)->WX;
|
|
|
|
width *= physDev->font.scale;
|
|
|
|
size->cx = GDI_ROUND((FLOAT)width * dc->xformVport2World.eM11);
|
|
size->cy = GDI_ROUND((FLOAT)physDev->font.tm.tmHeight *
|
|
dc->xformVport2World.eM22);
|
|
|
|
TRACE("cx=%li cy=%li\n", size->cx, size->cy);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* PSDRV_GetCharWidth
|
|
*/
|
|
#if 0
|
|
BOOL PSDRV_GetCharWidth( DC *dc, UINT firstChar, UINT lastChar,
|
|
LPINT buffer )
|
|
{
|
|
PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
|
|
UINT i;
|
|
|
|
TRACE("first = %d last = %d\n", firstChar, lastChar);
|
|
|
|
if(lastChar > 0xff) return FALSE;
|
|
for( i = firstChar; i <= lastChar; i++ )
|
|
*buffer++ = physDev->font.afm->CharWidths[i] * physDev->font.scale;
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
BOOL PSDRV_GetCharWidth(DC *dc, UINT firstChar, UINT lastChar, LPINT buffer)
|
|
{
|
|
PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
|
|
UINT i;
|
|
|
|
TRACE("U+%.4X U+%.4X\n", firstChar, lastChar);
|
|
|
|
if (lastChar > 0xffff || firstChar > lastChar)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = firstChar; i <= lastChar; ++i)
|
|
{
|
|
*buffer = GDI_ROUND(PSDRV_UVMetrics(i, physDev->font.afm)->WX
|
|
* physDev->font.scale);
|
|
TRACE("U+%.4X: %i\n", i, *buffer);
|
|
++buffer;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* PSDRV_SetFont
|
|
*/
|
|
BOOL PSDRV_SetFont( DC *dc )
|
|
{
|
|
PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
|
|
BOOL ReEncode = FALSE;
|
|
|
|
PSDRV_WriteSetColor(dc, &physDev->font.color);
|
|
if(physDev->font.set) return TRUE;
|
|
|
|
if(physDev->font.afm->EncodingScheme &&
|
|
!strcmp(physDev->font.afm->EncodingScheme, "AdobeStandardEncoding"))
|
|
ReEncode = TRUE;
|
|
if(ReEncode)
|
|
PSDRV_WriteReencodeFont(dc);
|
|
PSDRV_WriteSetFont(dc, ReEncode);
|
|
physDev->font.set = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* PSDRV_GetFontMetric
|
|
*/
|
|
static UINT PSDRV_GetFontMetric(HDC hdc, const AFM *pafm,
|
|
NEWTEXTMETRICEXW *pTM, ENUMLOGFONTEXW *pLF, INT16 size)
|
|
|
|
{
|
|
float scale = size / (pafm->FullAscender - pafm->Descender);
|
|
|
|
memset( pLF, 0, sizeof(*pLF) );
|
|
memset( pTM, 0, sizeof(*pTM) );
|
|
|
|
#define plf ((LPLOGFONTW)pLF)
|
|
#define ptm ((LPNEWTEXTMETRICW)pTM)
|
|
plf->lfHeight = ptm->tmHeight = size;
|
|
plf->lfWidth = ptm->tmAveCharWidth = pafm->CharWidths[120] * scale;
|
|
plf->lfWeight = ptm->tmWeight = pafm->Weight;
|
|
plf->lfItalic = ptm->tmItalic = pafm->ItalicAngle != 0.0;
|
|
plf->lfUnderline = ptm->tmUnderlined = 0;
|
|
plf->lfStrikeOut = ptm->tmStruckOut = 0;
|
|
plf->lfCharSet = ptm->tmCharSet = ANSI_CHARSET;
|
|
|
|
/* convert pitch values */
|
|
|
|
ptm->tmPitchAndFamily = pafm->IsFixedPitch ? 0 : TMPF_FIXED_PITCH;
|
|
ptm->tmPitchAndFamily |= TMPF_DEVICE;
|
|
plf->lfPitchAndFamily = 0;
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, pafm->FamilyName, -1,
|
|
plf->lfFaceName, LF_FACESIZE);
|
|
#undef plf
|
|
|
|
ptm->tmAscent = pafm->FullAscender * scale;
|
|
ptm->tmDescent = -pafm->Descender * scale;
|
|
ptm->tmInternalLeading = (pafm->FullAscender - pafm->Ascender) * scale;
|
|
ptm->tmMaxCharWidth = pafm->CharWidths[77] * scale;
|
|
/* FIXME: X and Y are swapped here, is this on purpose? */
|
|
ptm->tmDigitizedAspectX = GetDeviceCaps( hdc, LOGPIXELSY );
|
|
ptm->tmDigitizedAspectY = GetDeviceCaps( hdc, LOGPIXELSX );
|
|
|
|
*(INT*)&ptm->tmFirstChar = 32;
|
|
|
|
/* return font type */
|
|
return DEVICE_FONTTYPE;
|
|
#undef ptm
|
|
}
|
|
|
|
/***********************************************************************
|
|
* PSDRV_EnumDeviceFonts
|
|
*/
|
|
BOOL PSDRV_EnumDeviceFonts( HDC hdc, LPLOGFONTW plf,
|
|
DEVICEFONTENUMPROC proc, LPARAM lp )
|
|
{
|
|
ENUMLOGFONTEXW lf;
|
|
NEWTEXTMETRICEXW tm;
|
|
BOOL b, bRet = 0;
|
|
AFMLISTENTRY *afmle;
|
|
FONTFAMILY *family;
|
|
PSDRV_PDEVICE *physDev;
|
|
char FaceName[LF_FACESIZE];
|
|
DC *dc = DC_GetDCPtr( hdc );
|
|
if (!dc) return FALSE;
|
|
|
|
physDev = (PSDRV_PDEVICE *)dc->physDev;
|
|
/* FIXME!! should reevaluate dc->physDev after every callback */
|
|
GDI_ReleaseObj( hdc );
|
|
|
|
if( plf->lfFaceName[0] ) {
|
|
WideCharToMultiByte(CP_ACP, 0, plf->lfFaceName, -1,
|
|
FaceName, sizeof(FaceName), NULL, NULL);
|
|
TRACE("lfFaceName = '%s'\n", FaceName);
|
|
for(family = physDev->pi->Fonts; family; family = family->next) {
|
|
if(!strncmp(FaceName, family->FamilyName,
|
|
strlen(family->FamilyName)))
|
|
break;
|
|
}
|
|
if(family) {
|
|
for(afmle = family->afmlist; afmle; afmle = afmle->next) {
|
|
TRACE("Got '%s'\n", afmle->afm->FontName);
|
|
if( (b = (*proc)( &lf, &tm,
|
|
PSDRV_GetFontMetric( hdc, afmle->afm, &tm, &lf, 200 ),
|
|
lp )) )
|
|
bRet = b;
|
|
else break;
|
|
}
|
|
}
|
|
} else {
|
|
|
|
TRACE("lfFaceName = NULL\n");
|
|
for(family = physDev->pi->Fonts; family; family = family->next) {
|
|
afmle = family->afmlist;
|
|
TRACE("Got '%s'\n", afmle->afm->FontName);
|
|
if( (b = (*proc)( &lf, &tm,
|
|
PSDRV_GetFontMetric( hdc, afmle->afm, &tm, &lf, 200 ),
|
|
lp )) )
|
|
bRet = b;
|
|
else break;
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|