Added regedit unit test, a couple minor changes to regedit.
[wine] / dlls / wineps / font.c
1 /*
2  *      PostScript driver font functions
3  *
4  *      Copyright 1998  Huw D M Davies
5  *
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.
10  *
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.
15  *
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
19  */
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"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
29
30 /***********************************************************************
31  *           is_stock_font
32  */
33 inline static BOOL is_stock_font( HFONT font )
34 {
35     int i;
36     for (i = OEM_FIXED_FONT; i <= DEFAULT_GUI_FONT; i++)
37     {
38         if (i != DEFAULT_PALETTE && font == GetStockObject(i)) return TRUE;
39     }
40     return FALSE;
41 }
42
43
44 /*******************************************************************************
45  *  ScaleFont
46  *
47  *  Scale font to requested lfHeight
48  *
49  */
50 inline static float round(float f)
51 {
52     return (f > 0) ? (f + 0.5) : (f - 0.5);
53 }
54
55 static VOID ScaleFont(const AFM *afm, LONG lfHeight, PSFONT *font,
56         TEXTMETRICW *tm)
57 {
58     const WINMETRICS    *wm = &(afm->WinMetrics);
59     USHORT              usUnitsPerEm, usWinAscent, usWinDescent;
60     SHORT               sAscender, sDescender, sLineGap, sTypoAscender;
61     SHORT               sTypoDescender, sTypoLineGap, sAvgCharWidth;
62
63     TRACE("'%s' %li\n", afm->FontName, lfHeight);
64
65     if (lfHeight < 0)                                   /* match em height */
66     {
67         font->scale = - ((float)lfHeight / (float)(wm->usUnitsPerEm));
68     }
69     else                                                /* match cell height */
70     {
71         font->scale = (float)lfHeight /
72                 (float)(wm->usWinAscent + wm->usWinDescent);
73     }
74
75     font->size = (INT)round(font->scale * (float)wm->usUnitsPerEm);
76     font->set = FALSE;
77
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);
88
89     tm->tmAscent = (LONG)usWinAscent;
90     tm->tmDescent = (LONG)usWinDescent;
91     tm->tmHeight = tm->tmAscent + tm->tmDescent;
92
93     tm->tmInternalLeading = tm->tmHeight - (LONG)usUnitsPerEm;
94     if (tm->tmInternalLeading < 0)
95         tm->tmInternalLeading = 0;
96
97     tm->tmExternalLeading =
98             (LONG)(sAscender - sDescender + sLineGap) - tm->tmHeight;
99     if (tm->tmExternalLeading < 0)
100         tm->tmExternalLeading = 0;
101
102     tm->tmAveCharWidth = (LONG)sAvgCharWidth;
103
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' */
112
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;
118
119     tm->tmCharSet = ANSI_CHARSET;       /* FIXME */
120     tm->tmOverhang = 0;
121
122     /*
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..
127      */
128
129     font->scale *= (float)wm->usUnitsPerEm / 1000.0;
130
131     tm->tmMaxCharWidth = (LONG)round(
132             (afm->FontBBox.urx - afm->FontBBox.llx) * font->scale);
133
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);
139 }
140
141 /***********************************************************************
142  *           SelectFont   (WINEPS.@)
143  */
144 HFONT PSDRV_SelectFont( PSDRV_PDEVICE *physDev, HFONT hfont )
145 {
146     LOGFONTW lf;
147     BOOL bd = FALSE, it = FALSE;
148     AFMLISTENTRY *afmle;
149     FONTFAMILY *family;
150     char FaceName[LF_FACESIZE];
151
152     if (!GetObjectW( hfont, sizeof(lf), &lf )) return 0;
153
154     TRACE("FaceName = %s Height = %ld Italic = %d Weight = %ld\n",
155           debugstr_w(lf.lfFaceName), lf.lfHeight, lf.lfItalic,
156           lf.lfWeight);
157
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);
164
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;
182         }
183     }
184
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;
193         }
194     }
195
196     if (physDev->pi->FontSubTableSize != 0)
197     {
198         DWORD i;
199
200         for (i = 0; i < physDev->pi->FontSubTableSize; ++i)
201         {
202             if (!strcasecmp (FaceName,
203                     physDev->pi->FontSubTable[i].pValueName))
204             {
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;
215             }
216         }
217     }
218
219     TRACE("Trying to find facename '%s'\n", FaceName);
220
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;
225     }
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");
236
237         for(family = physDev->pi->Fonts; family; family = family->next) {
238             if(!strcmp(FaceName, family->FamilyName))
239                 break;
240         }
241     }
242     /* If all else fails, use the first font defined for the printer */
243     if(!family)
244         family = physDev->pi->Fonts;
245
246     TRACE("Got family '%s'\n", family->FamilyName);
247
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;
252     }
253     if(!afmle)
254         afmle = family->afmlist; /* not ideal */
255
256     TRACE("Got font '%s'\n", afmle->afm->FontName);
257
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));
263
264     physDev->font.escapement = lf.lfEscapement;
265
266     /* Does anyone know if these are supposed to be reversed like this? */
267
268     physDev->font.tm.tmDigitizedAspectX = physDev->logPixelsY;
269     physDev->font.tm.tmDigitizedAspectY = physDev->logPixelsX;
270
271     return TRUE; /* We'll use a device font for now */
272 }
273
274 /***********************************************************************
275  *           PSDRV_GetTextMetrics
276  */
277 BOOL PSDRV_GetTextMetrics(PSDRV_PDEVICE *physDev, TEXTMETRICW *metrics)
278 {
279     memcpy(metrics, &(physDev->font.tm), sizeof(physDev->font.tm));
280     return TRUE;
281 }
282
283 /******************************************************************************
284  *      PSDRV_UVMetrics
285  *
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.
288  */
289 static int MetricsByUV(const void *a, const void *b)
290 {
291     return (int)(((const AFMMETRICS *)a)->UV - ((const AFMMETRICS *)b)->UV);
292 }
293
294 const AFMMETRICS *PSDRV_UVMetrics(LONG UV, const AFM *afm)
295 {
296     AFMMETRICS          key;
297     const AFMMETRICS    *needle;
298
299     /*
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).
303      */
304
305     if ((afm->Metrics->UV & 0xff00) == 0xf000 && UV < 0x100)
306         UV |= 0xf000;
307
308     key.UV = UV;
309
310     needle = bsearch(&key, afm->Metrics, afm->NumofMetrics, sizeof(AFMMETRICS),
311             MetricsByUV);
312
313     if (needle == NULL)
314     {
315         WARN("No glyph for U+%.4lX in %s\n", UV, afm->FontName);
316         needle = afm->Metrics;
317     }
318
319     return needle;
320 }
321
322 /***********************************************************************
323  *           PSDRV_GetTextExtentPoint
324  */
325 BOOL PSDRV_GetTextExtentPoint(PSDRV_PDEVICE *physDev, LPCWSTR str, INT count, LPSIZE size)
326 {
327     DC *dc = physDev->dc;
328     int             i;
329     float           width = 0.0;
330
331     TRACE("%s %i\n", debugstr_wn(str, count), count);
332
333     for (i = 0; i < count && str[i] != '\0'; ++i)
334         width += PSDRV_UVMetrics(str[i], physDev->font.afm)->WX;
335
336     width *= physDev->font.scale;
337
338     size->cx = GDI_ROUND((FLOAT)width * dc->xformVport2World.eM11);
339     size->cy = GDI_ROUND((FLOAT)physDev->font.tm.tmHeight *
340             dc->xformVport2World.eM22);
341
342     TRACE("cx=%li cy=%li\n", size->cx, size->cy);
343
344     return TRUE;
345 }
346
347 /***********************************************************************
348  *           PSDRV_GetCharWidth
349  */
350 BOOL PSDRV_GetCharWidth(PSDRV_PDEVICE *physDev, UINT firstChar, UINT lastChar, LPINT buffer)
351 {
352     UINT            i;
353
354     TRACE("U+%.4X U+%.4X\n", firstChar, lastChar);
355
356     if (lastChar > 0xffff || firstChar > lastChar)
357     {
358         SetLastError(ERROR_INVALID_PARAMETER);
359         return FALSE;
360     }
361
362     for (i = firstChar; i <= lastChar; ++i)
363     {
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;
368     }
369
370     return TRUE;
371 }
372
373 /***********************************************************************
374  *           PSDRV_SetFont
375  */
376 BOOL PSDRV_SetFont( PSDRV_PDEVICE *physDev )
377 {
378     PSDRV_WriteSetColor(physDev, &physDev->font.color);
379     if(physDev->font.set) return TRUE;
380
381     PSDRV_WriteSetFont(physDev);
382     physDev->font.set = TRUE;
383     return TRUE;
384 }
385
386
387 /***********************************************************************
388  *           PSDRV_GetFontMetric
389  */
390 static UINT PSDRV_GetFontMetric( PSDRV_PDEVICE *physDev, const AFM *afm,
391                                  NEWTEXTMETRICEXW *ntmx, ENUMLOGFONTEXW *elfx)
392 {
393     /* ntmx->ntmTm is NEWTEXTMETRICW; compatible w/ TEXTMETRICW per Win32 doc */
394
395     TEXTMETRICW     *tm = (TEXTMETRICW *)&(ntmx->ntmTm);
396     LOGFONTW        *lf = &(elfx->elfLogFont);
397     PSFONT          font;
398
399     memset(ntmx, 0, sizeof(*ntmx));
400     memset(elfx, 0, sizeof(*elfx));
401
402     ScaleFont(afm, -(LONG)(afm->WinMetrics.usUnitsPerEm), &font, tm);
403
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;
409
410     lf->lfPitchAndFamily = (afm->IsFixedPitch) ? FIXED_PITCH : VARIABLE_PITCH;
411
412     MultiByteToWideChar(CP_ACP, 0, afm->FamilyName, -1, lf->lfFaceName,
413             LF_FACESIZE);
414
415     return DEVICE_FONTTYPE;
416 }
417
418 /***********************************************************************
419  *           PSDRV_EnumDeviceFonts
420  */
421 BOOL PSDRV_EnumDeviceFonts( PSDRV_PDEVICE *physDev, LPLOGFONTW plf,
422                             DEVICEFONTENUMPROC proc, LPARAM lp )
423 {
424     ENUMLOGFONTEXW      lf;
425     NEWTEXTMETRICEXW    tm;
426     BOOL                b, bRet = 0;
427     AFMLISTENTRY        *afmle;
428     FONTFAMILY          *family;
429     char                FaceName[LF_FACESIZE];
430
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;
439         }
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;
446             }
447         }
448     } else {
449
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;
457         }
458     }
459     return bRet;
460 }