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