Unicode-enable PostScript driver

Ian Pilcher ian.pilcher at home.com
Wed Jul 18 16:47:39 CDT 2001


With this patch, the PostScript driver no longer converts text strings
from 16-bit Unicode characters to 8-bit "ANSI" characters.  Instead, any
required character metrics are looked up their 16-bit UV (using bsearch)
and the PostScript 'glyphshow' operator is used to individually print
each character -- independent of code pages, encodings, etc.  My simple
test application is now able to print a string of Arabic characters
(using Courier New), which came out as garbage before.

Because this is such a significant change to the way the driver works, I
suspect that it may cause some problems.  Therefore, I've disabled the
old code (#if 0 ... #endif), rather than removing it.  This is will make
it easier for me to see what the driver used to do, if I have to fix
anything.  There's some encoding-related stuff that can be removed from
the driver when the new stuff has been pounded on a bit; I'll take the
disabled code out then.

A note of thanks to Gerard Patel for being the only person (as far as I
know) who actually tested the initial version of this patch.

Modified files:
    dlls/wineps: afm.c font.c ps.c psdrv.h truetype.c

Log message:
    Ian Pilcher
    WINEPS: read/calculate average character width for all fonts
    WINEPS: work directly with 16-bit WCHARs; don't convert to CP1252
-- 
========================================================================
Ian Pilcher                                         ian.pilcher at home.com
========================================================================
-------------- next part --------------
diff -urN ../wine-20010718cvs/dlls/wineps/afm.c ./dlls/wineps/afm.c
--- ../wine-20010718cvs/dlls/wineps/afm.c	Mon Jul  2 13:00:33 2001
+++ ./dlls/wineps/afm.c	Wed Jul 18 15:07:27 2001
@@ -802,6 +802,60 @@
 }
 
 /*******************************************************************************
+ *  PSDRV_CalcAvgCharWidth
+ *
+ *  Calculate WinMetrics.sAvgCharWidth for a Type 1 font.  Can also be used on
+ *  TrueType fonts, if font designer set OS/2:xAvgCharWidth to zero.
+ *
+ *  Tries to use formula in TrueType specification; falls back to simple mean
+ *  if any lowercase latin letter (or space) is not present.
+ */
+inline static SHORT MeanCharWidth(const AFM *afm)
+{
+    float   w = 0.0;
+    int     i;
+    
+    for (i = 0; i < afm->NumofMetrics; ++i)
+    	w += afm->Metrics[i].WX;
+	
+    w /= afm->NumofMetrics;
+    
+    return (SHORT)(w + 0.5);
+}
+
+static const struct { LONG UV; int weight; } UVweight[27] =
+{
+    { 0x0061,  64 }, { 0x0062,  14 }, { 0x0063,  27 }, { 0x0064,  35 },
+    { 0x0065, 100 }, { 0x0066,  20 }, { 0x0067,  14 }, { 0x0068,  42 },
+    { 0x0069,  63 }, { 0x006a,   3 }, { 0x006b,   6 }, { 0x006c,  35 },
+    { 0x006d,  20 }, { 0x006e,  56 }, { 0x006f,  56 }, { 0x0070,  17 },
+    { 0x0071,   4 }, { 0x0072,  49 }, { 0x0073,  56 }, { 0x0074,  71 },
+    { 0x0075,  31 }, { 0x0076,  10 }, { 0x0077,  18 }, { 0x0078,   3 },
+    { 0x0079,  18 }, { 0x007a,   2 }, { 0x0020, 166 }
+};
+ 
+SHORT PSDRV_CalcAvgCharWidth(const AFM *afm)
+{
+    float   w = 0.0;
+    int     i;
+    
+    for (i = 0; i < 27; ++i)
+    {
+    	const AFMMETRICS    *afmm;
+	
+	afmm = PSDRV_UVMetrics(UVweight[i].UV, afm);
+	if (afmm->UV != UVweight[i].UV)     /* UVMetrics returns first glyph */
+	    return MeanCharWidth(afm);	    /*   in font if UV is missing    */
+	    
+	w += afmm->WX * (float)(UVweight[i].weight);
+    }
+    
+    w /= 1000.0;
+    
+    return (SHORT)(w + 0.5);
+}
+
+/*******************************************************************************
  *  CalcWindowsMetrics
  *
  *  Calculates several Windows-specific font metrics for each font.  Relies on
@@ -871,6 +925,8 @@
 	    wm.sLineGap = 1150 - (wm.sAscender - wm.sDescender);
 	    if (wm.sLineGap < 0)
 	    	wm.sLineGap = 0;
+		
+	    wm.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
 						
 	    TRACE("Windows metrics for '%s':\n", afm->FullName);
 	    TRACE("\tsAscender = %i\n", wm.sAscender);
@@ -882,6 +938,7 @@
 	    TRACE("\tsTypoLineGap = %i\n", wm.sTypoLineGap);
 	    TRACE("\tusWinAscent = %u\n", wm.usWinAscent);
 	    TRACE("\tusWinDescent = %u\n", wm.usWinDescent);
+	    TRACE("\tsAvgCharWidth = %i\n", wm.sAvgCharWidth);
 	    
 	    afm->WinMetrics = wm;
 	    
diff -urN ../wine-20010718cvs/dlls/wineps/font.c ./dlls/wineps/font.c
--- ../wine-20010718cvs/dlls/wineps/font.c	Fri May 11 15:03:41 2001
+++ ./dlls/wineps/font.c	Wed Jul 18 14:53:47 2001
@@ -5,6 +5,7 @@
  *
  */
 #include <string.h>
+#include <stdlib.h> 	    /* for bsearch() */
 #include "winspool.h"
 #include "psdrv.h"
 #include "debugtools.h"
@@ -197,6 +198,7 @@
     return TRUE;
 }
 
+#if 0
 /***********************************************************************
  *           PSDRV_UnicodeToANSI
  */
@@ -224,9 +226,51 @@
 	return 0xff;
     }
 }
+#endif
+
+/******************************************************************************
+ *  	PSDRV_UVMetrics
+ *
+ *  Find the AFMMETRICS for a given UV.  Returns first glyph in the font
+ *  (space?) if the font does not have a glyph for the given UV.
+ */
+static int MetricsByUV(const void *a, const void *b)
+{
+    return (int)(((const AFMMETRICS *)a)->UV - ((const AFMMETRICS *)b)->UV);
+}
+ 
+const AFMMETRICS *PSDRV_UVMetrics(LONG UV, const AFM *afm)
+{
+    AFMMETRICS	    	key;
+    const AFMMETRICS	*needle;
+    
+    /*
+     *	Ugly work-around for symbol fonts.  Wine is sending characters which
+     *	belong in the Unicode private use range (U+F020 - U+F0FF) as ASCII
+     *	characters (U+0020 - U+00FF).
+     */
+    
+    if ((afm->Metrics->UV & 0xff00) == 0xf000 && UV < 0x100)
+    	UV |= 0xf000;
+    
+    key.UV = UV;
+    
+    needle = bsearch(&key, afm->Metrics, afm->NumofMetrics, sizeof(AFMMETRICS),
+	    MetricsByUV);
+
+    if (needle == NULL)
+    {
+    	WARN("No glyph for U+%.4lX in %s\n", UV, afm->FontName);
+    	needle = afm->Metrics;
+    }
+	
+    return needle;
+}
+
 /***********************************************************************
  *           PSDRV_GetTextExtentPoint
  */
+#if 0
 BOOL PSDRV_GetTextExtentPoint( DC *dc, LPCWSTR str, INT count,
                                   LPSIZE size )
 {
@@ -249,11 +293,34 @@
 
     return TRUE;
 }
+#endif
 
+BOOL PSDRV_GetTextExtentPoint(DC *dc, LPCWSTR str, INT count, LPSIZE size)
+{
+    PSDRV_PDEVICE   *physDev = (PSDRV_PDEVICE *)dc->physDev;
+    int     	    i;
+    float   	    width = 0.0;
+    
+    TRACE("%s %i\n", debugstr_wn(str, count), count);
+    
+    for (i = 0; i < count && str[i] != '\0'; ++i)
+	width += PSDRV_UVMetrics(str[i], physDev->font.afm)->WX;
+	
+    width *= physDev->font.scale;
+    
+    size->cx = GDI_ROUND((FLOAT)width * dc->xformVport2World.eM11);
+    size->cy = GDI_ROUND((FLOAT)physDev->font.tm.tmHeight *
+    	    dc->xformVport2World.eM22);
+	    
+    TRACE("cx=%li cy=%li\n", size->cx, size->cy);
+	    
+    return TRUE;
+}
 
 /***********************************************************************
  *           PSDRV_GetCharWidth
  */
+#if 0
 BOOL PSDRV_GetCharWidth( DC *dc, UINT firstChar, UINT lastChar,
 			   LPINT buffer )
 {
@@ -268,7 +335,31 @@
 
     return TRUE;
 }
+#endif
 
+BOOL PSDRV_GetCharWidth(DC *dc, UINT firstChar, UINT lastChar, LPINT buffer)
+{
+    PSDRV_PDEVICE   *physDev = (PSDRV_PDEVICE *)dc->physDev;
+    UINT    	    i;
+    
+    TRACE("U+%.4X U+%.4X\n", firstChar, lastChar);
+    
+    if (lastChar > 0xffff || firstChar > lastChar)
+    {
+    	SetLastError(ERROR_INVALID_PARAMETER);
+    	return FALSE;
+    }
+	
+    for (i = firstChar; i <= lastChar; ++i)
+    {
+    	*buffer = GDI_ROUND(PSDRV_UVMetrics(i, physDev->font.afm)->WX
+	    	* physDev->font.scale);
+	TRACE("U+%.4X: %i\n", i, *buffer);
+	++buffer;
+    }
+	
+    return TRUE;
+}
     
 /***********************************************************************
  *           PSDRV_SetFont
diff -urN ../wine-20010718cvs/dlls/wineps/ps.c ./dlls/wineps/ps.c
--- ../wine-20010718cvs/dlls/wineps/ps.c	Sun Jan 28 17:13:45 2001
+++ ./dlls/wineps/ps.c	Wed Jul 18 14:50:20 2001
@@ -115,6 +115,9 @@
 static char psshow[] = /* string */
 "(%s) show\n";
 
+static const char psglyphshow[] = /* glyph name */
+"/%s glyphshow\n";
+
 static char pssetfont[] = /* fontname, xscale, yscale, ascent, escapement */
 "/%s findfont\n"
 "[%d 0 0 %d 0 0]\n"
@@ -634,6 +637,7 @@
     return TRUE;
 }    
 
+#if 0
 BOOL PSDRV_WriteShow(DC *dc, LPCWSTR str, INT count)
 {
     char *buf, *buf1;
@@ -671,6 +675,33 @@
 
     return TRUE;
 }    
+#endif
+
+BOOL PSDRV_WriteShow(DC *dc, LPCWSTR str, INT count)
+{
+    char    buf[128];
+    int     i;
+    
+    for (i = 0; i < count; ++i)
+    {
+    	LPCSTR	name;
+	int 	l;
+	
+	name = PSDRV_UVMetrics(str[i],
+	    	((PSDRV_PDEVICE *)dc->physDev)->font.afm)->N->sz;
+	l = snprintf(buf, sizeof(buf), psglyphshow, name);
+	
+	if (l < sizeof(psglyphshow) - 2 || l > sizeof(buf) - 1)
+	{
+	    WARN("Unusable glyph name '%s' - ignoring\n", name);
+	    continue;
+	}
+	
+	PSDRV_WriteSpool(dc, buf, l);
+    }
+    
+    return TRUE;
+}
 
 BOOL PSDRV_WriteFill(DC *dc)
 {
diff -urN ../wine-20010718cvs/dlls/wineps/psdrv.h ./dlls/wineps/psdrv.h
--- ../wine-20010718cvs/dlls/wineps/psdrv.h	Tue May 29 17:06:11 2001
+++ ./dlls/wineps/psdrv.h	Wed Jul 18 15:05:05 2001
@@ -421,6 +421,8 @@
 GLYPHNAME *PSDRV_GlyphName(LPCSTR szName);
 VOID PSDRV_IndexGlyphList();
 BOOL PSDRV_GetTrueTypeMetrics();
+const AFMMETRICS *PSDRV_UVMetrics(LONG UV, const AFM *afm);
+SHORT PSDRV_CalcAvgCharWidth(const AFM *afm);
 
 #endif
 
diff -urN ../wine-20010718cvs/dlls/wineps/truetype.c ./dlls/wineps/truetype.c
--- ../wine-20010718cvs/dlls/wineps/truetype.c	Mon Jul  2 13:00:33 2001
+++ ./dlls/wineps/truetype.c	Wed Jul 18 15:18:00 2001
@@ -329,6 +329,7 @@
     afm->WinMetrics.sTypoLineGap = os2->sTypoLineGap;
     afm->WinMetrics.usWinAscent = os2->usWinAscent;
     afm->WinMetrics.usWinDescent = os2->usWinDescent;
+    afm->WinMetrics.sAvgCharWidth = os2->xAvgCharWidth;
     
     return TRUE;
 }
@@ -482,6 +483,11 @@
     
     if (ReadCharMetrics(afm) == FALSE)
     	return FALSE;
+
+    /* Can't do this check until character metrics are read */
+
+    if (afm->WinMetrics.sAvgCharWidth == 0)
+    	afm->WinMetrics.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
 
     return TRUE;
 }


More information about the wine-patches mailing list