Removed some direct accesses to the DC structure.
[wine/multimedia.git] / dlls / wineps / font.c
blob2240c81dac1249e373695e16d9cbf11c24921ae6
1 /*
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
20 #include <string.h>
21 #include <stdlib.h> /* for bsearch() */
22 #include "winspool.h"
23 #include "gdi.h"
24 #include "psdrv.h"
25 #include "wine/debug.h"
26 #include "winerror.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
30 /***********************************************************************
31 * is_stock_font
33 inline static BOOL is_stock_font( HFONT font )
35 int i;
36 for (i = OEM_FIXED_FONT; i <= DEFAULT_GUI_FONT; i++)
38 if (i != DEFAULT_PALETTE && font == GetStockObject(i)) return TRUE;
40 return FALSE;
44 /*******************************************************************************
45 * ScaleFont
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,
56 TEXTMETRICW *tm)
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);
76 font->set = FALSE;
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;
107 tm->tmStruckOut = 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 */
120 tm->tmOverhang = 0;
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 )
146 LOGFONTW lf;
147 BOOL bd = FALSE, it = FALSE;
148 AFMLISTENTRY *afmle;
149 FONTFAMILY *family;
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,
156 lf.lfWeight);
158 if(lf.lfItalic)
159 it = TRUE;
160 if(lf.lfWeight > 550)
161 bd = TRUE;
162 WideCharToMultiByte(CP_ACP, 0, lf.lfFaceName, -1,
163 FaceName, sizeof(FaceName), NULL, NULL);
165 if(FaceName[0] == '\0') {
166 switch(lf.lfPitchAndFamily & 0xf0) {
167 case FF_DONTCARE:
168 break;
169 case FF_ROMAN:
170 case FF_SCRIPT:
171 strcpy(FaceName, "Times");
172 break;
173 case FF_SWISS:
174 strcpy(FaceName, "Helvetica");
175 break;
176 case FF_MODERN:
177 strcpy(FaceName, "Courier");
178 break;
179 case FF_DECORATIVE:
180 strcpy(FaceName, "Symbol");
181 break;
185 if(FaceName[0] == '\0') {
186 switch(lf.lfPitchAndFamily & 0x0f) {
187 case VARIABLE_PITCH:
188 strcpy(FaceName, "Times");
189 break;
190 default:
191 strcpy(FaceName, "Courier");
192 break;
196 if (physDev->pi->FontSubTableSize != 0)
198 DWORD i;
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) <
208 LF_FACESIZE)
209 strcpy (FaceName,
210 (LPSTR) physDev->pi->FontSubTable[i].pData);
211 else
212 WARN ("Facename '%s' is too long; ignoring substitution\n",
213 (LPSTR) physDev->pi->FontSubTable[i].pData);
214 break;
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))
224 break;
226 if(!family) {
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))
239 break;
242 /* If all else fails, use the first font defined for the printer */
243 if(!family)
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)) )
251 break;
253 if(!afmle)
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));
280 return TRUE;
283 /******************************************************************************
284 * PSDRV_UVMetrics
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)
296 AFMMETRICS key;
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)
306 UV |= 0xf000;
308 key.UV = UV;
310 needle = bsearch(&key, afm->Metrics, afm->NumofMetrics, sizeof(AFMMETRICS),
311 MetricsByUV);
313 if (needle == NULL)
315 WARN("No glyph for U+%.4lX in %s\n", UV, afm->FontName);
316 needle = afm->Metrics;
319 return needle;
322 /***********************************************************************
323 * PSDRV_GetTextExtentPoint
325 BOOL PSDRV_GetTextExtentPoint(PSDRV_PDEVICE *physDev, LPCWSTR str, INT count, LPSIZE size)
327 DC *dc = physDev->dc;
328 int i;
329 float width = 0.0;
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);
344 return TRUE;
347 /***********************************************************************
348 * PSDRV_GetCharWidth
350 BOOL PSDRV_GetCharWidth(PSDRV_PDEVICE *physDev, UINT firstChar, UINT lastChar, LPINT buffer)
352 UINT i;
354 TRACE("U+%.4X U+%.4X\n", firstChar, lastChar);
356 if (lastChar > 0xffff || firstChar > lastChar)
358 SetLastError(ERROR_INVALID_PARAMETER);
359 return FALSE;
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);
367 ++buffer;
370 return TRUE;
373 /***********************************************************************
374 * PSDRV_SetFont
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;
383 return 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);
397 PSFONT font;
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,
413 LF_FACESIZE);
415 return DEVICE_FONTTYPE;
418 /***********************************************************************
419 * PSDRV_EnumDeviceFonts
421 BOOL PSDRV_EnumDeviceFonts( PSDRV_PDEVICE *physDev, LPLOGFONTW plf,
422 DEVICEFONTENUMPROC proc, LPARAM lp )
424 ENUMLOGFONTEXW lf;
425 NEWTEXTMETRICEXW tm;
426 BOOL b, bRet = 0;
427 AFMLISTENTRY *afmle;
428 FONTFAMILY *family;
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)))
438 break;
440 if(family) {
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 )) )
444 bRet = b;
445 else break;
448 } else {
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 )) )
455 bRet = b;
456 else break;
459 return bRet;