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