Update shell xxxAW wrapper prototypes for fixed SHLWAPI functions.
[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 "psdrv.h"
24 #include "wine/debug.h"
25 #include "winerror.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
28
29 /***********************************************************************
30  *           is_stock_font
31  */
32 inline static BOOL is_stock_font( HFONT font )
33 {
34     int i;
35     for (i = OEM_FIXED_FONT; i <= DEFAULT_GUI_FONT; i++)
36     {
37         if (i != DEFAULT_PALETTE && font == GetStockObject(i)) return TRUE;
38     }
39     return FALSE;
40 }
41
42
43 /*******************************************************************************
44  *  ScaleFont
45  *
46  *  Scale font to requested lfHeight
47  *
48  */
49 inline static float round(float f)
50 {
51     return (f > 0) ? (f + 0.5) : (f - 0.5);
52 }
53
54 static VOID ScaleFont(const AFM *afm, LONG lfHeight, PSFONT *font,
55         TEXTMETRICW *tm)
56 {
57     const WINMETRICS    *wm = &(afm->WinMetrics);
58     USHORT              usUnitsPerEm, usWinAscent, usWinDescent;
59     SHORT               sAscender, sDescender, sLineGap, sTypoAscender;
60     SHORT               sTypoDescender, sTypoLineGap, sAvgCharWidth;
61     
62     TRACE("'%s' %li\n", afm->FontName, lfHeight);
63                 
64     if (lfHeight < 0)                                   /* match em height */
65     {
66         font->scale = - ((float)lfHeight / (float)(wm->usUnitsPerEm));
67     }
68     else                                                /* match cell height */
69     {
70         font->scale = (float)lfHeight /
71                 (float)(wm->usWinAscent + wm->usWinDescent);
72     }
73     
74     font->size = (INT)round(font->scale * (float)wm->usUnitsPerEm);
75     font->set = FALSE;
76     
77     usUnitsPerEm = (USHORT)round((float)(wm->usUnitsPerEm) * font->scale);
78     sAscender = (SHORT)round((float)(wm->sAscender) * font->scale);
79     sDescender = (SHORT)round((float)(wm->sDescender) * font->scale);
80     sLineGap = (SHORT)round((float)(wm->sLineGap) * font->scale);
81     sTypoAscender = (SHORT)round((float)(wm->sTypoAscender) * font->scale);
82     sTypoDescender = (SHORT)round((float)(wm->sTypoDescender) * font->scale);
83     sTypoLineGap = (SHORT)round((float)(wm->sTypoLineGap) * font->scale);
84     usWinAscent = (USHORT)round((float)(wm->usWinAscent) * font->scale);
85     usWinDescent = (USHORT)round((float)(wm->usWinDescent) * font->scale);
86     sAvgCharWidth = (SHORT)round((float)(wm->sAvgCharWidth) * font->scale);
87     
88     tm->tmAscent = (LONG)usWinAscent;
89     tm->tmDescent = (LONG)usWinDescent;
90     tm->tmHeight = tm->tmAscent + tm->tmDescent;
91     
92     tm->tmInternalLeading = tm->tmHeight - (LONG)usUnitsPerEm;
93     if (tm->tmInternalLeading < 0)
94         tm->tmInternalLeading = 0;
95         
96     tm->tmExternalLeading =
97             (LONG)(sAscender - sDescender + sLineGap) - tm->tmHeight;
98     if (tm->tmExternalLeading < 0)
99         tm->tmExternalLeading = 0;
100     
101     tm->tmAveCharWidth = (LONG)sAvgCharWidth;
102         
103     tm->tmWeight = afm->Weight;
104     tm->tmItalic = (afm->ItalicAngle != 0.0);
105     tm->tmUnderlined = 0;
106     tm->tmStruckOut = 0;
107     tm->tmFirstChar = (WCHAR)(afm->Metrics[0].UV);
108     tm->tmLastChar = (WCHAR)(afm->Metrics[afm->NumofMetrics - 1].UV);
109     tm->tmDefaultChar = 0x001f;         /* Win2K does this - FIXME? */
110     tm->tmBreakChar = tm->tmFirstChar;          /* should be 'space' */
111     
112     tm->tmPitchAndFamily = TMPF_DEVICE | TMPF_VECTOR;
113     if (!afm->IsFixedPitch)
114         tm->tmPitchAndFamily |= TMPF_FIXED_PITCH;   /* yes, it's backwards */
115     if (wm->usUnitsPerEm != 1000)
116         tm->tmPitchAndFamily |= TMPF_TRUETYPE;
117     
118     tm->tmCharSet = ANSI_CHARSET;       /* FIXME */
119     tm->tmOverhang = 0;
120     
121     /*
122      *  This is kludgy.  font->scale is used in several places in the driver
123      *  to adjust PostScript-style metrics.  Since these metrics have been
124      *  "normalized" to an em-square size of 1000, font->scale needs to be
125      *  similarly adjusted..
126      */
127      
128     font->scale *= (float)wm->usUnitsPerEm / 1000.0;
129      
130     tm->tmMaxCharWidth = (LONG)round(
131             (afm->FontBBox.urx - afm->FontBBox.llx) * font->scale);
132     
133     TRACE("Selected PS font '%s' size %d weight %ld.\n", afm->FontName,
134             font->size, tm->tmWeight );
135     TRACE("H = %ld As = %ld Des = %ld IL = %ld EL = %ld\n", tm->tmHeight,
136             tm->tmAscent, tm->tmDescent, tm->tmInternalLeading,
137             tm->tmExternalLeading);
138 }
139
140 /***********************************************************************
141  *           PSDRV_FONT_SelectObject
142  */
143 HFONT PSDRV_FONT_SelectObject( DC * dc, HFONT hfont )
144 {
145     LOGFONTW lf;
146     PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
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(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(DC *dc, TEXTMETRICW *metrics)
278 {
279     PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
280
281     memcpy(metrics, &(physDev->font.tm), sizeof(physDev->font.tm));
282     return TRUE;
283 }
284
285 /******************************************************************************
286  *      PSDRV_UVMetrics
287  *
288  *  Find the AFMMETRICS for a given UV.  Returns first glyph in the font
289  *  (space?) if the font does not have a glyph for the given UV.
290  */
291 static int MetricsByUV(const void *a, const void *b)
292 {
293     return (int)(((const AFMMETRICS *)a)->UV - ((const AFMMETRICS *)b)->UV);
294 }
295  
296 const AFMMETRICS *PSDRV_UVMetrics(LONG UV, const AFM *afm)
297 {
298     AFMMETRICS          key;
299     const AFMMETRICS    *needle;
300     
301     /*
302      *  Ugly work-around for symbol fonts.  Wine is sending characters which
303      *  belong in the Unicode private use range (U+F020 - U+F0FF) as ASCII
304      *  characters (U+0020 - U+00FF).
305      */
306     
307     if ((afm->Metrics->UV & 0xff00) == 0xf000 && UV < 0x100)
308         UV |= 0xf000;
309     
310     key.UV = UV;
311     
312     needle = bsearch(&key, afm->Metrics, afm->NumofMetrics, sizeof(AFMMETRICS),
313             MetricsByUV);
314
315     if (needle == NULL)
316     {
317         WARN("No glyph for U+%.4lX in %s\n", UV, afm->FontName);
318         needle = afm->Metrics;
319     }
320         
321     return needle;
322 }
323
324 /***********************************************************************
325  *           PSDRV_GetTextExtentPoint
326  */
327 BOOL PSDRV_GetTextExtentPoint(DC *dc, LPCWSTR str, INT count, LPSIZE size)
328 {
329     PSDRV_PDEVICE   *physDev = (PSDRV_PDEVICE *)dc->physDev;
330     int             i;
331     float           width = 0.0;
332     
333     TRACE("%s %i\n", debugstr_wn(str, count), count);
334     
335     for (i = 0; i < count && str[i] != '\0'; ++i)
336         width += PSDRV_UVMetrics(str[i], physDev->font.afm)->WX;
337         
338     width *= physDev->font.scale;
339     
340     size->cx = GDI_ROUND((FLOAT)width * dc->xformVport2World.eM11);
341     size->cy = GDI_ROUND((FLOAT)physDev->font.tm.tmHeight *
342             dc->xformVport2World.eM22);
343             
344     TRACE("cx=%li cy=%li\n", size->cx, size->cy);
345             
346     return TRUE;
347 }
348
349 /***********************************************************************
350  *           PSDRV_GetCharWidth
351  */
352 BOOL PSDRV_GetCharWidth(DC *dc, UINT firstChar, UINT lastChar, LPINT buffer)
353 {
354     PSDRV_PDEVICE   *physDev = (PSDRV_PDEVICE *)dc->physDev;
355     UINT            i;
356     
357     TRACE("U+%.4X U+%.4X\n", firstChar, lastChar);
358     
359     if (lastChar > 0xffff || firstChar > lastChar)
360     {
361         SetLastError(ERROR_INVALID_PARAMETER);
362         return FALSE;
363     }
364         
365     for (i = firstChar; i <= lastChar; ++i)
366     {
367         *buffer = GDI_ROUND(PSDRV_UVMetrics(i, physDev->font.afm)->WX
368                 * physDev->font.scale);
369         TRACE("U+%.4X: %i\n", i, *buffer);
370         ++buffer;
371     }
372         
373     return TRUE;
374 }
375     
376 /***********************************************************************
377  *           PSDRV_SetFont
378  */
379 BOOL PSDRV_SetFont( DC *dc )
380 {
381     PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
382
383     PSDRV_WriteSetColor(dc, &physDev->font.color);
384     if(physDev->font.set) return TRUE;
385
386     PSDRV_WriteSetFont(dc);
387     physDev->font.set = TRUE;
388     return TRUE;
389 }
390
391
392 /***********************************************************************
393  *           PSDRV_GetFontMetric
394  */
395 static UINT PSDRV_GetFontMetric(HDC hdc, const AFM *afm,
396         NEWTEXTMETRICEXW *ntmx, ENUMLOGFONTEXW *elfx)
397 {
398     /* ntmx->ntmTm is NEWTEXTMETRICW; compatible w/ TEXTMETRICW per Win32 doc */
399
400     TEXTMETRICW     *tm = (TEXTMETRICW *)&(ntmx->ntmTm);
401     LOGFONTW        *lf = &(elfx->elfLogFont);
402     PSFONT          font;
403     
404     memset(ntmx, 0, sizeof(*ntmx));
405     memset(elfx, 0, sizeof(*elfx));
406     
407     ScaleFont(afm, -(LONG)(afm->WinMetrics.usUnitsPerEm), &font, tm);
408     
409     lf->lfHeight = tm->tmHeight;
410     lf->lfWidth = tm->tmAveCharWidth;
411     lf->lfWeight = tm->tmWeight;
412     lf->lfItalic = tm->tmItalic;
413     lf->lfCharSet = tm->tmCharSet;
414     
415     lf->lfPitchAndFamily = (afm->IsFixedPitch) ? FIXED_PITCH : VARIABLE_PITCH;
416     
417     MultiByteToWideChar(CP_ACP, 0, afm->FamilyName, -1, lf->lfFaceName,
418             LF_FACESIZE);
419             
420     return DEVICE_FONTTYPE;
421 }
422
423 /***********************************************************************
424  *           PSDRV_EnumDeviceFonts
425  */
426 BOOL PSDRV_EnumDeviceFonts( HDC hdc, LPLOGFONTW plf, 
427                             DEVICEFONTENUMPROC proc, LPARAM lp )
428 {
429     ENUMLOGFONTEXW      lf;
430     NEWTEXTMETRICEXW    tm;
431     BOOL                b, bRet = 0;
432     AFMLISTENTRY        *afmle;
433     FONTFAMILY          *family;
434     PSDRV_PDEVICE       *physDev;
435     char                FaceName[LF_FACESIZE];
436     DC *dc = DC_GetDCPtr( hdc );
437     if (!dc) return FALSE;
438
439     physDev = (PSDRV_PDEVICE *)dc->physDev;
440     /* FIXME!! should reevaluate dc->physDev after every callback */
441     GDI_ReleaseObj( hdc );
442
443     if( plf->lfFaceName[0] ) {
444         WideCharToMultiByte(CP_ACP, 0, plf->lfFaceName, -1,
445                           FaceName, sizeof(FaceName), NULL, NULL);
446         TRACE("lfFaceName = '%s'\n", FaceName);
447         for(family = physDev->pi->Fonts; family; family = family->next) {
448             if(!strncmp(FaceName, family->FamilyName, 
449                         strlen(family->FamilyName)))
450                 break;
451         }
452         if(family) {
453             for(afmle = family->afmlist; afmle; afmle = afmle->next) {
454                 TRACE("Got '%s'\n", afmle->afm->FontName);
455                 if( (b = (*proc)( &lf, &tm, 
456                         PSDRV_GetFontMetric( hdc, afmle->afm, &tm, &lf ),
457                                   lp )) )
458                      bRet = b;
459                 else break;
460             }
461         }
462     } else {
463
464         TRACE("lfFaceName = NULL\n");
465         for(family = physDev->pi->Fonts; family; family = family->next) {
466             afmle = family->afmlist;
467             TRACE("Got '%s'\n", afmle->afm->FontName);
468             if( (b = (*proc)( &lf, &tm, 
469                    PSDRV_GetFontMetric( hdc, afmle->afm, &tm, &lf ), 
470                               lp )) )
471                 bRet = b;
472             else break;
473         }
474     }
475     return bRet;
476 }