Create GDI stock objects as normal objects instead of using magic
[wine] / dlls / wineps / font.c
1 /*
2  *      PostScript driver font functions
3  *
4  *      Copyright 1998  Huw D M Davies
5  *
6  */
7 #include <string.h>
8 #include <stdlib.h>         /* for bsearch() */
9 #include "winspool.h"
10 #include "psdrv.h"
11 #include "debugtools.h"
12 #include "winerror.h"
13
14 DEFAULT_DEBUG_CHANNEL(psdrv);
15
16 /***********************************************************************
17  *           is_stock_font
18  */
19 inline static BOOL is_stock_font( HFONT font )
20 {
21     int i;
22     for (i = OEM_FIXED_FONT; i <= DEFAULT_GUI_FONT; i++)
23     {
24         if (i != DEFAULT_PALETTE && font == GetStockObject(i)) return TRUE;
25     }
26     return FALSE;
27 }
28
29
30 /*******************************************************************************
31  *  ScaleFont
32  *
33  *  Scale font to requested lfHeight
34  *
35  */
36 inline static float round(float f)
37 {
38     return (f > 0) ? (f + 0.5) : (f - 0.5);
39 }
40
41 static VOID ScaleFont(const AFM *afm, LONG lfHeight, PSFONT *font,
42         TEXTMETRICW *tm)
43 {
44     const WINMETRICS    *wm = &(afm->WinMetrics);
45     USHORT              usUnitsPerEm, usWinAscent, usWinDescent;
46     SHORT               sAscender, sDescender, sLineGap, sTypoAscender;
47     SHORT               sTypoDescender, sTypoLineGap, sAvgCharWidth;
48     
49     TRACE("'%s' %li\n", afm->FontName, lfHeight);
50                 
51     if (lfHeight < 0)                                   /* match em height */
52     {
53         font->scale = - ((float)lfHeight / (float)(wm->usUnitsPerEm));
54     }
55     else                                                /* match cell height */
56     {
57         font->scale = (float)lfHeight /
58                 (float)(wm->usWinAscent + wm->usWinDescent);
59     }
60     
61     font->size = (INT)round(font->scale * (float)wm->usUnitsPerEm);
62     font->set = FALSE;
63     
64     usUnitsPerEm = (USHORT)round((float)(wm->usUnitsPerEm) * font->scale);
65     sAscender = (SHORT)round((float)(wm->sAscender) * font->scale);
66     sDescender = (SHORT)round((float)(wm->sDescender) * font->scale);
67     sLineGap = (SHORT)round((float)(wm->sLineGap) * font->scale);
68     sTypoAscender = (SHORT)round((float)(wm->sTypoAscender) * font->scale);
69     sTypoDescender = (SHORT)round((float)(wm->sTypoDescender) * font->scale);
70     sTypoLineGap = (SHORT)round((float)(wm->sTypoLineGap) * font->scale);
71     usWinAscent = (USHORT)round((float)(wm->usWinAscent) * font->scale);
72     usWinDescent = (USHORT)round((float)(wm->usWinDescent) * font->scale);
73     sAvgCharWidth = (SHORT)round((float)(wm->sAvgCharWidth) * font->scale);
74     
75     tm->tmAscent = (LONG)usWinAscent;
76     tm->tmDescent = (LONG)usWinDescent;
77     tm->tmHeight = tm->tmAscent + tm->tmDescent;
78     
79     tm->tmInternalLeading = tm->tmHeight - (LONG)usUnitsPerEm;
80     if (tm->tmInternalLeading < 0)
81         tm->tmInternalLeading = 0;
82         
83     tm->tmExternalLeading =
84             (LONG)(sAscender - sDescender + sLineGap) - tm->tmHeight;
85     if (tm->tmExternalLeading < 0)
86         tm->tmExternalLeading = 0;
87     
88     tm->tmAveCharWidth = (LONG)sAvgCharWidth;
89         
90     tm->tmWeight = afm->Weight;
91     tm->tmItalic = (afm->ItalicAngle != 0.0);
92     tm->tmUnderlined = 0;
93     tm->tmStruckOut = 0;
94     tm->tmFirstChar = (WCHAR)(afm->Metrics[0].UV);
95     tm->tmLastChar = (WCHAR)(afm->Metrics[afm->NumofMetrics - 1].UV);
96     tm->tmDefaultChar = 0x001f;         /* Win2K does this - FIXME? */
97     tm->tmBreakChar = tm->tmFirstChar;          /* should be 'space' */
98     
99     tm->tmPitchAndFamily = TMPF_DEVICE | TMPF_VECTOR;
100     if (!afm->IsFixedPitch)
101         tm->tmPitchAndFamily |= TMPF_FIXED_PITCH;   /* yes, it's backwards */
102     if (wm->usUnitsPerEm != 1000)
103         tm->tmPitchAndFamily |= TMPF_TRUETYPE;
104     
105     tm->tmCharSet = ANSI_CHARSET;       /* FIXME */
106     tm->tmOverhang = 0;
107     
108     /*
109      *  This is kludgy.  font->scale is used in several places in the driver
110      *  to adjust PostScript-style metrics.  Since these metrics have been
111      *  "normalized" to an em-square size of 1000, font->scale needs to be
112      *  similarly adjusted..
113      */
114      
115     font->scale *= (float)wm->usUnitsPerEm / 1000.0;
116      
117     tm->tmMaxCharWidth = (LONG)round(
118             (afm->FontBBox.urx - afm->FontBBox.llx) * font->scale);
119     
120     TRACE("Selected PS font '%s' size %d weight %ld.\n", afm->FontName,
121             font->size, tm->tmWeight );
122     TRACE("H = %ld As = %ld Des = %ld IL = %ld EL = %ld\n", tm->tmHeight,
123             tm->tmAscent, tm->tmDescent, tm->tmInternalLeading,
124             tm->tmExternalLeading);
125 }
126
127 /***********************************************************************
128  *           PSDRV_FONT_SelectObject
129  */
130 HFONT PSDRV_FONT_SelectObject( DC * dc, HFONT hfont )
131 {
132     LOGFONTW lf;
133     HFONT16 prevfont = dc->hFont;
134     PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
135     BOOL bd = FALSE, it = FALSE;
136     AFMLISTENTRY *afmle;
137     FONTFAMILY *family;
138     char FaceName[LF_FACESIZE];
139
140     if (!GetObjectW( hfont, sizeof(lf), &lf )) return 0;
141
142     TRACE("FaceName = %s Height = %ld Italic = %d Weight = %ld\n",
143           debugstr_w(lf.lfFaceName), lf.lfHeight, lf.lfItalic,
144           lf.lfWeight);
145
146     dc->hFont = hfont;
147
148     if(lf.lfItalic)
149         it = TRUE;
150     if(lf.lfWeight > 550)
151         bd = TRUE;
152     WideCharToMultiByte(CP_ACP, 0, lf.lfFaceName, -1,
153                         FaceName, sizeof(FaceName), NULL, NULL);
154     
155     if(FaceName[0] == '\0') {
156         switch(lf.lfPitchAndFamily & 0xf0) {
157         case FF_DONTCARE:
158             break;
159         case FF_ROMAN:
160         case FF_SCRIPT:
161             strcpy(FaceName, "Times");
162             break;
163         case FF_SWISS:
164             strcpy(FaceName, "Helvetica");
165             break;
166         case FF_MODERN:
167             strcpy(FaceName, "Courier");
168             break;
169         case FF_DECORATIVE:
170             strcpy(FaceName, "Symbol");
171             break;
172         }
173     }
174
175     if(FaceName[0] == '\0') {
176         switch(lf.lfPitchAndFamily & 0x0f) {
177         case VARIABLE_PITCH:
178             strcpy(FaceName, "Times");
179             break;
180         default:
181             strcpy(FaceName, "Courier");
182             break;
183         }
184     }
185
186     if (physDev->pi->FontSubTableSize != 0)
187     {
188         DWORD i;
189
190         for (i = 0; i < physDev->pi->FontSubTableSize; ++i)
191         {
192             if (!strcasecmp (FaceName,
193                     physDev->pi->FontSubTable[i].pValueName))
194             {
195                 TRACE ("substituting facename '%s' for '%s'\n",
196                         (LPSTR) physDev->pi->FontSubTable[i].pData, FaceName);
197                 if (strlen ((LPSTR) physDev->pi->FontSubTable[i].pData) <
198                         LF_FACESIZE)
199                     strcpy (FaceName,
200                             (LPSTR) physDev->pi->FontSubTable[i].pData);
201                 else
202                     WARN ("Facename '%s' is too long; ignoring substitution\n",
203                             (LPSTR) physDev->pi->FontSubTable[i].pData);
204                 break;
205             }
206         }
207     }
208
209     TRACE("Trying to find facename '%s'\n", FaceName);
210
211     /* Look for a matching font family */
212     for(family = physDev->pi->Fonts; family; family = family->next) {
213         if(!strcasecmp(FaceName, family->FamilyName))
214             break;
215     }
216     if(!family) {
217         /* Fallback for Window's font families to common PostScript families */
218         if(!strcmp(FaceName, "Arial"))
219             strcpy(FaceName, "Helvetica");
220         else if(!strcmp(FaceName, "System"))
221             strcpy(FaceName, "Helvetica");
222         else if(!strcmp(FaceName, "Times New Roman"))
223             strcpy(FaceName, "Times");
224         else if(!strcmp(FaceName, "Courier New"))
225             strcpy(FaceName, "Courier");
226
227         for(family = physDev->pi->Fonts; family; family = family->next) {
228             if(!strcmp(FaceName, family->FamilyName))
229                 break;
230         }
231     }
232     /* If all else fails, use the first font defined for the printer */
233     if(!family)
234         family = physDev->pi->Fonts;
235
236     TRACE("Got family '%s'\n", family->FamilyName);
237
238     for(afmle = family->afmlist; afmle; afmle = afmle->next) {
239         if( (bd == (afmle->afm->Weight == FW_BOLD)) && 
240             (it == (afmle->afm->ItalicAngle != 0.0)) )
241                 break;
242     }
243     if(!afmle)
244         afmle = family->afmlist; /* not ideal */
245         
246     TRACE("Got font '%s'\n", afmle->afm->FontName);
247     
248     physDev->font.afm = afmle->afm;
249     /* stock fonts ignore the mapping mode */
250     if (!is_stock_font( hfont )) lf.lfHeight = INTERNAL_YWSTODS(dc, lf.lfHeight);
251     ScaleFont(physDev->font.afm, lf.lfHeight,
252             &(physDev->font), &(physDev->font.tm));
253     
254     physDev->font.escapement = lf.lfEscapement;
255     
256     /* Does anyone know if these are supposed to be reversed like this? */
257     
258     physDev->font.tm.tmDigitizedAspectX = physDev->logPixelsY;
259     physDev->font.tm.tmDigitizedAspectY = physDev->logPixelsX;
260     
261     return prevfont;
262 }
263
264 /***********************************************************************
265  *           PSDRV_GetTextMetrics
266  */
267 BOOL PSDRV_GetTextMetrics(DC *dc, TEXTMETRICW *metrics)
268 {
269     PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
270
271     memcpy(metrics, &(physDev->font.tm), sizeof(physDev->font.tm));
272     return TRUE;
273 }
274
275 /******************************************************************************
276  *      PSDRV_UVMetrics
277  *
278  *  Find the AFMMETRICS for a given UV.  Returns first glyph in the font
279  *  (space?) if the font does not have a glyph for the given UV.
280  */
281 static int MetricsByUV(const void *a, const void *b)
282 {
283     return (int)(((const AFMMETRICS *)a)->UV - ((const AFMMETRICS *)b)->UV);
284 }
285  
286 const AFMMETRICS *PSDRV_UVMetrics(LONG UV, const AFM *afm)
287 {
288     AFMMETRICS          key;
289     const AFMMETRICS    *needle;
290     
291     /*
292      *  Ugly work-around for symbol fonts.  Wine is sending characters which
293      *  belong in the Unicode private use range (U+F020 - U+F0FF) as ASCII
294      *  characters (U+0020 - U+00FF).
295      */
296     
297     if ((afm->Metrics->UV & 0xff00) == 0xf000 && UV < 0x100)
298         UV |= 0xf000;
299     
300     key.UV = UV;
301     
302     needle = bsearch(&key, afm->Metrics, afm->NumofMetrics, sizeof(AFMMETRICS),
303             MetricsByUV);
304
305     if (needle == NULL)
306     {
307         WARN("No glyph for U+%.4lX in %s\n", UV, afm->FontName);
308         needle = afm->Metrics;
309     }
310         
311     return needle;
312 }
313
314 /***********************************************************************
315  *           PSDRV_GetTextExtentPoint
316  */
317 BOOL PSDRV_GetTextExtentPoint(DC *dc, LPCWSTR str, INT count, LPSIZE size)
318 {
319     PSDRV_PDEVICE   *physDev = (PSDRV_PDEVICE *)dc->physDev;
320     int             i;
321     float           width = 0.0;
322     
323     TRACE("%s %i\n", debugstr_wn(str, count), count);
324     
325     for (i = 0; i < count && str[i] != '\0'; ++i)
326         width += PSDRV_UVMetrics(str[i], physDev->font.afm)->WX;
327         
328     width *= physDev->font.scale;
329     
330     size->cx = GDI_ROUND((FLOAT)width * dc->xformVport2World.eM11);
331     size->cy = GDI_ROUND((FLOAT)physDev->font.tm.tmHeight *
332             dc->xformVport2World.eM22);
333             
334     TRACE("cx=%li cy=%li\n", size->cx, size->cy);
335             
336     return TRUE;
337 }
338
339 /***********************************************************************
340  *           PSDRV_GetCharWidth
341  */
342 BOOL PSDRV_GetCharWidth(DC *dc, UINT firstChar, UINT lastChar, LPINT buffer)
343 {
344     PSDRV_PDEVICE   *physDev = (PSDRV_PDEVICE *)dc->physDev;
345     UINT            i;
346     
347     TRACE("U+%.4X U+%.4X\n", firstChar, lastChar);
348     
349     if (lastChar > 0xffff || firstChar > lastChar)
350     {
351         SetLastError(ERROR_INVALID_PARAMETER);
352         return FALSE;
353     }
354         
355     for (i = firstChar; i <= lastChar; ++i)
356     {
357         *buffer = GDI_ROUND(PSDRV_UVMetrics(i, physDev->font.afm)->WX
358                 * physDev->font.scale);
359         TRACE("U+%.4X: %i\n", i, *buffer);
360         ++buffer;
361     }
362         
363     return TRUE;
364 }
365     
366 /***********************************************************************
367  *           PSDRV_SetFont
368  */
369 BOOL PSDRV_SetFont( DC *dc )
370 {
371     PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
372
373     PSDRV_WriteSetColor(dc, &physDev->font.color);
374     if(physDev->font.set) return TRUE;
375
376     PSDRV_WriteSetFont(dc);
377     physDev->font.set = TRUE;
378     return TRUE;
379 }
380
381
382 /***********************************************************************
383  *           PSDRV_GetFontMetric
384  */
385 static UINT PSDRV_GetFontMetric(HDC hdc, const AFM *afm,
386         NEWTEXTMETRICEXW *ntmx, ENUMLOGFONTEXW *elfx)
387 {
388     /* ntmx->ntmTm is NEWTEXTMETRICW; compatible w/ TEXTMETRICW per Win32 doc */
389
390     TEXTMETRICW     *tm = (TEXTMETRICW *)&(ntmx->ntmTm);
391     LOGFONTW        *lf = &(elfx->elfLogFont);
392     PSFONT          font;
393     
394     memset(ntmx, 0, sizeof(*ntmx));
395     memset(elfx, 0, sizeof(*elfx));
396     
397     ScaleFont(afm, -(LONG)(afm->WinMetrics.usUnitsPerEm), &font, tm);
398     
399     lf->lfHeight = tm->tmHeight;
400     lf->lfWidth = tm->tmAveCharWidth;
401     lf->lfWeight = tm->tmWeight;
402     lf->lfItalic = tm->tmItalic;
403     lf->lfCharSet = tm->tmCharSet;
404     
405     lf->lfPitchAndFamily = (afm->IsFixedPitch) ? FIXED_PITCH : VARIABLE_PITCH;
406     
407     MultiByteToWideChar(CP_ACP, 0, afm->FamilyName, -1, lf->lfFaceName,
408             LF_FACESIZE);
409             
410     return DEVICE_FONTTYPE;
411 }
412
413 /***********************************************************************
414  *           PSDRV_EnumDeviceFonts
415  */
416 BOOL PSDRV_EnumDeviceFonts( HDC hdc, LPLOGFONTW plf, 
417                             DEVICEFONTENUMPROC proc, LPARAM lp )
418 {
419     ENUMLOGFONTEXW      lf;
420     NEWTEXTMETRICEXW    tm;
421     BOOL                b, bRet = 0;
422     AFMLISTENTRY        *afmle;
423     FONTFAMILY          *family;
424     PSDRV_PDEVICE       *physDev;
425     char                FaceName[LF_FACESIZE];
426     DC *dc = DC_GetDCPtr( hdc );
427     if (!dc) return FALSE;
428
429     physDev = (PSDRV_PDEVICE *)dc->physDev;
430     /* FIXME!! should reevaluate dc->physDev after every callback */
431     GDI_ReleaseObj( hdc );
432
433     if( plf->lfFaceName[0] ) {
434         WideCharToMultiByte(CP_ACP, 0, plf->lfFaceName, -1,
435                           FaceName, sizeof(FaceName), NULL, NULL);
436         TRACE("lfFaceName = '%s'\n", FaceName);
437         for(family = physDev->pi->Fonts; family; family = family->next) {
438             if(!strncmp(FaceName, family->FamilyName, 
439                         strlen(family->FamilyName)))
440                 break;
441         }
442         if(family) {
443             for(afmle = family->afmlist; afmle; afmle = afmle->next) {
444                 TRACE("Got '%s'\n", afmle->afm->FontName);
445                 if( (b = (*proc)( &lf, &tm, 
446                         PSDRV_GetFontMetric( hdc, afmle->afm, &tm, &lf ),
447                                   lp )) )
448                      bRet = b;
449                 else break;
450             }
451         }
452     } else {
453
454         TRACE("lfFaceName = NULL\n");
455         for(family = physDev->pi->Fonts; family; family = family->next) {
456             afmle = family->afmlist;
457             TRACE("Got '%s'\n", afmle->afm->FontName);
458             if( (b = (*proc)( &lf, &tm, 
459                    PSDRV_GetFontMetric( hdc, afmle->afm, &tm, &lf ), 
460                               lp )) )
461                 bRet = b;
462             else break;
463         }
464     }
465     return bRet;
466 }