msi/tests: automation: Add tests for StringList::_NewEnum.
[wine] / dlls / 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 <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 typedef struct _tagTT_NAME_RECORD {
56     USHORT uPlatformID;
57     USHORT uEncodingID;
58     USHORT uLanguageID;
59     USHORT uNameID;
60     USHORT uStringLength;
61     USHORT uStringOffset; /* from start of storage area */
62 } TT_NAME_RECORD;
63
64 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
65 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
66
67 static const WCHAR szRegisterFonts[] =
68     {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
69
70 /*
71  * Code based off of code located here
72  * http://www.codeproject.com/gdi/fontnamefromfile.asp
73  *
74  * Using string index 4 (full font name) instead of 1 (family name)
75  */
76 static LPWSTR load_ttfname_from(LPCWSTR filename)
77 {
78     TT_TABLE_DIRECTORY tblDir;
79     BOOL bFound = FALSE;
80     TT_OFFSET_TABLE ttOffsetTable;
81     TT_NAME_TABLE_HEADER ttNTHeader;
82     TT_NAME_RECORD ttRecord;
83     DWORD dwRead;
84     HANDLE handle;
85     LPWSTR ret = NULL;
86     int i;
87
88     handle = CreateFileW(filename ,GENERIC_READ, 0, NULL, OPEN_EXISTING,
89                     FILE_ATTRIBUTE_NORMAL, 0 );
90     if (handle == INVALID_HANDLE_VALUE)
91     {
92         ERR("Unable to open font file %s\n", debugstr_w(filename));
93         return NULL;
94     }
95
96     if (!ReadFile(handle,&ttOffsetTable, sizeof(TT_OFFSET_TABLE),&dwRead,NULL))
97         goto end;
98
99     ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
100     ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
101     ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
102
103     if (ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
104         goto end;
105
106     for (i=0; i< ttOffsetTable.uNumOfTables; i++)
107     {
108         if (!ReadFile(handle,&tblDir, sizeof(TT_TABLE_DIRECTORY),&dwRead,NULL))
109             break;
110         if (memcmp(tblDir.szTag,"name",4)==0)
111         {
112             bFound = TRUE;
113             tblDir.uLength = SWAPLONG(tblDir.uLength);
114             tblDir.uOffset = SWAPLONG(tblDir.uOffset);
115             break;
116         }
117     }
118
119     if (!bFound)
120         goto end;
121
122     SetFilePointer(handle, tblDir.uOffset, NULL, FILE_BEGIN);
123     if (!ReadFile(handle,&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER), &dwRead,NULL))
124         goto end;
125
126     ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
127     ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
128     bFound = FALSE;
129     for(i=0; i<ttNTHeader.uNRCount; i++)
130     {
131         if (!ReadFile(handle,&ttRecord, sizeof(TT_NAME_RECORD),&dwRead,NULL))
132             break;
133
134         ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
135         /* 4 is the Full Font Name */
136         if(ttRecord.uNameID == 4)
137         {
138             int nPos;
139             LPSTR buf;
140             static const char tt[] = " (TrueType)";
141
142             ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
143             ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
144             nPos = SetFilePointer(handle, 0, NULL, FILE_CURRENT);
145             SetFilePointer(handle, tblDir.uOffset +
146                             ttRecord.uStringOffset +
147                             ttNTHeader.uStorageOffset,
148                             NULL, FILE_BEGIN);
149             buf = msi_alloc_zero( ttRecord.uStringLength + 1 + strlen(tt) );
150             ReadFile(handle, buf, ttRecord.uStringLength, &dwRead, NULL);
151             if (strlen(buf) > 0)
152             {
153                 strcat(buf,tt);
154                 ret = strdupAtoW(buf);
155                 msi_free(buf);
156                 break;
157             }
158
159             msi_free(buf);
160             SetFilePointer(handle,nPos, NULL, FILE_BEGIN);
161         }
162     }
163
164 end:
165     CloseHandle(handle);
166
167     TRACE("Returning fontname %s\n",debugstr_w(ret));
168     return ret;
169 }
170
171 static UINT ITERATE_RegisterFonts(MSIRECORD *row, LPVOID param)
172 {
173     MSIPACKAGE *package = (MSIPACKAGE*)param;
174     LPWSTR name;
175     LPCWSTR filename;
176     MSIFILE *file;
177     static const WCHAR regfont1[] =
178         {'S','o','f','t','w','a','r','e','\\',
179          'M','i','c','r','o','s','o','f','t','\\',
180          'W','i','n','d','o','w','s',' ','N','T','\\',
181          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
182          'F','o','n','t','s',0};
183     static const WCHAR regfont2[] =
184         {'S','o','f','t','w','a','r','e','\\',
185          'M','i','c','r','o','s','o','f','t','\\',
186          'W','i','n','d','o','w','s','\\',
187          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
188          'F','o','n','t','s',0};
189     HKEY hkey1;
190     HKEY hkey2;
191     MSIRECORD *uirow;
192     LPWSTR uipath, p;
193
194     filename = MSI_RecordGetString( row, 1 );
195     file = get_loaded_file( package, filename );
196     if (!file)
197     {
198         ERR("Unable to load file\n");
199         return ERROR_SUCCESS;
200     }
201
202     /* check to make sure that component is installed */
203     if (!ACTION_VerifyComponentForAction( file->Component, INSTALLSTATE_LOCAL))
204     {
205         TRACE("Skipping: Component not scheduled for install\n");
206         return ERROR_SUCCESS;
207     }
208
209     RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont1,&hkey1);
210     RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont2,&hkey2);
211
212     if (MSI_RecordIsNull(row,2))
213         name = load_ttfname_from( file->TargetPath );
214     else
215         name = msi_dup_record_field(row,2);
216
217     if (name)
218     {
219         msi_reg_set_val_str( hkey1, name, file->TargetPath);
220         msi_reg_set_val_str( hkey2, name, file->TargetPath);
221     }
222
223     msi_free(name);
224     RegCloseKey(hkey1);
225     RegCloseKey(hkey2);
226
227     /* the UI chunk */
228     uirow = MSI_CreateRecord( 1 );
229     uipath = strdupW( file->TargetPath );
230     p = strrchrW(uipath,'\\');
231     if (p) p++;
232     else p = uipath;
233     MSI_RecordSetStringW( uirow, 1, p );
234     ui_actiondata( package, szRegisterFonts, uirow);
235     msiobj_release( &uirow->hdr );
236     msi_free( uipath );
237     /* FIXME: call ui_progress? */
238
239     return ERROR_SUCCESS;
240 }
241
242 UINT ACTION_RegisterFonts(MSIPACKAGE *package)
243 {
244     UINT rc;
245     MSIQUERY * view;
246     static const WCHAR ExecSeqQuery[] =
247         {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
248          '`','F','o','n','t','`',0};
249
250     rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
251     if (rc != ERROR_SUCCESS)
252     {
253         TRACE("MSI_DatabaseOpenViewW failed: %d\n", rc);
254         return ERROR_SUCCESS;
255     }
256
257     MSI_IterateRecords(view, NULL, ITERATE_RegisterFonts, package);
258     msiobj_release(&view->hdr);
259
260     return ERROR_SUCCESS;
261 }