Reimplemented DXGrab with improvements; it no longer depends on
[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 "winspool.h"
9 #include "psdrv.h"
10 #include "debugtools.h"
11 #include "gdi.h"
12 #include "winerror.h"
13
14 DEFAULT_DEBUG_CHANNEL(psdrv);
15
16
17 /***********************************************************************
18  *           PSDRV_FONT_SelectObject
19  */
20 HFONT16 PSDRV_FONT_SelectObject( DC * dc, HFONT16 hfont,
21                                  FONTOBJ *font )
22 {
23     HFONT16 prevfont = dc->hFont;
24     PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
25     LOGFONTW *lf = &(font->logfont);
26     BOOL bd = FALSE, it = FALSE;
27     AFMLISTENTRY *afmle;
28     AFM *afm;
29     FONTFAMILY *family;
30     char FaceName[LF_FACESIZE];
31
32
33     TRACE("FaceName = '%s' Height = %ld Italic = %d Weight = %ld\n",
34           debugstr_w(lf->lfFaceName), lf->lfHeight, lf->lfItalic,
35           lf->lfWeight);
36
37     dc->hFont = hfont;
38
39     if(lf->lfItalic)
40         it = TRUE;
41     if(lf->lfWeight > 550)
42         bd = TRUE;
43     WideCharToMultiByte(CP_ACP, 0, lf->lfFaceName, -1,
44                         FaceName, sizeof(FaceName), NULL, NULL);
45     
46     if(FaceName[0] == '\0') {
47         switch(lf->lfPitchAndFamily & 0xf0) {
48         case FF_DONTCARE:
49             break;
50         case FF_ROMAN:
51         case FF_SCRIPT:
52             strcpy(FaceName, "Times");
53             break;
54         case FF_SWISS:
55             strcpy(FaceName, "Helvetica");
56             break;
57         case FF_MODERN:
58             strcpy(FaceName, "Courier");
59             break;
60         case FF_DECORATIVE:
61             strcpy(FaceName, "Symbol");
62             break;
63         }
64     }
65
66     if(FaceName[0] == '\0') {
67         switch(lf->lfPitchAndFamily & 0x0f) {
68         case VARIABLE_PITCH:
69             strcpy(FaceName, "Times");
70             break;
71         default:
72             strcpy(FaceName, "Courier");
73             break;
74         }
75     }
76
77     if (physDev->pi->FontSubTableSize != 0)
78     {
79         DWORD i;
80
81         for (i = 0; i < physDev->pi->FontSubTableSize; ++i)
82         {
83             if (!strcasecmp (FaceName,
84                     physDev->pi->FontSubTable[i].pValueName))
85             {
86                 TRACE ("substituting facename '%s' for '%s'\n",
87                         (LPSTR) physDev->pi->FontSubTable[i].pData, FaceName);
88                 if (strlen ((LPSTR) physDev->pi->FontSubTable[i].pData) <
89                         LF_FACESIZE)
90                     strcpy (FaceName,
91                             (LPSTR) physDev->pi->FontSubTable[i].pData);
92                 else
93                     WARN ("Facename '%s' is too long; ignoring substitution\n",
94                             (LPSTR) physDev->pi->FontSubTable[i].pData);
95                 break;
96             }
97         }
98     }
99
100     TRACE("Trying to find facename '%s'\n", FaceName);
101
102     /* Look for a matching font family */
103     for(family = physDev->pi->Fonts; family; family = family->next) {
104         if(!strcasecmp(FaceName, family->FamilyName))
105             break;
106     }
107     if(!family) {
108         /* Fallback for Window's font families to common PostScript families */
109         if(!strcmp(FaceName, "Arial"))
110             strcpy(FaceName, "Helvetica");
111         else if(!strcmp(FaceName, "System"))
112             strcpy(FaceName, "Helvetica");
113         else if(!strcmp(FaceName, "Times New Roman"))
114             strcpy(FaceName, "Times");
115         else if(!strcmp(FaceName, "Courier New"))
116             strcpy(FaceName, "Courier");
117
118         for(family = physDev->pi->Fonts; family; family = family->next) {
119             if(!strcmp(FaceName, family->FamilyName))
120                 break;
121         }
122     }
123     /* If all else fails, use the first font defined for the printer */
124     if(!family)
125         family = physDev->pi->Fonts;
126
127     TRACE("Got family '%s'\n", family->FamilyName);
128
129     for(afmle = family->afmlist; afmle; afmle = afmle->next) {
130         if( (bd == (afmle->afm->Weight == FW_BOLD)) && 
131             (it == (afmle->afm->ItalicAngle != 0.0)) )
132                 break;
133     }
134     if(!afmle)
135         afmle = family->afmlist; /* not ideal */
136     
137     afm = afmle->afm;
138
139     physDev->font.afm = afm;
140     physDev->font.tm.tmHeight = INTERNAL_YWSTODS(dc, lf->lfHeight);
141     if(physDev->font.tm.tmHeight < 0) {
142         physDev->font.tm.tmHeight *= - (afm->FullAscender - afm->Descender) /
143                                        (afm->Ascender - afm->Descender);
144         TRACE("Fixed -ve height to %ld\n", physDev->font.tm.tmHeight);
145     }
146     physDev->font.size = physDev->font.tm.tmHeight * 1000.0 /
147                                 (afm->FullAscender - afm->Descender);
148     physDev->font.scale = physDev->font.size / 1000.0;
149     physDev->font.escapement = lf->lfEscapement;
150     physDev->font.tm.tmAscent = afm->FullAscender * physDev->font.scale;
151     physDev->font.tm.tmDescent = -afm->Descender * physDev->font.scale;
152     physDev->font.tm.tmInternalLeading = (afm->FullAscender - afm->Ascender)
153                                                 * physDev->font.scale;
154     physDev->font.tm.tmExternalLeading = (1000.0 - afm->FullAscender)
155                                                 * physDev->font.scale; /* ?? */
156     physDev->font.tm.tmAveCharWidth = afm->CharWidths[120] * /* x */
157                                                    physDev->font.scale;
158     physDev->font.tm.tmMaxCharWidth = afm->CharWidths[77] * /* M */
159                                            physDev->font.scale;
160     physDev->font.tm.tmWeight = afm->Weight;
161     physDev->font.tm.tmItalic = afm->ItalicAngle != 0.0;
162     physDev->font.tm.tmUnderlined = lf->lfUnderline;
163     physDev->font.tm.tmStruckOut = lf->lfStrikeOut;
164     physDev->font.tm.tmFirstChar = 32;
165     physDev->font.tm.tmLastChar = 251;
166     physDev->font.tm.tmDefaultChar = 128;
167     physDev->font.tm.tmBreakChar = 32;
168     physDev->font.tm.tmPitchAndFamily = afm->IsFixedPitch ? 0 :
169                                           TMPF_FIXED_PITCH;
170     physDev->font.tm.tmPitchAndFamily |= TMPF_DEVICE;
171     physDev->font.tm.tmCharSet = ANSI_CHARSET;
172     physDev->font.tm.tmOverhang = 0;
173     physDev->font.tm.tmDigitizedAspectX = dc->devCaps->logPixelsY;
174     physDev->font.tm.tmDigitizedAspectY = dc->devCaps->logPixelsX;
175
176     physDev->font.set = FALSE;
177
178     TRACE("Selected PS font '%s' size %d weight %ld.\n", 
179           physDev->font.afm->FontName, physDev->font.size,
180           physDev->font.tm.tmWeight );
181     TRACE("H = %ld As = %ld Des = %ld IL = %ld EL = %ld\n",
182           physDev->font.tm.tmHeight, physDev->font.tm.tmAscent,
183           physDev->font.tm.tmDescent, physDev->font.tm.tmInternalLeading,
184           physDev->font.tm.tmExternalLeading);
185
186     return prevfont;
187 }
188
189 /***********************************************************************
190  *           PSDRV_GetTextMetrics
191  */
192 BOOL PSDRV_GetTextMetrics(DC *dc, TEXTMETRICW *metrics)
193 {
194     PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
195
196     memcpy(metrics, &(physDev->font.tm), sizeof(physDev->font.tm));
197     return TRUE;
198 }
199
200 /***********************************************************************
201  *           PSDRV_UnicodeToANSI
202  */
203 char PSDRV_UnicodeToANSI(int u)
204 {
205     if((u & 0xff) == u)
206         return u;
207     switch(u) {
208     case 0x2013: /* endash */
209         return 0x96;
210     case 0x2014: /* emdash */
211         return 0x97;
212     case 0x2018: /* quoteleft */
213         return 0x91;
214     case 0x2019: /* quoteright */
215         return 0x92;
216     case 0x201c: /* quotedblleft */
217        return 0x93;
218     case 0x201d: /* quotedblright */
219         return 0x94;
220     case 0x2022: /* bullet */
221         return 0x95;
222     default:
223         WARN("Umapped unicode char U%04x\n", u);
224         return 0xff;
225     }
226 }
227 /***********************************************************************
228  *           PSDRV_GetTextExtentPoint
229  */
230 BOOL PSDRV_GetTextExtentPoint( DC *dc, LPCWSTR str, INT count,
231                                   LPSIZE size )
232 {
233     PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
234     INT i;
235     float width;
236
237     width = 0.0;
238
239     for(i = 0; i < count && str[i]; i++) {
240         char c = PSDRV_UnicodeToANSI(str[i]);
241         width += physDev->font.afm->CharWidths[(int)(unsigned char)c];
242 /*      TRACE(psdrv, "Width after %dth char '%c' = %f\n", i, str[i], width);*/
243     }
244     width *= physDev->font.scale;
245     TRACE("Width after scale (%f) is %f\n", physDev->font.scale, width);
246
247     size->cx = GDI_ROUND((FLOAT)width * dc->xformVport2World.eM11);
248     size->cy = GDI_ROUND((FLOAT)physDev->font.tm.tmHeight  * dc->xformVport2World.eM22);
249
250     return TRUE;
251 }
252
253
254 /***********************************************************************
255  *           PSDRV_GetCharWidth
256  */
257 BOOL PSDRV_GetCharWidth( DC *dc, UINT firstChar, UINT lastChar,
258                            LPINT buffer )
259 {
260     PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
261     UINT i;
262
263     TRACE("first = %d last = %d\n", firstChar, lastChar);
264
265     if(lastChar > 0xff) return FALSE;
266     for( i = firstChar; i <= lastChar; i++ )
267         *buffer++ = physDev->font.afm->CharWidths[i] * physDev->font.scale;
268
269     return TRUE;
270 }
271
272     
273 /***********************************************************************
274  *           PSDRV_SetFont
275  */
276 BOOL PSDRV_SetFont( DC *dc )
277 {
278     PSDRV_PDEVICE *physDev = (PSDRV_PDEVICE *)dc->physDev;
279     BOOL ReEncode = FALSE;
280
281     PSDRV_WriteSetColor(dc, &physDev->font.color);
282     if(physDev->font.set) return TRUE;
283
284     if(physDev->font.afm->EncodingScheme && 
285        !strcmp(physDev->font.afm->EncodingScheme, "AdobeStandardEncoding"))
286         ReEncode = TRUE;
287     if(ReEncode)
288         PSDRV_WriteReencodeFont(dc);
289     PSDRV_WriteSetFont(dc, ReEncode);
290     physDev->font.set = TRUE;
291     return TRUE;
292 }
293
294
295 /***********************************************************************
296  *           PSDRV_GetFontMetric
297  */
298 static UINT PSDRV_GetFontMetric(HDC hdc, AFM *pafm, NEWTEXTMETRICEXW *pTM, 
299                                 ENUMLOGFONTEXW *pLF, INT16 size)
300
301 {
302     DC *dc = DC_GetDCPtr( hdc );
303     float scale = size / (pafm->FullAscender - pafm->Descender);
304
305     if (!dc) return FALSE;
306
307     memset( pLF, 0, sizeof(*pLF) );
308     memset( pTM, 0, sizeof(*pTM) );
309
310 #define plf ((LPLOGFONTW)pLF)
311 #define ptm ((LPNEWTEXTMETRICW)pTM)
312     plf->lfHeight    = ptm->tmHeight       = size;
313     plf->lfWidth     = ptm->tmAveCharWidth = pafm->CharWidths[120] * scale;
314     plf->lfWeight    = ptm->tmWeight       = pafm->Weight;
315     plf->lfItalic    = ptm->tmItalic       = pafm->ItalicAngle != 0.0;
316     plf->lfUnderline = ptm->tmUnderlined   = 0;
317     plf->lfStrikeOut = ptm->tmStruckOut    = 0;
318     plf->lfCharSet   = ptm->tmCharSet      = ANSI_CHARSET;
319
320     /* convert pitch values */
321
322     ptm->tmPitchAndFamily = pafm->IsFixedPitch ? 0 : TMPF_FIXED_PITCH;
323     ptm->tmPitchAndFamily |= TMPF_DEVICE;
324     plf->lfPitchAndFamily = 0;
325
326     MultiByteToWideChar(CP_ACP, 0, pafm->FamilyName, -1,
327                         plf->lfFaceName, LF_FACESIZE);
328 #undef plf
329
330     ptm->tmAscent = pafm->FullAscender * scale;
331     ptm->tmDescent = -pafm->Descender * scale;
332     ptm->tmInternalLeading = (pafm->FullAscender - pafm->Ascender) * scale;
333     ptm->tmMaxCharWidth = pafm->CharWidths[77] * scale;
334     ptm->tmDigitizedAspectX = dc->devCaps->logPixelsY;
335     ptm->tmDigitizedAspectY = dc->devCaps->logPixelsX;
336
337     *(INT*)&ptm->tmFirstChar = 32;
338
339     GDI_ReleaseObj( hdc );
340     /* return font type */
341
342     return DEVICE_FONTTYPE;
343 #undef ptm
344 }
345
346 /***********************************************************************
347  *           PSDRV_EnumDeviceFonts
348  */
349 BOOL PSDRV_EnumDeviceFonts( HDC hdc, LPLOGFONTW plf, 
350                             DEVICEFONTENUMPROC proc, LPARAM lp )
351 {
352     ENUMLOGFONTEXW      lf;
353     NEWTEXTMETRICEXW    tm;
354     BOOL                b, bRet = 0;
355     AFMLISTENTRY        *afmle;
356     FONTFAMILY          *family;
357     PSDRV_PDEVICE       *physDev;
358     char                FaceName[LF_FACESIZE];
359     DC *dc = DC_GetDCPtr( hdc );
360     if (!dc) return FALSE;
361
362     physDev = (PSDRV_PDEVICE *)dc->physDev;
363     /* FIXME!! should reevaluate dc->physDev after every callback */
364     GDI_ReleaseObj( hdc );
365
366     if( plf->lfFaceName[0] ) {
367         WideCharToMultiByte(CP_ACP, 0, plf->lfFaceName, -1,
368                           FaceName, sizeof(FaceName), NULL, NULL);
369         TRACE("lfFaceName = '%s'\n", FaceName);
370         for(family = physDev->pi->Fonts; family; family = family->next) {
371             if(!strncmp(FaceName, family->FamilyName, 
372                         strlen(family->FamilyName)))
373                 break;
374         }
375         if(family) {
376             for(afmle = family->afmlist; afmle; afmle = afmle->next) {
377                 TRACE("Got '%s'\n", afmle->afm->FontName);
378                 if( (b = (*proc)( &lf, &tm, 
379                         PSDRV_GetFontMetric( hdc, afmle->afm, &tm, &lf, 200 ),
380                                   lp )) )
381                      bRet = b;
382                 else break;
383             }
384         }
385     } else {
386
387         TRACE("lfFaceName = NULL\n");
388         for(family = physDev->pi->Fonts; family; family = family->next) {
389             afmle = family->afmlist;
390             TRACE("Got '%s'\n", afmle->afm->FontName);
391             if( (b = (*proc)( &lf, &tm, 
392                    PSDRV_GetFontMetric( hdc, afmle->afm, &tm, &lf, 200 ), 
393                               lp )) )
394                 bRet = b;
395             else break;
396         }
397     }
398     return bRet;
399 }