wineps.drv: Remove the unscaled font hack and read the outline directly from the...
authorHuw Davies <huw@codeweavers.com>
Wed, 21 Apr 2010 13:28:40 +0000 (14:28 +0100)
committerAlexandre Julliard <julliard@winehq.org>
Wed, 21 Apr 2010 14:32:17 +0000 (16:32 +0200)
dlls/wineps.drv/type1.c

index f82fc089e0bc8bf7aa3e51aae2d15fb5c59137f1..bc33ef6757811e85d74c540cd9d1f0fb8bce41c0 100644 (file)
@@ -30,6 +30,7 @@
 #include "windef.h"
 #include "winbase.h"
 #include "wingdi.h"
+#include "winternl.h"
 
 #include "psdrv.h"
 #include "wine/debug.h"
@@ -55,6 +56,20 @@ enum t1_cmds {
 };
 
 
+#define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
+          ( ( (DWORD)_x4 << 24 ) |     \
+            ( (DWORD)_x3 << 16 ) |     \
+            ( (DWORD)_x2 <<  8 ) |     \
+              (DWORD)_x1         )
+
+#ifdef WORDS_BIGENDIAN
+static inline WORD  get_be_word(const void *p)  { return *(WORD*)p; }
+static inline DWORD get_be_dword(const void *p) { return *(DWORD*)p; }
+#else
+static inline WORD  get_be_word(const void *p)  { return RtlUshortByteSwap(*(WORD*)p); }
+static inline DWORD get_be_dword(const void *p) { return RtlUlongByteSwap(*(DWORD*)p); }
+#endif
+
 TYPE1 *T1_download_header(PSDRV_PDEVICE *physDev, char *ps_name, RECT *bbox, UINT emsize)
 {
     char *buf;
@@ -154,15 +169,11 @@ static void str_add_num(STR *str, int num)
     }
 }
 
-static void str_add_point(STR *str, POINTFX *pt, POINT *curpos)
+static void str_add_point(STR *str, POINT pt, POINT *curpos)
 {
-    POINT newpos;
-    newpos.x = pt->x.value + ((pt->x.fract >> 15) & 0x1);
-    newpos.y = pt->y.value + ((pt->y.fract >> 15) & 0x1);
-
-    str_add_num(str, newpos.x - curpos->x);
-    str_add_num(str, newpos.y - curpos->y);
-    *curpos = newpos;
+    str_add_num(str, pt.x - curpos->x);
+    str_add_num(str, pt.y - curpos->y);
+    *curpos = pt;
 }
 
 static void str_add_cmd(STR *str, enum t1_cmds cmd)
@@ -176,23 +187,343 @@ static int str_get_bytes(STR *str, BYTE **b)
   return str->len;
 }
 
+static BOOL get_hmetrics(HDC hdc, DWORD index, short *lsb, WORD *advance)
+{
+    BYTE hhea[36];
+    WORD num_of_long;
+    BYTE buf[4];
+
+    *lsb = 0;
+    *advance = 0;
+
+    GetFontData(hdc, MS_MAKE_TAG('h','h','e','a'), 0, hhea, sizeof(hhea));
+    num_of_long = get_be_word(hhea + 34);
+
+    if(index < num_of_long)
+    {
+        if(GetFontData(hdc, MS_MAKE_TAG('h','m','t','x'), index * 4, buf, 4) != 4) return FALSE;
+        *advance = get_be_word(buf);
+        *lsb = (signed short)get_be_word(buf + 2);
+    }
+    else
+    {
+        if(GetFontData(hdc, MS_MAKE_TAG('h','m','t','x'), (num_of_long - 1) * 4, buf, 2) != 2) return FALSE;
+        *advance = get_be_word(buf);
+        if(GetFontData(hdc, MS_MAKE_TAG('h','m','t','x'), num_of_long * 4 + (index - num_of_long) * 2, buf, 2) != 2) return FALSE;
+        *lsb = (signed short)get_be_word(buf);
+    }
+
+    return TRUE;
+}
+
+static BOOL get_glyf_pos(HDC hdc, DWORD index, DWORD *start, DWORD *end)
+{
+    DWORD len;
+    BYTE *head, *loca;
+    WORD loca_format;
+    BOOL ret = FALSE;
+
+    *start = *end = 0;
+
+    len = GetFontData(hdc, MS_MAKE_TAG('h','e','a','d'), 0, NULL, 0);
+    head = HeapAlloc(GetProcessHeap(), 0, len);
+    GetFontData(hdc, MS_MAKE_TAG('h','e','a','d'), 0, head, len);
+    loca_format = get_be_word(head + 50);
+
+    len = GetFontData(hdc, MS_MAKE_TAG('l','o','c','a'), 0, NULL, 0);
+    loca = HeapAlloc(GetProcessHeap(), 0, len);
+    GetFontData(hdc, MS_MAKE_TAG('l','o','c','a'), 0, loca, len);
+
+    switch(loca_format) {
+    case 0:
+        *start = get_be_word(((WORD*)loca) + index);
+        *start <<= 1;
+        *end = get_be_word(((WORD*)loca) + index + 1);
+        *end <<= 1;
+        ret = TRUE;
+        break;
+    case 1:
+        *start = get_be_dword(((DWORD*)loca) + index);
+        *end = get_be_dword(((DWORD*)loca) + index + 1);
+        ret = TRUE;
+        break;
+    default:
+        ERR("Unknown loca_format %d\n", loca_format);
+    }
+
+    HeapFree(GetProcessHeap(), 0, loca);
+    HeapFree(GetProcessHeap(), 0, head);
+    return ret;
+}
+
+
+static BYTE *get_glyph_data(HDC hdc, DWORD index)
+{
+    DWORD start, end, len;
+    BYTE *data;
+
+    if(!get_glyf_pos(hdc, index, &start, &end))
+        return NULL;
+
+    len = end - start;
+    if(!len) return NULL;
+
+    data = HeapAlloc(GetProcessHeap(), 0, len);
+    if(!data) return NULL;
+
+    if(GetFontData(hdc, MS_MAKE_TAG('g','l','y','f'), start, data, len) != len)
+    {
+        HeapFree(GetProcessHeap(), 0, data);
+        return NULL;
+    }
+    return data;
+}
+
+typedef struct
+{
+    WORD num_conts;
+    WORD *end_pts; /* size_is(num_conts) */
+    BYTE *flags;   /* size_is(end_pts[num_conts - 1] + 1) */
+    POINT *pts;    /* size_is(end_pts[num_conts - 1] + 1) */
+    short lsb;
+    WORD advance;
+} glyph_outline;
+
+static inline WORD pts_in_outline(glyph_outline *outline)
+{
+    WORD num_pts = 0;
+
+    if(outline->num_conts)
+        num_pts = outline->end_pts[outline->num_conts - 1] + 1;
+
+    return num_pts;
+}
+
+static BOOL append_glyph_outline(HDC hdc, DWORD index, glyph_outline *outline);
+
+static BOOL append_simple_glyph(BYTE *data, glyph_outline *outline)
+{
+    USHORT num_pts, start_pt = 0, ins_len;
+    WORD *end_pts;
+    int num_conts, start_cont, i;
+    BYTE *ptr;
+    int x, y;
+
+    start_cont = outline->num_conts;
+    start_pt = pts_in_outline(outline);
+
+    num_conts = get_be_word(data);
+
+    end_pts = (WORD*)(data + 10);
+    num_pts = get_be_word(end_pts + num_conts - 1) + 1;
+
+    ins_len = get_be_word(end_pts + num_conts);
+
+    ptr = (BYTE*)(end_pts + num_conts) + 2 + ins_len;
+
+    if(outline->num_conts)
+    {
+        outline->end_pts = HeapReAlloc(GetProcessHeap(), 0, outline->end_pts, (start_cont + num_conts) * sizeof(*outline->end_pts));
+        outline->flags   = HeapReAlloc(GetProcessHeap(), 0, outline->flags, start_pt + num_pts);
+        outline->pts     = HeapReAlloc(GetProcessHeap(), 0, outline->pts,  (start_pt + num_pts) * sizeof(*outline->pts));
+    }
+    else
+    {
+        outline->end_pts = HeapAlloc(GetProcessHeap(), 0, num_conts * sizeof(*outline->end_pts));
+        outline->flags   = HeapAlloc(GetProcessHeap(), 0, num_pts);
+        outline->pts     = HeapAlloc(GetProcessHeap(), 0, num_pts * sizeof(*outline->pts));
+    }
+
+    outline->num_conts += num_conts;
+
+    for(i = 0; i < num_conts; i++)
+        outline->end_pts[start_cont + i] = start_pt + get_be_word(end_pts + i);
+
+    for(i = 0; i < num_pts; i++)
+    {
+        outline->flags[start_pt + i] = *ptr;
+        if(*ptr & 8)
+        {
+            BYTE count = *++ptr;
+            while(count)
+            {
+                i++;
+                outline->flags[start_pt + i] = *(ptr - 1);
+                count--;
+            }
+        }
+        ptr++;
+    }
+
+    x = 0;
+
+    for(i = 0; i < num_pts; i++)
+    {
+        INT delta;
+
+        delta = 0;
+        if(outline->flags[start_pt + i] & 2)
+        {
+            delta = *ptr++;
+            if((outline->flags[start_pt + i] & 0x10) == 0)
+                delta = -delta;
+        }
+        else if((outline->flags[start_pt + i] & 0x10) == 0)
+        {
+            delta = (signed short)get_be_word(ptr);
+            ptr += 2;
+        }
+        x += delta;
+        outline->pts[start_pt + i].x = x;
+    }
+
+    y = 0;
+    for(i = 0; i < num_pts; i++)
+    {
+        INT delta;
+
+        delta = 0;
+        if(outline->flags[start_pt + i] & 4)
+        {
+            delta = *ptr++;
+            if((outline->flags[start_pt + i] & 0x20) == 0)
+                delta = -delta;
+        }
+        else if((outline->flags[start_pt + i] & 0x20) == 0)
+        {
+            delta = (signed short)get_be_word(ptr);
+            ptr += 2;
+        }
+        y += delta;
+        outline->pts[start_pt + i].y = y;
+    }
+    return TRUE;
+}
+
+/* Some flags for composite glyphs.  See glyf table in OT spec */
+#define ARG_1_AND_2_ARE_WORDS    (1L << 0)
+#define ARGS_ARE_XY_VALUES       (1L << 1)
+#define WE_HAVE_A_SCALE          (1L << 3)
+#define MORE_COMPONENTS          (1L << 5)
+#define WE_HAVE_AN_X_AND_Y_SCALE (1L << 6)
+#define WE_HAVE_A_TWO_BY_TWO     (1L << 7)
+#define USE_MY_METRICS           (1L << 9)
+
+static BOOL append_complex_glyph(HDC hdc, const BYTE *data, glyph_outline *outline)
+{
+    const BYTE *ptr = data;
+    WORD flags, index;
+    short arg1, arg2;
+    WORD scale_xx = 1, scale_xy = 0, scale_yx = 0, scale_yy = 1;
+    WORD start_pt, end_pt;
+
+    ptr += 10;
+    do
+    {
+        flags = get_be_word(ptr);
+        ptr += 2;
+        index = get_be_word(ptr);
+        ptr += 2;
+        if(flags & ARG_1_AND_2_ARE_WORDS)
+        {
+            arg1 = (short)get_be_word(ptr);
+            ptr += 2;
+            arg2 = (short)get_be_word(ptr);
+            ptr += 2;
+        }
+        else
+        {
+            arg1 = *(char*)ptr++;
+            arg2 = *(char*)ptr++;
+        }
+        if(flags & WE_HAVE_A_SCALE)
+        {
+            scale_xx = scale_yy = get_be_word(ptr);
+            ptr += 2;
+        }
+        else if(flags & WE_HAVE_AN_X_AND_Y_SCALE)
+        {
+            scale_xx = get_be_word(ptr);
+            ptr += 2;
+            scale_yy = get_be_word(ptr);
+            ptr += 2;
+        }
+        else if(flags & WE_HAVE_A_TWO_BY_TWO)
+        {
+            scale_xx = get_be_word(ptr);
+            ptr += 2;
+            scale_xy = get_be_word(ptr);
+            ptr += 2;
+            scale_yx = get_be_word(ptr);
+            ptr += 2;
+            scale_yy = get_be_word(ptr);
+            ptr += 2;
+        }
+
+        start_pt = pts_in_outline(outline);
+        append_glyph_outline(hdc, index, outline);
+        end_pt = pts_in_outline(outline);
+
+        if((flags & ARGS_ARE_XY_VALUES) == 0)
+        {
+            WORD orig_pt = arg1, new_pt = arg2;
+            new_pt += start_pt;
+
+            if(orig_pt >= start_pt || new_pt >= end_pt) return FALSE;
+
+            arg1 = outline->pts[orig_pt].x - outline->pts[new_pt].x;
+            arg2 = outline->pts[orig_pt].y - outline->pts[new_pt].y;
+        }
+        while(start_pt < end_pt)
+        {
+            outline->pts[start_pt].x += arg1;
+            outline->pts[start_pt].y += arg2;
+            start_pt++;
+        }
+
+        if(flags & USE_MY_METRICS)
+            get_hmetrics(hdc, index, &outline->lsb, &outline->advance);
+
+    } while(flags & MORE_COMPONENTS);
+    return TRUE;
+}
+
+static BOOL append_glyph_outline(HDC hdc, DWORD index, glyph_outline *outline)
+{
+    BYTE *glyph_data;
+    SHORT num_conts;
+
+    glyph_data = get_glyph_data(hdc, index);
+    if(!glyph_data) return FALSE;
+
+    num_conts = get_be_word(glyph_data);
+
+    if(num_conts == -1)
+        append_complex_glyph(hdc, glyph_data, outline);
+    else if(num_conts > 0)
+        append_simple_glyph(glyph_data, outline);
+
+    HeapFree(GetProcessHeap(), 0, glyph_data);
+    return TRUE;
+}
+
+static inline BOOL on_point(const glyph_outline *outline, WORD pt)
+{
+    return outline->flags[pt] & 1;
+}
+
 BOOL T1_download_glyph(PSDRV_PDEVICE *physDev, DOWNLOAD *pdl, DWORD index,
                       char *glyph_name)
 {
-    DWORD len, i;
+    DWORD len;
+    WORD cur_pt, cont;
     char *buf;
     TYPE1 *t1;
     STR *charstring;
     BYTE *bytes;
-    HFONT old_font, unscaled_font;
-    GLYPHMETRICS gm;
-    char *glyph_buf;
     POINT curpos;
-    TTPOLYGONHEADER *pph;
-    TTPOLYCURVE *ppc;
-    LOGFONTW lf;
-    RECT rc;
-    static const MAT2 identity = { {0,1},{0,0},{0,0},{0,1} };
+    glyph_outline outline;
+
     static const char glyph_def_begin[] =
       "/%s findfont dup\n"
       "/Private get begin\n"
@@ -216,71 +547,88 @@ BOOL T1_download_glyph(PSDRV_PDEVICE *physDev, DOWNLOAD *pdl, DWORD index,
                                      t1->glyph_sent_size * sizeof(*(t1->glyph_sent)));
     }
 
-    GetObjectW(GetCurrentObject(physDev->hdc, OBJ_FONT), sizeof(lf), &lf);
-    rc.left = rc.right = rc.bottom = 0;
-    rc.top = t1->emsize;
-    DPtoLP(physDev->hdc, (POINT*)&rc, 2);
-    lf.lfHeight = -abs(rc.top - rc.bottom);
-    lf.lfWidth = 0;
-    lf.lfOrientation = lf.lfEscapement = 0;
-    unscaled_font = CreateFontIndirectW(&lf);
-    old_font = SelectObject(physDev->hdc, unscaled_font);
-    len = GetGlyphOutlineW(physDev->hdc, index, GGO_GLYPH_INDEX | GGO_BEZIER,
-                          &gm, 0, NULL, &identity);
-    if(len == GDI_ERROR) return FALSE;
-    glyph_buf = HeapAlloc(GetProcessHeap(), 0, len);
-    GetGlyphOutlineW(physDev->hdc, index, GGO_GLYPH_INDEX | GGO_BEZIER,
-                    &gm, len, glyph_buf, &identity);
-
-    SelectObject(physDev->hdc, old_font);
-    DeleteObject(unscaled_font);
+    outline.num_conts = 0;
+    outline.flags = NULL;
+    outline.end_pts = NULL;
+    outline.pts = NULL;
+    get_hmetrics(physDev->hdc, index, &outline.lsb, &outline.advance);
 
-    charstring = str_init(100);
+    if(!append_glyph_outline(physDev->hdc, index, &outline)) return FALSE;
 
-    curpos.x = gm.gmptGlyphOrigin.x;
+    charstring = str_init(100);
+    curpos.x = outline.lsb;
     curpos.y = 0;
 
     str_add_num(charstring, curpos.x);
-    str_add_num(charstring, gm.gmCellIncX);
+    str_add_num(charstring, outline.advance);
     str_add_cmd(charstring, hsbw);
 
-    pph = (TTPOLYGONHEADER*)glyph_buf;
-    while((char*)pph < glyph_buf + len) {
-        TRACE("contour len %d\n", pph->cb);
-       ppc = (TTPOLYCURVE*)((char*)pph + sizeof(*pph));
+    for(cur_pt = 0, cont = 0; cont < outline.num_conts; cont++)
+    {
+        POINT start_pos = outline.pts[cur_pt++];
+        WORD end_pt = outline.end_pts[cont];
+        POINT curve_pts[3] = {{0,0},{0,0},{0,0}};
 
-       str_add_point(charstring, &pph->pfxStart, &curpos);
+       str_add_point(charstring, start_pos, &curpos);
        str_add_cmd(charstring, rmoveto);
 
-       while((char*)ppc < (char*)pph + pph->cb) {
-           TRACE("line type %d cpfx = %d\n", ppc->wType, ppc->cpfx);
-           switch(ppc->wType) {
-           case TT_PRIM_LINE:
-               for(i = 0; i < ppc->cpfx; i++) {
-                   str_add_point(charstring, ppc->apfx + i, &curpos);
-                   str_add_cmd(charstring, rlineto);
-               }
-               break;
-           case TT_PRIM_CSPLINE:
-               for(i = 0; i < ppc->cpfx/3; i++) {
-                   str_add_point(charstring, ppc->apfx + 3 * i, &curpos);
-                   str_add_point(charstring, ppc->apfx + 3 * i + 1, &curpos);
-                   str_add_point(charstring, ppc->apfx + 3 * i + 2, &curpos);
-                   str_add_cmd(charstring, rrcurveto);
-               }
-               break;
-           default:
-               ERR("curve type = %d\n", ppc->wType);
-               return FALSE;
-           }
-           ppc = (TTPOLYCURVE*)((char*)ppc + sizeof(*ppc) +
-                                (ppc->cpfx - 1) * sizeof(POINTFX));
-       }
-       str_add_cmd(charstring, closepath);
-       pph = (TTPOLYGONHEADER*)((char*)pph + pph->cb);
+        for(; cur_pt <= end_pt; cur_pt++)
+        {
+            if(on_point(&outline, cur_pt))
+            {
+                str_add_point(charstring, outline.pts[cur_pt], &curpos);
+                str_add_cmd(charstring, rlineto);
+            }
+            else
+            {
+                BOOL added_next = FALSE;
+
+                /* temporarily store the start pt in curve_pts[0] */
+                if(on_point(&outline, cur_pt - 1))
+                    curve_pts[0] = outline.pts[cur_pt - 1];
+                else /* last pt was off curve too, so the previous curve's
+                        end pt was constructed */
+                    curve_pts[0] = curve_pts[2];
+
+                if(cur_pt != end_pt)
+                {
+                    if(on_point(&outline, cur_pt + 1))
+                    {
+                        curve_pts[2] = outline.pts[cur_pt + 1];
+                        added_next = TRUE;
+                    }
+                    else /* next pt is off curve too, so construct the end pt from the
+                            average of the two */
+                    {
+                        curve_pts[2].x = (outline.pts[cur_pt].x + outline.pts[cur_pt + 1].x + 1) / 2;
+                        curve_pts[2].y = (outline.pts[cur_pt].y + outline.pts[cur_pt + 1].y + 1) / 2;
+                    }
+                }
+                else /* last pt of the contour is off curve, so the end pt is the
+                        start pt of the contour */
+                    curve_pts[2] = start_pos;
+
+                curve_pts[0].x = (curve_pts[0].x + 2 * outline.pts[cur_pt].x + 1) / 3;
+                curve_pts[0].y = (curve_pts[0].y + 2 * outline.pts[cur_pt].y + 1) / 3;
+
+                curve_pts[1].x = (curve_pts[2].x + 2 * outline.pts[cur_pt].x + 1) / 3;
+                curve_pts[1].y = (curve_pts[2].y + 2 * outline.pts[cur_pt].y + 1) / 3;
+
+                str_add_point(charstring, curve_pts[0], &curpos);
+                str_add_point(charstring, curve_pts[1], &curpos);
+                str_add_point(charstring, curve_pts[2], &curpos);
+                str_add_cmd(charstring, rrcurveto);
+                if(added_next) cur_pt++;
+            }
+        }
+        str_add_cmd(charstring, closepath);
     }
     str_add_cmd(charstring, endchar);
 
+    HeapFree(GetProcessHeap(), 0, outline.pts);
+    HeapFree(GetProcessHeap(), 0, outline.end_pts);
+    HeapFree(GetProcessHeap(), 0, outline.flags);
+
     buf = HeapAlloc(GetProcessHeap(), 0, sizeof(glyph_def_begin) +
                    strlen(pdl->ps_name) + strlen(glyph_name) + 100);
 
@@ -296,7 +644,6 @@ BOOL T1_download_glyph(PSDRV_PDEVICE *physDev, DOWNLOAD *pdl, DWORD index,
     str_free(charstring);
 
     t1->glyph_sent[index] = TRUE;
-    HeapFree(GetProcessHeap(), 0, glyph_buf);
     HeapFree(GetProcessHeap(), 0, buf);
     return TRUE;
 }