xref: /reactos/dll/win32/msi/font.c (revision 8a978a17)
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2004,2005 Aric Stewart for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * 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 #include <stdarg.h>
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winerror.h"
25 #include "winreg.h"
26 #include "wine/debug.h"
27 #include "msipriv.h"
28 #include "wine/unicode.h"
29 
30 WINE_DEFAULT_DEBUG_CHANNEL(msi);
31 
32 typedef struct _tagTT_OFFSET_TABLE {
33     USHORT uMajorVersion;
34     USHORT uMinorVersion;
35     USHORT uNumOfTables;
36     USHORT uSearchRange;
37     USHORT uEntrySelector;
38     USHORT uRangeShift;
39 } TT_OFFSET_TABLE;
40 
41 typedef struct _tagTT_TABLE_DIRECTORY {
42     char szTag[4]; /* table name */
43     ULONG uCheckSum; /* Check sum */
44     ULONG uOffset; /* Offset from beginning of file */
45     ULONG uLength; /* length of the table in bytes */
46 } TT_TABLE_DIRECTORY;
47 
48 typedef struct _tagTT_NAME_TABLE_HEADER {
49     USHORT uFSelector; /* format selector. Always 0 */
50     USHORT uNRCount; /* Name Records count */
51     USHORT uStorageOffset; /* Offset for strings storage,
52                             * from start of the table */
53 } TT_NAME_TABLE_HEADER;
54 
55 #define NAME_ID_FULL_FONT_NAME  4
56 #define NAME_ID_VERSION         5
57 
58 typedef struct _tagTT_NAME_RECORD {
59     USHORT uPlatformID;
60     USHORT uEncodingID;
61     USHORT uLanguageID;
62     USHORT uNameID;
63     USHORT uStringLength;
64     USHORT uStringOffset; /* from start of storage area */
65 } TT_NAME_RECORD;
66 
67 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
68 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
69 
70 static const WCHAR regfont1[] =
71     {'S','o','f','t','w','a','r','e','\\',
72      'M','i','c','r','o','s','o','f','t','\\',
73      'W','i','n','d','o','w','s',' ','N','T','\\',
74      'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
75      'F','o','n','t','s',0};
76 static const WCHAR regfont2[] =
77     {'S','o','f','t','w','a','r','e','\\',
78      'M','i','c','r','o','s','o','f','t','\\',
79      'W','i','n','d','o','w','s','\\',
80      'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
81      'F','o','n','t','s',0};
82 
83 /*
84  * Code based off of code located here
85  * http://www.codeproject.com/gdi/fontnamefromfile.asp
86  */
87 static WCHAR *load_ttf_name_id( const WCHAR *filename, DWORD id )
88 {
89     TT_TABLE_DIRECTORY tblDir;
90     BOOL bFound = FALSE;
91     TT_OFFSET_TABLE ttOffsetTable;
92     TT_NAME_TABLE_HEADER ttNTHeader;
93     TT_NAME_RECORD ttRecord;
94     DWORD dwRead;
95     HANDLE handle;
96     LPWSTR ret = NULL;
97     int i;
98 
99     handle = CreateFileW(filename ,GENERIC_READ, 0, NULL, OPEN_EXISTING,
100                     FILE_ATTRIBUTE_NORMAL, 0 );
101     if (handle == INVALID_HANDLE_VALUE)
102     {
103         ERR("Unable to open font file %s\n", debugstr_w(filename));
104         return NULL;
105     }
106 
107     if (!ReadFile(handle,&ttOffsetTable, sizeof(TT_OFFSET_TABLE),&dwRead,NULL))
108         goto end;
109 
110     ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
111     ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
112     ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
113 
114     if ((ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0) &&
115         (ttOffsetTable.uMajorVersion != 0x4f54 || ttOffsetTable.uMinorVersion != 0x544f))
116         goto end;
117 
118     for (i=0; i< ttOffsetTable.uNumOfTables; i++)
119     {
120         if (!ReadFile(handle,&tblDir, sizeof(TT_TABLE_DIRECTORY),&dwRead,NULL))
121             break;
122         if (memcmp(tblDir.szTag,"name",4)==0)
123         {
124             bFound = TRUE;
125             tblDir.uLength = SWAPLONG(tblDir.uLength);
126             tblDir.uOffset = SWAPLONG(tblDir.uOffset);
127             break;
128         }
129     }
130 
131     if (!bFound)
132         goto end;
133 
134     SetFilePointer(handle, tblDir.uOffset, NULL, FILE_BEGIN);
135     if (!ReadFile(handle,&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER), &dwRead,NULL))
136         goto end;
137 
138     ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
139     ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
140     for(i=0; i<ttNTHeader.uNRCount; i++)
141     {
142         if (!ReadFile(handle,&ttRecord, sizeof(TT_NAME_RECORD),&dwRead,NULL))
143             break;
144 
145         ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
146         ttRecord.uPlatformID = SWAPWORD(ttRecord.uPlatformID);
147         ttRecord.uEncodingID = SWAPWORD(ttRecord.uEncodingID);
148         if (ttRecord.uNameID == id && ttRecord.uPlatformID == 3 &&
149             (ttRecord.uEncodingID == 0 || ttRecord.uEncodingID == 1))
150         {
151             WCHAR *buf;
152             unsigned int i;
153 
154             ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
155             ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
156             SetFilePointer(handle, tblDir.uOffset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset,
157                            NULL, FILE_BEGIN);
158             if (!(buf = msi_alloc_zero( ttRecord.uStringLength + sizeof(WCHAR) ))) goto end;
159             dwRead = 0;
160             ReadFile(handle, buf, ttRecord.uStringLength, &dwRead, NULL);
161             if (dwRead % sizeof(WCHAR))
162             {
163                 msi_free(buf);
164                 goto end;
165             }
166             for (i = 0; i < dwRead / sizeof(WCHAR); i++) buf[i] = SWAPWORD(buf[i]);
167             ret = strdupW(buf);
168             msi_free(buf);
169             break;
170         }
171     }
172 
173 end:
174     CloseHandle(handle);
175     return ret;
176 }
177 
178 static WCHAR *font_name_from_file( const WCHAR *filename )
179 {
180     static const WCHAR truetypeW[] = {' ','(','T','r','u','e','T','y','p','e',')',0};
181     WCHAR *name, *ret = NULL;
182 
183     if ((name = load_ttf_name_id( filename, NAME_ID_FULL_FONT_NAME )))
184     {
185         if (!name[0])
186         {
187             WARN("empty font name\n");
188             msi_free( name );
189             return NULL;
190         }
191         ret = msi_alloc( (strlenW( name ) + strlenW( truetypeW ) + 1 ) * sizeof(WCHAR) );
192         strcpyW( ret, name );
193         strcatW( ret, truetypeW );
194         msi_free( name );
195     }
196     return ret;
197 }
198 
199 WCHAR *msi_font_version_from_file( const WCHAR *filename )
200 {
201     static const WCHAR fmtW[] = {'%','u','.','%','u','.','0','.','0',0};
202     WCHAR *version, *p, *q, *ret = NULL;
203 
204     if ((version = load_ttf_name_id( filename, NAME_ID_VERSION )))
205     {
206         int len, major = 0, minor = 0;
207         if ((p = strchrW( version, ';' ))) *p = 0;
208         p = version;
209         while (*p && !isdigitW( *p )) p++;
210         if ((q = strchrW( p, '.' )))
211         {
212             major = atoiW( p );
213             p = ++q;
214             while (*q && isdigitW( *q )) q++;
215             if (!*q || *q == ' ') minor = atoiW( p );
216             else major = 0;
217         }
218         len = strlenW( fmtW ) + 20;
219         ret = msi_alloc( len * sizeof(WCHAR) );
220         sprintfW( ret, fmtW, major, minor );
221         msi_free( version );
222     }
223     return ret;
224 }
225 
226 static UINT ITERATE_RegisterFonts(MSIRECORD *row, LPVOID param)
227 {
228     MSIPACKAGE *package = param;
229     LPWSTR name;
230     LPCWSTR filename;
231     MSIFILE *file;
232     MSICOMPONENT *comp;
233     HKEY hkey1, hkey2;
234     MSIRECORD *uirow;
235     LPWSTR uipath, p;
236 
237     filename = MSI_RecordGetString( row, 1 );
238     file = msi_get_loaded_file( package, filename );
239     if (!file)
240     {
241         WARN("unable to find file %s\n", debugstr_w(filename));
242         return ERROR_SUCCESS;
243     }
244     comp = msi_get_loaded_component( package, file->Component->Component );
245     if (!comp)
246     {
247         WARN("unable to find component %s\n", debugstr_w(file->Component->Component));
248         return ERROR_SUCCESS;
249     }
250     comp->Action = msi_get_component_action( package, comp );
251     if (comp->Action != INSTALLSTATE_LOCAL)
252     {
253         TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
254         return ERROR_SUCCESS;
255     }
256 
257     RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont1,&hkey1);
258     RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont2,&hkey2);
259 
260     if (MSI_RecordIsNull(row,2))
261         name = font_name_from_file( file->TargetPath );
262     else
263         name = msi_dup_record_field(row,2);
264 
265     if (name)
266     {
267         msi_reg_set_val_str( hkey1, name, file->TargetPath);
268         msi_reg_set_val_str( hkey2, name, file->TargetPath);
269     }
270 
271     msi_free(name);
272     RegCloseKey(hkey1);
273     RegCloseKey(hkey2);
274 
275     /* the UI chunk */
276     uirow = MSI_CreateRecord( 1 );
277     uipath = strdupW( file->TargetPath );
278     p = strrchrW(uipath,'\\');
279     if (p) p++;
280     else p = uipath;
281     MSI_RecordSetStringW( uirow, 1, p );
282     MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
283     msiobj_release( &uirow->hdr );
284     msi_free( uipath );
285     /* FIXME: call msi_ui_progress? */
286 
287     return ERROR_SUCCESS;
288 }
289 
290 UINT ACTION_RegisterFonts(MSIPACKAGE *package)
291 {
292     static const WCHAR query[] = {
293         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','o','n','t','`',0};
294     MSIQUERY *view;
295     UINT rc;
296 
297     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
298     if (rc != ERROR_SUCCESS)
299         return ERROR_SUCCESS;
300 
301     rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterFonts, package);
302     msiobj_release(&view->hdr);
303     return rc;
304 }
305 
306 static UINT ITERATE_UnregisterFonts( MSIRECORD *row, LPVOID param )
307 {
308     MSIPACKAGE *package = param;
309     LPWSTR name;
310     LPCWSTR filename;
311     MSIFILE *file;
312     MSICOMPONENT *comp;
313     HKEY hkey1, hkey2;
314     MSIRECORD *uirow;
315     LPWSTR uipath, p;
316 
317     filename = MSI_RecordGetString( row, 1 );
318     file = msi_get_loaded_file( package, filename );
319     if (!file)
320     {
321         WARN("unable to find file %s\n", debugstr_w(filename));
322         return ERROR_SUCCESS;
323     }
324     comp = msi_get_loaded_component( package, file->Component->Component );
325     if (!comp)
326     {
327         WARN("unable to find component %s\n", debugstr_w(file->Component->Component));
328         return ERROR_SUCCESS;
329     }
330     comp->Action = msi_get_component_action( package, comp );
331     if (comp->Action != INSTALLSTATE_ABSENT)
332     {
333         TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
334         return ERROR_SUCCESS;
335     }
336 
337     RegCreateKeyW( HKEY_LOCAL_MACHINE, regfont1, &hkey1 );
338     RegCreateKeyW( HKEY_LOCAL_MACHINE, regfont2, &hkey2 );
339 
340     if (MSI_RecordIsNull( row, 2 ))
341         name = font_name_from_file( file->TargetPath );
342     else
343         name = msi_dup_record_field( row, 2 );
344 
345     if (name)
346     {
347         RegDeleteValueW( hkey1, name );
348         RegDeleteValueW( hkey2, name );
349     }
350 
351     msi_free( name );
352     RegCloseKey( hkey1 );
353     RegCloseKey( hkey2 );
354 
355     /* the UI chunk */
356     uirow = MSI_CreateRecord( 1 );
357     uipath = strdupW( file->TargetPath );
358     p = strrchrW( uipath,'\\' );
359     if (p) p++;
360     else p = uipath;
361     MSI_RecordSetStringW( uirow, 1, p );
362     MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
363     msiobj_release( &uirow->hdr );
364     msi_free( uipath );
365     /* FIXME: call msi_ui_progress? */
366 
367     return ERROR_SUCCESS;
368 }
369 
370 UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
371 {
372     static const WCHAR query[] = {
373         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','o','n','t','`',0};
374     MSIQUERY *view;
375     UINT r;
376 
377     r = MSI_DatabaseOpenViewW( package->db, query, &view );
378     if (r != ERROR_SUCCESS)
379         return ERROR_SUCCESS;
380 
381     r = MSI_IterateRecords( view, NULL, ITERATE_UnregisterFonts, package );
382     msiobj_release( &view->hdr );
383     return r;
384 }
385