When including 'wine/port.h', include it first.
[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     PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
134     BOOL bd = FALSE, it = FALSE;
135     AFMLISTENTRY *afmle;
136     FONTFAMILY *family;
137     char FaceName[LF_FACESIZE];
138
139     if (!GetObjectW( hfont, sizeof(lf), &lf )) return 0;
140
141     TRACE("FaceName = %s Height = %ld Italic = %d Weight = %ld\n",
142           debugstr_w(lf.lfFaceName), lf.lfHeight, lf.lfItalic,
143           lf.lfWeight);
144
145     if(lf.lfItalic)
146         it = TRUE;
147     if(lf.lfWeight > 550)
148         bd = TRUE;
149     WideCharToMultiByte(CP_ACP, 0, lf.lfFaceName, -1,
150                         FaceName, sizeof(FaceName), NULL, NULL);
151     
152     if(FaceName[0] == '\0') {
153         switch(lf.lfPitchAndFamily & 0xf0) {
154         case FF_DONTCARE:
155             break;
156         case FF_ROMAN:
157         case FF_SCRIPT:
158             strcpy(FaceName, "Times");
159             break;
160         case FF_SWISS:
161             strcpy(FaceName, "Helvetica");
162             break;
163         case FF_MODERN:
164             strcpy(FaceName, "Courier");
165             break;
166         case FF_DECORATIVE:
167             strcpy(FaceName, "Symbol");
168             break;
169         }
170     }
171
172     if(FaceName[0] == '\0') {
173         switch(lf.lfPitchAndFamily & 0x0f) {
174         case VARIABLE_PITCH:
175             strcpy(FaceName, "Times");
176             break;
177         default:
178             strcpy(FaceName, "Courier");
179             break;
180         }
181     }
182
183     if (physDev->pi->FontSubTableSize != 0)
184     {
185         DWORD i;
186
187         for (i = 0; i < physDev->pi->FontSubTableSize; ++i)
188         {
189             if (!strcasecmp (FaceName,
190                     physDev->pi->FontSubTable[i].pValueName))
191             {
192                 TRACE ("substituting facename '%s' for '%s'\n",
193                         (LPSTR) physDev->pi->FontSubTable[i].pData, FaceName);
194                 if (strlen ((LPSTR) physDev->pi->FontSubTable[i].pData) <
195                         LF_FACESIZE)
196                     strcpy (FaceName,
197                             (LPSTR) physDev->pi->FontSubTable[i].pData);
198                 else
199                     WARN ("Facename '%s' is too long; ignoring substitution\n",
200                             (LPSTR) physDev->pi->FontSubTable[i].pData);
201                 break;
202             }
203         }
204     }
205
206     TRACE("Trying to find facename '%s'\n", FaceName);
207
208     /* Look for a matching font family */
209     for(family = physDev->pi->Fonts; family; family = family->next) {
210         if(!strcasecmp(FaceName, family->FamilyName))
211             break;
212     }
213     if(!family) {
214         /* Fallback for Window's font families to common PostScript families */
215         if(!strcmp(FaceName, "Arial"))
216             strcpy(FaceName, "Helvetica");
217         else if(!strcmp(FaceName, "System"))
218             strcpy(FaceName, "Helvetica");
219         else if(!strcmp(FaceName, "Times New Roman"))
220             strcpy(FaceName, "Times");
221         else if(!strcmp(FaceName, "Courier New"))
222             strcpy(FaceName, "Courier");
223
224         for(family = physDev->pi->Fonts; family; family = family->next) {
225             if(!strcmp(FaceName, family->FamilyName))
226                 break;
227         }
228     }
229     /* If all else fails, use the first font defined for the printer */
230     if(!family)
231         family = physDev->pi->Fonts;
232
233     TRACE("Got family '%s'\n", family->FamilyName);
234
235     for(afmle = family->afmlist; afmle; afmle = afmle->next) {
236         if( (bd == (afmle->afm->Weight == FW_BOLD)) && 
237             (it == (afmle->afm->ItalicAngle != 0.0)) )
238                 break;
239     }
240     if(!afmle)
241         afmle = family->afmlist; /* not ideal */
242         
243     TRACE("Got font '%s'\n", afmle->afm->FontName);
244     
245     physDev->font.afm = afmle->afm;
246     /* stock fonts ignore the mapping mode */
247     if (!is_stock_font( hfont )) lf.lfHeight = INTERNAL_YWSTODS(dc, lf.lfHeight);
248     ScaleFont(physDev->font.afm, lf.lfHeight,
249             &(physDev->font), &(physDev->font.tm));
250     
251     physDev->font.escapement = lf.lfEscapement;
252     
253     /* Does anyone know if these are supposed to be reversed like this? */
254     
255     physDev->font.tm.tmDigitizedAspectX = physDev->logPixelsY;
256     physDev->font.tm.tmDigitizedAspectY = physDev->logPixelsX;
257     
258     return TRUE; /* We'll use a device font for now */
259 }
260
261 /***********************************************************************
262  *           PSDRV_GetTextMetrics
263  */
264 BOOL PSDRV_GetTextMetrics(DC *dc, TEXTMETRICW *metrics)
265 {
266     PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
267
268     memcpy(metrics, &(physDev->font.tm), sizeof(physDev->font.tm));
269     return TRUE;
270 }
271
272 /******************************************************************************
273  *      PSDRV_UVMetrics
274  *
275  *  Find the AFMMETRICS for a given UV.  Returns first glyph in the font
276  *  (space?) if the font does not have a glyph for the given UV.
277  */
278 static int MetricsByUV(const void *a, const void *b)
279 {
280     return (int)(((const AFMMETRICS *)a)->UV - ((const AFMMETRICS *)b)->UV);
281 }
282  
283 const AFMMETRICS *PSDRV_UVMetrics(LONG UV, const AFM *afm)
284 {
285     AFMMETRICS          key;
286     const AFMMETRICS    *needle;
287     
288     /*
289      *  Ugly work-around for symbol fonts.  Wine is sending characters which
290      *  belong in the Unicode private use range (U+F020 - U+F0FF) as ASCII
291      *  characters (U+0020 - U+00FF).
292      */
293     
294     if ((afm->Metrics->UV & 0xff00) == 0xf000 && UV < 0x100)
295         UV |= 0xf000;
296     
297     key.UV = UV;
298     
299     needle = bsearch(&key, afm->Metrics, afm->NumofMetrics, sizeof(AFMMETRICS),
300             MetricsByUV);
301
302     if (needle == NULL)
303     {
304         WARN("No glyph for U+%.4lX in %s\n", UV, afm->FontName);
305         needle = afm->Metrics;
306     }
307         
308     return needle;
309 }
310
311 /***********************************************************************
312  *           PSDRV_GetTextExtentPoint
313  */
314 BOOL PSDRV_GetTextExtentPoint(DC *dc, LPCWSTR str, INT count, LPSIZE size)
315 {
316     PSDRV_PDEVICE   *physDev = (PSDRV_PDEVICE *)dc->physDev;
317     int             i;
318     float           width = 0.0;
319     
320     TRACE("%s %i\n", debugstr_wn(str, count), count);
321     
322     for (i = 0; i < count && str[i] != '\0'; ++i)
323         width += PSDRV_UVMetrics(str[i], physDev->font.afm)->WX;
324         
325     width *= physDev->font.scale;
326     
327     size->cx = GDI_ROUND((FLOAT)width * dc->xformVport2World.eM11);
328     size->cy = GDI_ROUND((FLOAT)physDev->font.tm.tmHeight *
329             dc->xformVport2World.eM22);
330             
331     TRACE("cx=%li cy=%li\n", size->cx, size->cy);
332             
333     return TRUE;
334 }
335
336 /***********************************************************************
337  *           PSDRV_GetCharWidth
338  */
339 BOOL PSDRV_GetCharWidth(DC *dc, UINT firstChar, UINT lastChar, LPINT buffer)
340 {
341     PSDRV_PDEVICE   *physDev = (PSDRV_PDEVICE *)dc->physDev;
342     UINT            i;
343     
344     TRACE("U+%.4X U+%.4X\n", firstChar, lastChar);
345     
346     if (lastChar > 0xffff || firstChar > lastChar)
347     {
348         SetLastError(ERROR_INVALID_PARAMETER);
349         return FALSE;
350     }
351         
352     for (i = firstChar; i <= lastChar; ++i)
353     {
354         *buffer = GDI_ROUND(PSDRV_UVMetrics(i, physDev->font.afm)->WX
355                 * physDev->font.scale);
356         TRACE("U+%.4X: %i\n", i, *buffer);
357         ++buffer;
358     }
359         
360     return TRUE;
361 }
362     
363 /***********************************************************************
364  *           PSDRV_SetFont
365  */
366 BOOL PSDRV_SetFont( DC *dc )
367 {
368     PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
369
370     PSDRV_WriteSetColor(dc, &physDev->font.color);
371     if(physDev->font.set) return TRUE;
372
373     PSDRV_WriteSetFont(dc);
374     physDev->font.set = TRUE;
375     return TRUE;
376 }
377
378
379 /***********************************************************************
380  *           PSDRV_GetFontMetric
381  */
382 static UINT PSDRV_GetFontMetric(HDC hdc, const AFM *afm,
383         NEWTEXTMETRICEXW *ntmx, ENUMLOGFONTEXW *elfx)
384 {
385     /* ntmx->ntmTm is NEWTEXTMETRICW; compatible w/ TEXTMETRICW per Win32 doc */
386
387     TEXTMETRICW     *tm = (TEXTMETRICW *)&(ntmx->ntmTm);
388     LOGFONTW        *lf = &(elfx->elfLogFont);
389     PSFONT          font;
390     
391     memset(ntmx, 0, sizeof(*ntmx));
392     memset(elfx, 0, sizeof(*elfx));
393     
394     ScaleFont(afm, -(LONG)(afm->WinMetrics.usUnitsPerEm), &font, tm);
395     
396     lf->lfHeight = tm->tmHeight;
397     lf->lfWidth = tm->tmAveCharWidth;
398     lf->lfWeight = tm->tmWeight;
399     lf->lfItalic = tm->tmItalic;
400     lf->lfCharSet = tm->tmCharSet;
401     
402     lf->lfPitchAndFamily = (afm->IsFixedPitch) ? FIXED_PITCH : VARIABLE_PITCH;
403     
404     MultiByteToWideChar(CP_ACP, 0, afm->FamilyName, -1, lf->lfFaceName,
405             LF_FACESIZE);
406             
407     return DEVICE_FONTTYPE;
408 }
409
410 /***********************************************************************
411  *           PSDRV_EnumDeviceFonts
412  */
413 BOOL PSDRV_EnumDeviceFonts( HDC hdc, LPLOGFONTW plf, 
414                             DEVICEFONTENUMPROC proc, LPARAM lp )
415 {
416     ENUMLOGFONTEXW      lf;
417     NEWTEXTMETRICEXW    tm;
418     BOOL                b, bRet = 0;
419     AFMLISTENTRY        *afmle;
420     FONTFAMILY          *family;
421     PSDRV_PDEVICE       *physDev;
422     char                FaceName[LF_FACESIZE];
423     DC *dc = DC_GetDCPtr( hdc );
424     if (!dc) return FALSE;
425
426     physDev = (PSDRV_PDEVICE *)dc->physDev;
427     /* FIXME!! should reevaluate dc->physDev after every callback */
428     GDI_ReleaseObj( hdc );
429
430     if( plf->lfFaceName[0] ) {
431         WideCharToMultiByte(CP_ACP, 0, plf->lfFaceName, -1,
432                           FaceName, sizeof(FaceName), NULL, NULL);
433         TRACE("lfFaceName = '%s'\n", FaceName);
434         for(family = physDev->pi->Fonts; family; family = family->next) {
435             if(!strncmp(FaceName, family->FamilyName, 
436                         strlen(family->FamilyName)))
437                 break;
438         }
439         if(family) {
440             for(afmle = family->afmlist; afmle; afmle = afmle->next) {
441                 TRACE("Got '%s'\n", afmle->afm->FontName);
442                 if( (b = (*proc)( &lf, &tm, 
443                         PSDRV_GetFontMetric( hdc, afmle->afm, &tm, &lf ),
444                                   lp )) )
445                      bRet = b;
446                 else break;
447             }
448         }
449     } else {
450
451         TRACE("lfFaceName = NULL\n");
452         for(family = physDev->pi->Fonts; family; family = family->next) {
453             afmle = family->afmlist;
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     return bRet;
463 }