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