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