download.c 14.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 *	PostScript driver downloadable font functions
 *
 *	Copyright 2002  Huw D M Davies for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 21 22
 */
#include <string.h>
#include <stdlib.h>
#include <assert.h>
23
#include <math.h>
24
#include <stdarg.h>
25
#include <stdio.h>
26

27
#include "windef.h"
28 29
#include "winbase.h"
#include "wingdi.h"
30
#include "winnls.h"
31

32 33 34 35 36
#include "psdrv.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(psdrv);

37 38 39 40 41 42 43 44 45
#define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
          ( ( (DWORD)_x4 << 24 ) |     \
            ( (DWORD)_x3 << 16 ) |     \
            ( (DWORD)_x2 <<  8 ) |     \
              (DWORD)_x1         )

#define GET_BE_WORD(ptr)  MAKEWORD( ((BYTE *)(ptr))[1], ((BYTE *)(ptr))[0] )
#define GET_BE_DWORD(ptr) ((DWORD)MAKELONG( GET_BE_WORD(&((WORD *)(ptr))[1]), \
                                            GET_BE_WORD(&((WORD *)(ptr))[0]) ))
46 47 48 49

/****************************************************************************
 *  get_download_name
 */
50
static void get_download_name(PHYSDEV dev, LPOUTLINETEXTMETRICA potm, char **str, BOOL vertical)
51
{
52
    static const char reserved_chars[] = " %/(){}[]<>\n\r\t\b\f";
53
    static const char vertical_suffix[] = "_vert";
54 55
    int len;
    char *p;
56 57
    DWORD size;

58
    size = GetFontData(dev->hdc, MS_MAKE_TAG('n','a','m','e'), 0, NULL, 0);
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
    if(size != 0 && size != GDI_ERROR)
    {
        BYTE *name = HeapAlloc(GetProcessHeap(), 0, size);
        if(name)
        {
            USHORT count, i;
            BYTE *strings;
            struct
            {
                USHORT platform_id;
                USHORT encoding_id;
                USHORT language_id;
                USHORT name_id;
                USHORT length;
                USHORT offset;
            } *name_record;

76
            GetFontData(dev->hdc, MS_MAKE_TAG('n','a','m','e'), 0, name, size);
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
            count = GET_BE_WORD(name + 2);
            strings = name + GET_BE_WORD(name + 4);
            name_record = (typeof(name_record))(name + 6);
            for(i = 0; i < count; i++, name_record++)
            {
                name_record->platform_id = GET_BE_WORD(&name_record->platform_id);
                name_record->encoding_id = GET_BE_WORD(&name_record->encoding_id);
                name_record->language_id = GET_BE_WORD(&name_record->language_id);
                name_record->name_id     = GET_BE_WORD(&name_record->name_id);
                name_record->length      = GET_BE_WORD(&name_record->length);
                name_record->offset      = GET_BE_WORD(&name_record->offset);

                if(name_record->platform_id == 1 && name_record->encoding_id == 0 &&
                   name_record->language_id == 0 && name_record->name_id == 6)
                {
                    TRACE("Got Mac PS name %s\n", debugstr_an((char*)strings + name_record->offset, name_record->length));
93
                    *str = HeapAlloc(GetProcessHeap(), 0, name_record->length + sizeof(vertical_suffix));
94 95 96
                    memcpy(*str, strings + name_record->offset, name_record->length);
                    *(*str + name_record->length) = '\0';
                    HeapFree(GetProcessHeap(), 0, name);
97
                    goto done;
98 99 100 101 102 103 104 105 106 107 108 109 110
                }
                if(name_record->platform_id == 3 && name_record->encoding_id == 1 &&
                   name_record->language_id == 0x409 && name_record->name_id == 6)
                {
                    WCHAR *unicode = HeapAlloc(GetProcessHeap(), 0, name_record->length + 2);
                    DWORD len;
                    int c;

                    for(c = 0; c < name_record->length / 2; c++)
                        unicode[c] = GET_BE_WORD(strings + name_record->offset + c * 2);
                    unicode[c] = 0;
                    TRACE("Got Windows PS name %s\n", debugstr_w(unicode));
                    len = WideCharToMultiByte(1252, 0, unicode, -1, NULL, 0, NULL, NULL);
111
                    *str = HeapAlloc(GetProcessHeap(), 0, len + sizeof(vertical_suffix) - 1);
112 113 114
                    WideCharToMultiByte(1252, 0, unicode, -1, *str, len, NULL, NULL);
                    HeapFree(GetProcessHeap(), 0, unicode);
                    HeapFree(GetProcessHeap(), 0, name);
115
                    goto done;
116 117 118 119 120 121 122
                }
            }
            TRACE("Unable to find PostScript name\n");
            HeapFree(GetProcessHeap(), 0, name);
        }
    }

123
    len = strlen((char*)potm + (ptrdiff_t)potm->otmpFaceName) + 1;
124
    *str = HeapAlloc(GetProcessHeap(),0,len + sizeof(vertical_suffix) - 1);
125
    strcpy(*str, (char*)potm + (ptrdiff_t)potm->otmpFaceName);
126

127 128
done:
    for (p = *str; *p; p++) if (strchr( reserved_chars, *p )) *p = '_';
129 130
    if (vertical)
        strcat(*str,vertical_suffix);
131 132 133 134 135 136 137 138
}

/****************************************************************************
 *  is_font_downloaded
 */
static DOWNLOAD *is_font_downloaded(PSDRV_PDEVICE *physDev, char *ps_name)
{
    DOWNLOAD *pdl;
139

140 141 142 143 144 145
    for(pdl = physDev->downloaded_fonts; pdl; pdl = pdl->next)
        if(!strcmp(pdl->ps_name, ps_name))
	    break;
    return pdl;
}

146 147 148 149 150 151 152 153 154
/****************************************************************************
 *  is_room_for_font
 */
static BOOL is_room_for_font(PSDRV_PDEVICE *physDev)
{
    DOWNLOAD *pdl;
    int count = 0;

    /* FIXME: should consider vm usage of each font and available printer memory.
155
       For now we allow up to two fonts to be downloaded at a time */
156 157 158 159 160 161 162 163
    for(pdl = physDev->downloaded_fonts; pdl; pdl = pdl->next)
        count++;

    if(count > 1)
        return FALSE;
    return TRUE;
}

164 165 166 167 168 169 170 171
/****************************************************************************
 *  get_bbox
 *
 * This retrieves the bounding box of the font in font units as well as
 * the size of the emsquare.  To avoid having to worry about mapping mode and
 * the font size we'll get the data directly from the TrueType HEAD table rather
 * than using GetOutlineTextMetrics.
 */
172
static UINT get_bbox(HDC hdc, RECT *rc)
173 174 175
{
    BYTE head[54]; /* the head table is 54 bytes long */

176 177
    if(GetFontData(hdc, MS_MAKE_TAG('h','e','a','d'), 0, head, sizeof(head)) == GDI_ERROR)
    {
178
        ERR("Can't retrieve head table\n");
179
        return 0;
180
    }
181 182 183 184 185 186 187
    if(rc)
    {
        rc->left   = (signed short)GET_BE_WORD(head + 36); /* xMin */
        rc->bottom = (signed short)GET_BE_WORD(head + 38); /* yMin */
        rc->right  = (signed short)GET_BE_WORD(head + 40); /* xMax */
        rc->top    = (signed short)GET_BE_WORD(head + 42); /* yMax */
    }
188
    return GET_BE_WORD(head + 18); /* unitsPerEm */
189 190
}

191 192 193 194 195 196
/****************************************************************************
 *  PSDRV_SelectDownloadFont
 *
 *  Set up physDev->font for a downloadable font
 *
 */
197
BOOL PSDRV_SelectDownloadFont(PHYSDEV dev)
198
{
199 200
    PSDRV_PDEVICE *physDev = get_psdrv_dev( dev );

201
    physDev->font.fontloc = Download;
202
    physDev->font.fontinfo.Download = NULL;
203 204 205 206

    return TRUE;
}

207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
static UINT calc_ppem_for_height(HDC hdc, LONG height)
{
    BYTE os2[78]; /* size of version 0 table */
    BYTE hhea[8]; /* just enough to get the ascender and descender */
    LONG ascent = 0, descent = 0;
    UINT emsize;

    if(height < 0) return -height;

    if(GetFontData(hdc, MS_MAKE_TAG('O','S','/','2'), 0, os2, sizeof(os2)) == sizeof(os2))
    {
        ascent  = GET_BE_WORD(os2 + 74); /* usWinAscent */
        descent = GET_BE_WORD(os2 + 76); /* usWinDescent */
    }

    if(ascent + descent == 0)
    {
        if(GetFontData(hdc, MS_MAKE_TAG('h','h','e','a'), 0, hhea, sizeof(hhea)) == sizeof(hhea))
        {
            ascent  =  (signed short)GET_BE_WORD(hhea + 4); /* Ascender */
            descent = -(signed short)GET_BE_WORD(hhea + 6); /* Descender */
        }
    }

    if(ascent + descent == 0) return height;

233
    emsize = get_bbox(hdc, NULL);
234 235 236 237

    return MulDiv(emsize, height, ascent + descent);
}

238 239 240 241
static inline float ps_round(float f)
{
    return (f > 0) ? (f + 0.5) : (f - 0.5);
}
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259

static BOOL is_fake_italic( HDC hdc )
{
    TEXTMETRICW tm;
    BYTE head[54]; /* the head table is 54 bytes long */
    WORD mac_style;

    GetTextMetricsW( hdc, &tm );
    if (!tm.tmItalic) return FALSE;

    if (GetFontData( hdc, MS_MAKE_TAG('h','e','a','d'), 0, head, sizeof(head) ) == GDI_ERROR)
        return FALSE;

    mac_style = GET_BE_WORD( head + 44 );
    TRACE( "mac style %04x\n", mac_style );
    return !(mac_style & 2);
}

260 261 262 263 264 265
/****************************************************************************
 *  PSDRV_WriteSetDownloadFont
 *
 *  Write setfont for download font.
 *
 */
266
BOOL PSDRV_WriteSetDownloadFont(PHYSDEV dev, BOOL vertical)
267
{
268
    PSDRV_PDEVICE *physDev = get_psdrv_dev( dev );
269 270
    char *ps_name;
    LPOUTLINETEXTMETRICA potm;
271
    DWORD len = GetOutlineTextMetricsA(dev->hdc, 0, NULL);
272
    DOWNLOAD *pdl;
273 274
    LOGFONTW lf;
    UINT ppem;
275
    XFORM xform;
276
    INT escapement;
277 278 279

    assert(physDev->font.fontloc == Download);

280 281 282
    if (!GetObjectW( GetCurrentObject(dev->hdc, OBJ_FONT), sizeof(lf), &lf ))
        return FALSE;

283
    potm = HeapAlloc(GetProcessHeap(), 0, len);
284 285 286
    if (!potm)
        return FALSE;

287
    GetOutlineTextMetricsA(dev->hdc, len, potm);
288

289
    get_download_name(dev, potm, &ps_name, vertical);
290 291
    physDev->font.fontinfo.Download = is_font_downloaded(physDev, ps_name);

292
    ppem = calc_ppem_for_height(dev->hdc, lf.lfHeight);
293

294
    /* Retrieve the world -> device transform */
295
    GetTransform(dev->hdc, 0x204, &xform);
296

297
    if(GetGraphicsMode(dev->hdc) == GM_COMPATIBLE)
298
    {
299
        if (xform.eM22 < 0) physDev->font.escapement = -physDev->font.escapement;
300 301 302 303
        xform.eM11 = xform.eM22 = fabs(xform.eM22);
        xform.eM21 = xform.eM12 = 0;
    }

304 305
    physDev->font.size.xx = ps_round(ppem * xform.eM11);
    physDev->font.size.xy = ps_round(ppem * xform.eM12);
306 307
    physDev->font.size.yx = -ps_round(ppem * xform.eM21);
    physDev->font.size.yy = -ps_round(ppem * xform.eM22);
308

309 310 311 312 313
    physDev->font.underlineThickness = potm->otmsUnderscoreSize;
    physDev->font.underlinePosition = potm->otmsUnderscorePosition;
    physDev->font.strikeoutThickness = potm->otmsStrikeoutSize;
    physDev->font.strikeoutPosition = potm->otmsStrikeoutPosition;

314
    if(physDev->font.fontinfo.Download == NULL) {
315
        RECT bbox;
316
        UINT emsize = get_bbox(dev->hdc, &bbox);
317

318
        if (!emsize) {
319 320 321 322
            HeapFree(GetProcessHeap(), 0, ps_name);
            HeapFree(GetProcessHeap(), 0, potm);
            return FALSE;
        }
323
        if(!is_room_for_font(physDev))
324
            PSDRV_EmptyDownloadList(dev, TRUE);
325

326 327 328 329 330 331
        pdl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pdl));
	pdl->ps_name = HeapAlloc(GetProcessHeap(), 0, strlen(ps_name)+1);
	strcpy(pdl->ps_name, ps_name);
	pdl->next = NULL;

        if(physDev->pi->ppd->TTRasterizer == RO_Type42) {
332
	    pdl->typeinfo.Type42 = T42_download_header(dev, ps_name, &bbox, emsize);
333
	    pdl->type = Type42;
334 335
	}
	if(pdl->typeinfo.Type42 == NULL) {
336
	    pdl->typeinfo.Type1 = T1_download_header(dev, ps_name, &bbox, emsize);
337 338
	    pdl->type = Type1;
	}
339 340
	pdl->next = physDev->downloaded_fonts;
	physDev->downloaded_fonts = pdl;
341
	physDev->font.fontinfo.Download = pdl;
342 343 344

        if(pdl->type == Type42) {
            char g_name[MAX_G_NAME + 1];
345
            get_glyph_name(dev->hdc, 0, g_name);
346
            T42_download_glyph(dev, pdl, 0, g_name);
347
        }
348 349
    }

350 351 352
    escapement = physDev->font.escapement;
    if (vertical)
        escapement += 900;
353

354
    PSDRV_WriteSetFont(dev, ps_name, physDev->font.size, escapement,
355
                        is_fake_italic( dev->hdc ));
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374

    HeapFree(GetProcessHeap(), 0, ps_name);
    HeapFree(GetProcessHeap(), 0, potm);
    return TRUE;
}

void get_glyph_name(HDC hdc, WORD index, char *name)
{
  /* FIXME */
    sprintf(name, "g%04x", index);
    return;
}

/****************************************************************************
 *  PSDRV_WriteDownloadGlyphShow
 *
 *  Download and write out a number of glyphs
 *
 */
375
BOOL PSDRV_WriteDownloadGlyphShow(PHYSDEV dev, const WORD *glyphs,
376 377
				  UINT count)
{
378
    PSDRV_PDEVICE *physDev = get_psdrv_dev( dev );
379 380 381 382 383 384 385
    UINT i;
    char g_name[MAX_G_NAME + 1];
    assert(physDev->font.fontloc == Download);

    switch(physDev->font.fontinfo.Download->type) {
    case Type42:
    for(i = 0; i < count; i++) {
386
        get_glyph_name(dev->hdc, glyphs[i], g_name);
387 388
	T42_download_glyph(dev, physDev->font.fontinfo.Download, glyphs[i], g_name);
	PSDRV_WriteGlyphShow(dev, g_name);
389 390 391 392 393
    }
    break;

    case Type1:
    for(i = 0; i < count; i++) {
394
        get_glyph_name(dev->hdc, glyphs[i], g_name);
395 396
	T1_download_glyph(dev, physDev->font.fontinfo.Download, glyphs[i], g_name);
	PSDRV_WriteGlyphShow(dev, g_name);
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
    }
    break;

    default:
        ERR("Type = %d\n", physDev->font.fontinfo.Download->type);
	assert(0);
    }
    return TRUE;
}

/****************************************************************************
 *  PSDRV_EmptyDownloadList
 *
 *  Clear the list of downloaded fonts
 *
 */
413
BOOL PSDRV_EmptyDownloadList(PHYSDEV dev, BOOL write_undef)
414
{
415
    PSDRV_PDEVICE *physDev = get_psdrv_dev( dev );
416
    DOWNLOAD *pdl, *old;
417
    static const char undef[] = "/%s findfont 40 scalefont setfont /%s undefinefont\n";
418
    char buf[sizeof(undef) + 200];
419
    const char *default_font = physDev->pi->ppd->DefaultFont ?
420 421
        physDev->pi->ppd->DefaultFont : "Courier";

422 423 424 425 426 427 428 429
    if(physDev->font.fontloc == Download) {
        physDev->font.set = FALSE;
	physDev->font.fontinfo.Download = NULL;
    }

    pdl = physDev->downloaded_fonts;
    physDev->downloaded_fonts = NULL;
    while(pdl) {
430 431
        if(write_undef) {
            sprintf(buf, undef, default_font, pdl->ps_name);
432
            PSDRV_WriteSpool(dev, buf, strlen(buf));
433 434
        }

435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
        switch(pdl->type) {
	case Type42:
	    T42_free(pdl->typeinfo.Type42);
	    break;

	case Type1:
	    T1_free(pdl->typeinfo.Type1);
	    break;

	default:
	    ERR("Type = %d\n", pdl->type);
	    assert(0);
	}

	HeapFree(GetProcessHeap(), 0, pdl->ps_name);
	old = pdl;
	pdl = pdl->next;
	HeapFree(GetProcessHeap(), 0, old);
    }
    return TRUE;
}