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