font.c 12.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
/*
 * Implementation of the Microsoft Installer (msi.dll)
 *
 * Copyright 2004,2005 Aric Stewart 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
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "winreg.h"
#include "wine/debug.h"
#include "msipriv.h"

WINE_DEFAULT_DEBUG_CHANNEL(msi);

typedef struct _tagTT_OFFSET_TABLE {
    USHORT uMajorVersion;
    USHORT uMinorVersion;
    USHORT uNumOfTables;
    USHORT uSearchRange;
    USHORT uEntrySelector;
    USHORT uRangeShift;
} TT_OFFSET_TABLE;

typedef struct _tagTT_TABLE_DIRECTORY {
    char szTag[4]; /* table name */
    ULONG uCheckSum; /* Check sum */
    ULONG uOffset; /* Offset from beginning of file */
    ULONG uLength; /* length of the table in bytes */
} TT_TABLE_DIRECTORY;

typedef struct _tagTT_NAME_TABLE_HEADER {
    USHORT uFSelector; /* format selector. Always 0 */
    USHORT uNRCount; /* Name Records count */
    USHORT uStorageOffset; /* Offset for strings storage,
                            * from start of the table */
} TT_NAME_TABLE_HEADER;

54 55 56
#define NAME_ID_FULL_FONT_NAME  4
#define NAME_ID_VERSION         5

57 58 59 60 61 62 63 64 65 66 67 68
typedef struct _tagTT_NAME_RECORD {
    USHORT uPlatformID;
    USHORT uEncodingID;
    USHORT uLanguageID;
    USHORT uNameID;
    USHORT uStringLength;
    USHORT uStringOffset; /* from start of storage area */
} TT_NAME_RECORD;

#define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
#define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))

69 70 71 72 73 74 75 76 77 78 79 80
static const WCHAR regfont1[] =
    {'S','o','f','t','w','a','r','e','\\',
     'M','i','c','r','o','s','o','f','t','\\',
     'W','i','n','d','o','w','s',' ','N','T','\\',
     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
     'F','o','n','t','s',0};
static const WCHAR regfont2[] =
    {'S','o','f','t','w','a','r','e','\\',
     'M','i','c','r','o','s','o','f','t','\\',
     'W','i','n','d','o','w','s','\\',
     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
     'F','o','n','t','s',0};
81 82 83 84 85

/*
 * Code based off of code located here
 * http://www.codeproject.com/gdi/fontnamefromfile.asp
 */
86
static WCHAR *load_ttf_name_id( MSIPACKAGE *package, const WCHAR *filename, DWORD id )
87 88 89 90 91 92 93 94 95 96 97
{
    TT_TABLE_DIRECTORY tblDir;
    BOOL bFound = FALSE;
    TT_OFFSET_TABLE ttOffsetTable;
    TT_NAME_TABLE_HEADER ttNTHeader;
    TT_NAME_RECORD ttRecord;
    DWORD dwRead;
    HANDLE handle;
    LPWSTR ret = NULL;
    int i;

98 99 100 101
    if (package)
        handle = msi_create_file( package, filename, GENERIC_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL );
    else
        handle = CreateFileW( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
102 103 104 105 106 107 108 109 110 111 112 113 114
    if (handle == INVALID_HANDLE_VALUE)
    {
        ERR("Unable to open font file %s\n", debugstr_w(filename));
        return NULL;
    }

    if (!ReadFile(handle,&ttOffsetTable, sizeof(TT_OFFSET_TABLE),&dwRead,NULL))
        goto end;

    ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
    ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
    ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);

115 116
    if ((ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0) &&
        (ttOffsetTable.uMajorVersion != 0x4f54 || ttOffsetTable.uMinorVersion != 0x544f))
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
        goto end;

    for (i=0; i< ttOffsetTable.uNumOfTables; i++)
    {
        if (!ReadFile(handle,&tblDir, sizeof(TT_TABLE_DIRECTORY),&dwRead,NULL))
            break;
        if (memcmp(tblDir.szTag,"name",4)==0)
        {
            bFound = TRUE;
            tblDir.uLength = SWAPLONG(tblDir.uLength);
            tblDir.uOffset = SWAPLONG(tblDir.uOffset);
            break;
        }
    }

    if (!bFound)
        goto end;

    SetFilePointer(handle, tblDir.uOffset, NULL, FILE_BEGIN);
    if (!ReadFile(handle,&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER), &dwRead,NULL))
        goto end;

    ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
    ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
    for(i=0; i<ttNTHeader.uNRCount; i++)
    {
        if (!ReadFile(handle,&ttRecord, sizeof(TT_NAME_RECORD),&dwRead,NULL))
            break;

        ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
147 148 149 150
        ttRecord.uPlatformID = SWAPWORD(ttRecord.uPlatformID);
        ttRecord.uEncodingID = SWAPWORD(ttRecord.uEncodingID);
        if (ttRecord.uNameID == id && ttRecord.uPlatformID == 3 &&
            (ttRecord.uEncodingID == 0 || ttRecord.uEncodingID == 1))
151
        {
152 153
            WCHAR *buf;
            unsigned int i;
154 155 156

            ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
            ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
157 158
            SetFilePointer(handle, tblDir.uOffset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset,
                           NULL, FILE_BEGIN);
159 160
            if (!(buf = msi_alloc_zero( ttRecord.uStringLength + sizeof(WCHAR) ))) goto end;
            dwRead = 0;
161
            ReadFile(handle, buf, ttRecord.uStringLength, &dwRead, NULL);
162 163 164 165 166 167 168
            if (dwRead % sizeof(WCHAR))
            {
                msi_free(buf);
                goto end;
            }
            for (i = 0; i < dwRead / sizeof(WCHAR); i++) buf[i] = SWAPWORD(buf[i]);
            ret = strdupW(buf);
169
            msi_free(buf);
170
            break;
171 172 173 174 175
        }
    }

end:
    CloseHandle(handle);
176 177 178
    return ret;
}

179
static WCHAR *font_name_from_file( MSIPACKAGE *package, const WCHAR *filename )
180 181 182 183
{
    static const WCHAR truetypeW[] = {' ','(','T','r','u','e','T','y','p','e',')',0};
    WCHAR *name, *ret = NULL;

184
    if ((name = load_ttf_name_id( package, filename, NAME_ID_FULL_FONT_NAME )))
185
    {
186 187 188 189 190 191
        if (!name[0])
        {
            WARN("empty font name\n");
            msi_free( name );
            return NULL;
        }
192 193 194
        ret = msi_alloc( (lstrlenW( name ) + lstrlenW( truetypeW ) + 1 ) * sizeof(WCHAR) );
        lstrcpyW( ret, name );
        lstrcatW( ret, truetypeW );
195 196 197 198
        msi_free( name );
    }
    return ret;
}
199

200
WCHAR *msi_get_font_file_version( MSIPACKAGE *package, const WCHAR *filename )
201
{
202 203
    static const WCHAR fmtW[] = {'%','u','.','%','u','.','0','.','0',0};
    WCHAR *version, *p, *q, *ret = NULL;
204

205
    if ((version = load_ttf_name_id( package, filename, NAME_ID_VERSION )))
206
    {
207
        int len, major = 0, minor = 0;
208
        if ((p = wcschr( version, ';' ))) *p = 0;
209
        p = version;
210 211
        while (*p && !iswdigit( *p )) p++;
        if ((q = wcschr( p, '.' )))
212
        {
213
            major = wcstol( p, NULL, 10 );
214
            p = ++q;
215 216
            while (*q && iswdigit( *q )) q++;
            if (!*q || *q == ' ') minor = wcstol( p, NULL, 10 );
217
            else major = 0;
218
        }
219
        len = lstrlenW( fmtW ) + 20;
220
        ret = msi_alloc( len * sizeof(WCHAR) );
221
        swprintf( ret, len, fmtW, major, minor );
222 223
        msi_free( version );
    }
224 225 226 227 228
    return ret;
}

static UINT ITERATE_RegisterFonts(MSIRECORD *row, LPVOID param)
{
229
    MSIPACKAGE *package = param;
230 231 232
    LPWSTR name;
    LPCWSTR filename;
    MSIFILE *file;
233
    MSICOMPONENT *comp;
234
    HKEY hkey1, hkey2;
235 236 237 238
    MSIRECORD *uirow;
    LPWSTR uipath, p;

    filename = MSI_RecordGetString( row, 1 );
239
    file = msi_get_loaded_file( package, filename );
240 241
    if (!file)
    {
242
        WARN("unable to find file %s\n", debugstr_w(filename));
243 244
        return ERROR_SUCCESS;
    }
245 246
    comp = msi_get_loaded_component( package, file->Component->Component );
    if (!comp)
247
    {
248
        WARN("unable to find component %s\n", debugstr_w(file->Component->Component));
249 250
        return ERROR_SUCCESS;
    }
251 252
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_LOCAL)
253
    {
254
        TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
255 256 257 258 259 260 261
        return ERROR_SUCCESS;
    }

    RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont1,&hkey1);
    RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont2,&hkey2);

    if (MSI_RecordIsNull(row,2))
262
        name = font_name_from_file( package, file->TargetPath );
263 264 265 266 267
    else
        name = msi_dup_record_field(row,2);

    if (name)
    {
268 269
        msi_reg_set_val_str( hkey1, name, file->TargetPath);
        msi_reg_set_val_str( hkey2, name, file->TargetPath);
270 271 272 273 274 275 276 277 278
    }

    msi_free(name);
    RegCloseKey(hkey1);
    RegCloseKey(hkey2);

    /* the UI chunk */
    uirow = MSI_CreateRecord( 1 );
    uipath = strdupW( file->TargetPath );
279
    p = wcsrchr(uipath,'\\');
280 281 282
    if (p) p++;
    else p = uipath;
    MSI_RecordSetStringW( uirow, 1, p );
283
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
284 285
    msiobj_release( &uirow->hdr );
    msi_free( uipath );
286
    /* FIXME: call msi_ui_progress? */
287 288 289 290 291 292

    return ERROR_SUCCESS;
}

UINT ACTION_RegisterFonts(MSIPACKAGE *package)
{
293 294 295
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','o','n','t','`',0};
    MSIQUERY *view;
296 297
    UINT rc;

298 299 300
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szRegisterFonts);

301
    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
302 303 304
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

305
    rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterFonts, package);
306
    msiobj_release(&view->hdr);
307
    return rc;
308
}
309 310 311 312 313 314 315

static UINT ITERATE_UnregisterFonts( MSIRECORD *row, LPVOID param )
{
    MSIPACKAGE *package = param;
    LPWSTR name;
    LPCWSTR filename;
    MSIFILE *file;
316
    MSICOMPONENT *comp;
317 318 319 320 321
    HKEY hkey1, hkey2;
    MSIRECORD *uirow;
    LPWSTR uipath, p;

    filename = MSI_RecordGetString( row, 1 );
322
    file = msi_get_loaded_file( package, filename );
323 324
    if (!file)
    {
325
        WARN("unable to find file %s\n", debugstr_w(filename));
326 327
        return ERROR_SUCCESS;
    }
328 329
    comp = msi_get_loaded_component( package, file->Component->Component );
    if (!comp)
330
    {
331
        WARN("unable to find component %s\n", debugstr_w(file->Component->Component));
332 333
        return ERROR_SUCCESS;
    }
334 335
    comp->Action = msi_get_component_action( package, comp );
    if (comp->Action != INSTALLSTATE_ABSENT)
336
    {
337
        TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
338 339 340 341 342 343 344
        return ERROR_SUCCESS;
    }

    RegCreateKeyW( HKEY_LOCAL_MACHINE, regfont1, &hkey1 );
    RegCreateKeyW( HKEY_LOCAL_MACHINE, regfont2, &hkey2 );

    if (MSI_RecordIsNull( row, 2 ))
345
        name = font_name_from_file( package, file->TargetPath );
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
    else
        name = msi_dup_record_field( row, 2 );

    if (name)
    {
        RegDeleteValueW( hkey1, name );
        RegDeleteValueW( hkey2, name );
    }

    msi_free( name );
    RegCloseKey( hkey1 );
    RegCloseKey( hkey2 );

    /* the UI chunk */
    uirow = MSI_CreateRecord( 1 );
    uipath = strdupW( file->TargetPath );
362
    p = wcsrchr( uipath,'\\' );
363 364 365
    if (p) p++;
    else p = uipath;
    MSI_RecordSetStringW( uirow, 1, p );
366
    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
367 368
    msiobj_release( &uirow->hdr );
    msi_free( uipath );
369
    /* FIXME: call msi_ui_progress? */
370 371 372 373 374 375

    return ERROR_SUCCESS;
}

UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
{
376 377
    static const WCHAR query[] = {
        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','o','n','t','`',0};
378
    MSIQUERY *view;
379
    UINT r;
380

381 382 383
    if (package->script == SCRIPT_NONE)
        return msi_schedule_action(package, SCRIPT_INSTALL, szUnregisterFonts);

384 385 386 387
    r = MSI_DatabaseOpenViewW( package->db, query, &view );
    if (r != ERROR_SUCCESS)
        return ERROR_SUCCESS;

388
    r = MSI_IterateRecords( view, NULL, ITERATE_UnregisterFonts, package );
389
    msiobj_release( &view->hdr );
390
    return r;
391
}