2 * PostScript driver font functions
4 * Copyright 1998 Huw D M Davies
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <stdlib.h> /* for bsearch() */
25 #include "wine/debug.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(psdrv
);
30 /***********************************************************************
33 inline static BOOL
is_stock_font( HFONT font
)
36 for (i
= OEM_FIXED_FONT
; i
<= DEFAULT_GUI_FONT
; i
++)
38 if (i
!= DEFAULT_PALETTE
&& font
== GetStockObject(i
)) return TRUE
;
44 /*******************************************************************************
47 * Scale font to requested lfHeight
50 inline static float round(float f
)
52 return (f
> 0) ? (f
+ 0.5) : (f
- 0.5);
55 static VOID
ScaleFont(const AFM
*afm
, LONG lfHeight
, PSFONT
*font
,
58 const WINMETRICS
*wm
= &(afm
->WinMetrics
);
59 USHORT usUnitsPerEm
, usWinAscent
, usWinDescent
;
60 SHORT sAscender
, sDescender
, sLineGap
, sTypoAscender
;
61 SHORT sTypoDescender
, sTypoLineGap
, sAvgCharWidth
;
63 TRACE("'%s' %li\n", afm
->FontName
, lfHeight
);
65 if (lfHeight
< 0) /* match em height */
67 font
->scale
= - ((float)lfHeight
/ (float)(wm
->usUnitsPerEm
));
69 else /* match cell height */
71 font
->scale
= (float)lfHeight
/
72 (float)(wm
->usWinAscent
+ wm
->usWinDescent
);
75 font
->size
= (INT
)round(font
->scale
* (float)wm
->usUnitsPerEm
);
78 usUnitsPerEm
= (USHORT
)round((float)(wm
->usUnitsPerEm
) * font
->scale
);
79 sAscender
= (SHORT
)round((float)(wm
->sAscender
) * font
->scale
);
80 sDescender
= (SHORT
)round((float)(wm
->sDescender
) * font
->scale
);
81 sLineGap
= (SHORT
)round((float)(wm
->sLineGap
) * font
->scale
);
82 sTypoAscender
= (SHORT
)round((float)(wm
->sTypoAscender
) * font
->scale
);
83 sTypoDescender
= (SHORT
)round((float)(wm
->sTypoDescender
) * font
->scale
);
84 sTypoLineGap
= (SHORT
)round((float)(wm
->sTypoLineGap
) * font
->scale
);
85 usWinAscent
= (USHORT
)round((float)(wm
->usWinAscent
) * font
->scale
);
86 usWinDescent
= (USHORT
)round((float)(wm
->usWinDescent
) * font
->scale
);
87 sAvgCharWidth
= (SHORT
)round((float)(wm
->sAvgCharWidth
) * font
->scale
);
89 tm
->tmAscent
= (LONG
)usWinAscent
;
90 tm
->tmDescent
= (LONG
)usWinDescent
;
91 tm
->tmHeight
= tm
->tmAscent
+ tm
->tmDescent
;
93 tm
->tmInternalLeading
= tm
->tmHeight
- (LONG
)usUnitsPerEm
;
94 if (tm
->tmInternalLeading
< 0)
95 tm
->tmInternalLeading
= 0;
97 tm
->tmExternalLeading
=
98 (LONG
)(sAscender
- sDescender
+ sLineGap
) - tm
->tmHeight
;
99 if (tm
->tmExternalLeading
< 0)
100 tm
->tmExternalLeading
= 0;
102 tm
->tmAveCharWidth
= (LONG
)sAvgCharWidth
;
104 tm
->tmWeight
= afm
->Weight
;
105 tm
->tmItalic
= (afm
->ItalicAngle
!= 0.0);
106 tm
->tmUnderlined
= 0;
108 tm
->tmFirstChar
= (WCHAR
)(afm
->Metrics
[0].UV
);
109 tm
->tmLastChar
= (WCHAR
)(afm
->Metrics
[afm
->NumofMetrics
- 1].UV
);
110 tm
->tmDefaultChar
= 0x001f; /* Win2K does this - FIXME? */
111 tm
->tmBreakChar
= tm
->tmFirstChar
; /* should be 'space' */
113 tm
->tmPitchAndFamily
= TMPF_DEVICE
| TMPF_VECTOR
;
114 if (!afm
->IsFixedPitch
)
115 tm
->tmPitchAndFamily
|= TMPF_FIXED_PITCH
; /* yes, it's backwards */
116 if (wm
->usUnitsPerEm
!= 1000)
117 tm
->tmPitchAndFamily
|= TMPF_TRUETYPE
;
119 tm
->tmCharSet
= ANSI_CHARSET
; /* FIXME */
123 * This is kludgy. font->scale is used in several places in the driver
124 * to adjust PostScript-style metrics. Since these metrics have been
125 * "normalized" to an em-square size of 1000, font->scale needs to be
126 * similarly adjusted..
129 font
->scale
*= (float)wm
->usUnitsPerEm
/ 1000.0;
131 tm
->tmMaxCharWidth
= (LONG
)round(
132 (afm
->FontBBox
.urx
- afm
->FontBBox
.llx
) * font
->scale
);
134 TRACE("Selected PS font '%s' size %d weight %ld.\n", afm
->FontName
,
135 font
->size
, tm
->tmWeight
);
136 TRACE("H = %ld As = %ld Des = %ld IL = %ld EL = %ld\n", tm
->tmHeight
,
137 tm
->tmAscent
, tm
->tmDescent
, tm
->tmInternalLeading
,
138 tm
->tmExternalLeading
);
141 /***********************************************************************
142 * SelectFont (WINEPS.@)
144 HFONT
PSDRV_SelectFont( PSDRV_PDEVICE
*physDev
, HFONT hfont
)
147 BOOL bd
= FALSE
, it
= FALSE
;
150 char FaceName
[LF_FACESIZE
];
152 if (!GetObjectW( hfont
, sizeof(lf
), &lf
)) return 0;
154 TRACE("FaceName = %s Height = %ld Italic = %d Weight = %ld\n",
155 debugstr_w(lf
.lfFaceName
), lf
.lfHeight
, lf
.lfItalic
,
160 if(lf
.lfWeight
> 550)
162 WideCharToMultiByte(CP_ACP
, 0, lf
.lfFaceName
, -1,
163 FaceName
, sizeof(FaceName
), NULL
, NULL
);
165 if(FaceName
[0] == '\0') {
166 switch(lf
.lfPitchAndFamily
& 0xf0) {
171 strcpy(FaceName
, "Times");
174 strcpy(FaceName
, "Helvetica");
177 strcpy(FaceName
, "Courier");
180 strcpy(FaceName
, "Symbol");
185 if(FaceName
[0] == '\0') {
186 switch(lf
.lfPitchAndFamily
& 0x0f) {
188 strcpy(FaceName
, "Times");
191 strcpy(FaceName
, "Courier");
196 if (physDev
->pi
->FontSubTableSize
!= 0)
200 for (i
= 0; i
< physDev
->pi
->FontSubTableSize
; ++i
)
202 if (!strcasecmp (FaceName
,
203 physDev
->pi
->FontSubTable
[i
].pValueName
))
205 TRACE ("substituting facename '%s' for '%s'\n",
206 (LPSTR
) physDev
->pi
->FontSubTable
[i
].pData
, FaceName
);
207 if (strlen ((LPSTR
) physDev
->pi
->FontSubTable
[i
].pData
) <
210 (LPSTR
) physDev
->pi
->FontSubTable
[i
].pData
);
212 WARN ("Facename '%s' is too long; ignoring substitution\n",
213 (LPSTR
) physDev
->pi
->FontSubTable
[i
].pData
);
219 TRACE("Trying to find facename '%s'\n", FaceName
);
221 /* Look for a matching font family */
222 for(family
= physDev
->pi
->Fonts
; family
; family
= family
->next
) {
223 if(!strcasecmp(FaceName
, family
->FamilyName
))
227 /* Fallback for Window's font families to common PostScript families */
228 if(!strcmp(FaceName
, "Arial"))
229 strcpy(FaceName
, "Helvetica");
230 else if(!strcmp(FaceName
, "System"))
231 strcpy(FaceName
, "Helvetica");
232 else if(!strcmp(FaceName
, "Times New Roman"))
233 strcpy(FaceName
, "Times");
234 else if(!strcmp(FaceName
, "Courier New"))
235 strcpy(FaceName
, "Courier");
237 for(family
= physDev
->pi
->Fonts
; family
; family
= family
->next
) {
238 if(!strcmp(FaceName
, family
->FamilyName
))
242 /* If all else fails, use the first font defined for the printer */
244 family
= physDev
->pi
->Fonts
;
246 TRACE("Got family '%s'\n", family
->FamilyName
);
248 for(afmle
= family
->afmlist
; afmle
; afmle
= afmle
->next
) {
249 if( (bd
== (afmle
->afm
->Weight
== FW_BOLD
)) &&
250 (it
== (afmle
->afm
->ItalicAngle
!= 0.0)) )
254 afmle
= family
->afmlist
; /* not ideal */
256 TRACE("Got font '%s'\n", afmle
->afm
->FontName
);
258 physDev
->font
.afm
= afmle
->afm
;
259 /* stock fonts ignore the mapping mode */
260 if (!is_stock_font( hfont
)) lf
.lfHeight
= INTERNAL_YWSTODS(physDev
->dc
, lf
.lfHeight
);
261 ScaleFont(physDev
->font
.afm
, lf
.lfHeight
,
262 &(physDev
->font
), &(physDev
->font
.tm
));
264 physDev
->font
.escapement
= lf
.lfEscapement
;
266 /* Does anyone know if these are supposed to be reversed like this? */
268 physDev
->font
.tm
.tmDigitizedAspectX
= physDev
->logPixelsY
;
269 physDev
->font
.tm
.tmDigitizedAspectY
= physDev
->logPixelsX
;
271 return TRUE
; /* We'll use a device font for now */
274 /***********************************************************************
275 * PSDRV_GetTextMetrics
277 BOOL
PSDRV_GetTextMetrics(PSDRV_PDEVICE
*physDev
, TEXTMETRICW
*metrics
)
279 memcpy(metrics
, &(physDev
->font
.tm
), sizeof(physDev
->font
.tm
));
283 /******************************************************************************
286 * Find the AFMMETRICS for a given UV. Returns first glyph in the font
287 * (space?) if the font does not have a glyph for the given UV.
289 static int MetricsByUV(const void *a
, const void *b
)
291 return (int)(((const AFMMETRICS
*)a
)->UV
- ((const AFMMETRICS
*)b
)->UV
);
294 const AFMMETRICS
*PSDRV_UVMetrics(LONG UV
, const AFM
*afm
)
297 const AFMMETRICS
*needle
;
300 * Ugly work-around for symbol fonts. Wine is sending characters which
301 * belong in the Unicode private use range (U+F020 - U+F0FF) as ASCII
302 * characters (U+0020 - U+00FF).
305 if ((afm
->Metrics
->UV
& 0xff00) == 0xf000 && UV
< 0x100)
310 needle
= bsearch(&key
, afm
->Metrics
, afm
->NumofMetrics
, sizeof(AFMMETRICS
),
315 WARN("No glyph for U+%.4lX in %s\n", UV
, afm
->FontName
);
316 needle
= afm
->Metrics
;
322 /***********************************************************************
323 * PSDRV_GetTextExtentPoint
325 BOOL
PSDRV_GetTextExtentPoint(PSDRV_PDEVICE
*physDev
, LPCWSTR str
, INT count
, LPSIZE size
)
327 DC
*dc
= physDev
->dc
;
331 TRACE("%s %i\n", debugstr_wn(str
, count
), count
);
333 for (i
= 0; i
< count
&& str
[i
] != '\0'; ++i
)
334 width
+= PSDRV_UVMetrics(str
[i
], physDev
->font
.afm
)->WX
;
336 width
*= physDev
->font
.scale
;
338 size
->cx
= GDI_ROUND((FLOAT
)width
* dc
->xformVport2World
.eM11
);
339 size
->cy
= GDI_ROUND((FLOAT
)physDev
->font
.tm
.tmHeight
*
340 dc
->xformVport2World
.eM22
);
342 TRACE("cx=%li cy=%li\n", size
->cx
, size
->cy
);
347 /***********************************************************************
350 BOOL
PSDRV_GetCharWidth(PSDRV_PDEVICE
*physDev
, UINT firstChar
, UINT lastChar
, LPINT buffer
)
354 TRACE("U+%.4X U+%.4X\n", firstChar
, lastChar
);
356 if (lastChar
> 0xffff || firstChar
> lastChar
)
358 SetLastError(ERROR_INVALID_PARAMETER
);
362 for (i
= firstChar
; i
<= lastChar
; ++i
)
364 *buffer
= GDI_ROUND(PSDRV_UVMetrics(i
, physDev
->font
.afm
)->WX
365 * physDev
->font
.scale
);
366 TRACE("U+%.4X: %i\n", i
, *buffer
);
373 /***********************************************************************
376 BOOL
PSDRV_SetFont( PSDRV_PDEVICE
*physDev
)
378 PSDRV_WriteSetColor(physDev
, &physDev
->font
.color
);
379 if(physDev
->font
.set
) return TRUE
;
381 PSDRV_WriteSetFont(physDev
);
382 physDev
->font
.set
= TRUE
;
387 /***********************************************************************
388 * PSDRV_GetFontMetric
390 static UINT
PSDRV_GetFontMetric( PSDRV_PDEVICE
*physDev
, const AFM
*afm
,
391 NEWTEXTMETRICEXW
*ntmx
, ENUMLOGFONTEXW
*elfx
)
393 /* ntmx->ntmTm is NEWTEXTMETRICW; compatible w/ TEXTMETRICW per Win32 doc */
395 TEXTMETRICW
*tm
= (TEXTMETRICW
*)&(ntmx
->ntmTm
);
396 LOGFONTW
*lf
= &(elfx
->elfLogFont
);
399 memset(ntmx
, 0, sizeof(*ntmx
));
400 memset(elfx
, 0, sizeof(*elfx
));
402 ScaleFont(afm
, -(LONG
)(afm
->WinMetrics
.usUnitsPerEm
), &font
, tm
);
404 lf
->lfHeight
= tm
->tmHeight
;
405 lf
->lfWidth
= tm
->tmAveCharWidth
;
406 lf
->lfWeight
= tm
->tmWeight
;
407 lf
->lfItalic
= tm
->tmItalic
;
408 lf
->lfCharSet
= tm
->tmCharSet
;
410 lf
->lfPitchAndFamily
= (afm
->IsFixedPitch
) ? FIXED_PITCH
: VARIABLE_PITCH
;
412 MultiByteToWideChar(CP_ACP
, 0, afm
->FamilyName
, -1, lf
->lfFaceName
,
415 return DEVICE_FONTTYPE
;
418 /***********************************************************************
419 * PSDRV_EnumDeviceFonts
421 BOOL
PSDRV_EnumDeviceFonts( PSDRV_PDEVICE
*physDev
, LPLOGFONTW plf
,
422 DEVICEFONTENUMPROC proc
, LPARAM lp
)
429 char FaceName
[LF_FACESIZE
];
431 if( plf
->lfFaceName
[0] ) {
432 WideCharToMultiByte(CP_ACP
, 0, plf
->lfFaceName
, -1,
433 FaceName
, sizeof(FaceName
), NULL
, NULL
);
434 TRACE("lfFaceName = '%s'\n", FaceName
);
435 for(family
= physDev
->pi
->Fonts
; family
; family
= family
->next
) {
436 if(!strncmp(FaceName
, family
->FamilyName
,
437 strlen(family
->FamilyName
)))
441 for(afmle
= family
->afmlist
; afmle
; afmle
= afmle
->next
) {
442 TRACE("Got '%s'\n", afmle
->afm
->FontName
);
443 if( (b
= proc( &lf
, &tm
, PSDRV_GetFontMetric(physDev
, afmle
->afm
, &tm
, &lf
), lp
)) )
450 TRACE("lfFaceName = NULL\n");
451 for(family
= physDev
->pi
->Fonts
; family
; family
= family
->next
) {
452 afmle
= family
->afmlist
;
453 TRACE("Got '%s'\n", afmle
->afm
->FontName
);
454 if( (b
= proc( &lf
, &tm
, PSDRV_GetFontMetric(physDev
, afmle
->afm
, &tm
, &lf
), lp
)) )