e2a7734aa73f328cdac33ff52621cd28e8e3954e
[reactos.git] / reactos / dll / win32 / msi / font.c
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 TRACE("Returning %s\n", debugstr_w(ret));
169 return ret;
170 }
171
172 static WCHAR *font_name_from_file( const WCHAR *filename )
173 {
174 static const WCHAR truetypeW[] = {' ','(','T','r','u','e','T','y','p','e',')',0};
175 WCHAR *name, *ret = NULL;
176
177 if ((name = load_ttf_name_id( filename, NAME_ID_FULL_FONT_NAME )))
178 {
179 if (!name[0])
180 {
181 WARN("empty font name\n");
182 msi_free( name );
183 return NULL;
184 }
185 ret = msi_alloc( (strlenW( name ) + strlenW( truetypeW ) + 1 ) * sizeof(WCHAR) );
186 strcpyW( ret, name );
187 strcatW( ret, truetypeW );
188 msi_free( name );
189 }
190 return ret;
191 }
192
193 WCHAR *msi_font_version_from_file( const WCHAR *filename )
194 {
195 static const WCHAR fmtW[] = {'%','u','.','%','u','.','0','.','0',0};
196 WCHAR *version, *p, *q, *ret = NULL;
197
198 if ((version = load_ttf_name_id( filename, NAME_ID_VERSION )))
199 {
200 int len, major = 0, minor = 0;
201 if ((p = strchrW( version, ';' ))) *p = 0;
202 p = version;
203 while (*p && !isdigitW( *p )) p++;
204 if ((q = strchrW( p, '.' )))
205 {
206 major = atoiW( p );
207 p = ++q;
208 while (*q && isdigitW( *q )) q++;
209 if (!*q || *q == ' ') minor = atoiW( p );
210 else major = 0;
211 }
212 len = strlenW( fmtW ) + 20;
213 ret = msi_alloc( len * sizeof(WCHAR) );
214 sprintfW( ret, fmtW, major, minor );
215 msi_free( version );
216 }
217 return ret;
218 }
219
220 static UINT ITERATE_RegisterFonts(MSIRECORD *row, LPVOID param)
221 {
222 MSIPACKAGE *package = param;
223 LPWSTR name;
224 LPCWSTR filename;
225 MSIFILE *file;
226 MSICOMPONENT *comp;
227 HKEY hkey1, hkey2;
228 MSIRECORD *uirow;
229 LPWSTR uipath, p;
230
231 filename = MSI_RecordGetString( row, 1 );
232 file = msi_get_loaded_file( package, filename );
233 if (!file)
234 {
235 WARN("unable to find file %s\n", debugstr_w(filename));
236 return ERROR_SUCCESS;
237 }
238 comp = msi_get_loaded_component( package, file->Component->Component );
239 if (!comp)
240 {
241 WARN("unable to find component %s\n", debugstr_w(file->Component->Component));
242 return ERROR_SUCCESS;
243 }
244 comp->Action = msi_get_component_action( package, comp );
245 if (comp->Action != INSTALLSTATE_LOCAL)
246 {
247 TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
248 return ERROR_SUCCESS;
249 }
250
251 RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont1,&hkey1);
252 RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont2,&hkey2);
253
254 if (MSI_RecordIsNull(row,2))
255 name = font_name_from_file( file->TargetPath );
256 else
257 name = msi_dup_record_field(row,2);
258
259 if (name)
260 {
261 msi_reg_set_val_str( hkey1, name, file->TargetPath);
262 msi_reg_set_val_str( hkey2, name, file->TargetPath);
263 }
264
265 msi_free(name);
266 RegCloseKey(hkey1);
267 RegCloseKey(hkey2);
268
269 /* the UI chunk */
270 uirow = MSI_CreateRecord( 1 );
271 uipath = strdupW( file->TargetPath );
272 p = strrchrW(uipath,'\\');
273 if (p) p++;
274 else p = uipath;
275 MSI_RecordSetStringW( uirow, 1, p );
276 msi_ui_actiondata( package, szRegisterFonts, uirow );
277 msiobj_release( &uirow->hdr );
278 msi_free( uipath );
279 /* FIXME: call msi_ui_progress? */
280
281 return ERROR_SUCCESS;
282 }
283
284 UINT ACTION_RegisterFonts(MSIPACKAGE *package)
285 {
286 static const WCHAR query[] = {
287 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','o','n','t','`',0};
288 MSIQUERY *view;
289 UINT rc;
290
291 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
292 if (rc != ERROR_SUCCESS)
293 return ERROR_SUCCESS;
294
295 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterFonts, package);
296 msiobj_release(&view->hdr);
297 return rc;
298 }
299
300 static UINT ITERATE_UnregisterFonts( MSIRECORD *row, LPVOID param )
301 {
302 MSIPACKAGE *package = param;
303 LPWSTR name;
304 LPCWSTR filename;
305 MSIFILE *file;
306 MSICOMPONENT *comp;
307 HKEY hkey1, hkey2;
308 MSIRECORD *uirow;
309 LPWSTR uipath, p;
310
311 filename = MSI_RecordGetString( row, 1 );
312 file = msi_get_loaded_file( package, filename );
313 if (!file)
314 {
315 WARN("unable to find file %s\n", debugstr_w(filename));
316 return ERROR_SUCCESS;
317 }
318 comp = msi_get_loaded_component( package, file->Component->Component );
319 if (!comp)
320 {
321 WARN("unable to find component %s\n", debugstr_w(file->Component->Component));
322 return ERROR_SUCCESS;
323 }
324 comp->Action = msi_get_component_action( package, comp );
325 if (comp->Action != INSTALLSTATE_ABSENT)
326 {
327 TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
328 return ERROR_SUCCESS;
329 }
330
331 RegCreateKeyW( HKEY_LOCAL_MACHINE, regfont1, &hkey1 );
332 RegCreateKeyW( HKEY_LOCAL_MACHINE, regfont2, &hkey2 );
333
334 if (MSI_RecordIsNull( row, 2 ))
335 name = font_name_from_file( file->TargetPath );
336 else
337 name = msi_dup_record_field( row, 2 );
338
339 if (name)
340 {
341 RegDeleteValueW( hkey1, name );
342 RegDeleteValueW( hkey2, name );
343 }
344
345 msi_free( name );
346 RegCloseKey( hkey1 );
347 RegCloseKey( hkey2 );
348
349 /* the UI chunk */
350 uirow = MSI_CreateRecord( 1 );
351 uipath = strdupW( file->TargetPath );
352 p = strrchrW( uipath,'\\' );
353 if (p) p++;
354 else p = uipath;
355 MSI_RecordSetStringW( uirow, 1, p );
356 msi_ui_actiondata( package, szUnregisterFonts, uirow );
357 msiobj_release( &uirow->hdr );
358 msi_free( uipath );
359 /* FIXME: call msi_ui_progress? */
360
361 return ERROR_SUCCESS;
362 }
363
364 UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
365 {
366 static const WCHAR query[] = {
367 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','o','n','t','`',0};
368 MSIQUERY *view;
369 UINT r;
370
371 r = MSI_DatabaseOpenViewW( package->db, query, &view );
372 if (r != ERROR_SUCCESS)
373 return ERROR_SUCCESS;
374
375 r = MSI_IterateRecords( view, NULL, ITERATE_UnregisterFonts, package );
376 msiobj_release( &view->hdr );
377 return r;
378 }