xref: /reactos/dll/shellext/fontext/CFontCache.cpp (revision 53221834)
1 /*
2  * PROJECT:     ReactOS Font Shell Extension
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     font list cache handling
5  * COPYRIGHT:   Copyright 2019-2021 Mark Jansen <mark.jansen@reactos.org>
6  */
7 
8 #include "precomp.h"
9 
10 WINE_DEFAULT_DEBUG_CHANNEL(fontext);
11 
12 CFontCache* g_FontCache = NULL;
13 
14 CFontInfo::CFontInfo(LPCWSTR name)
15     : m_Name(name)
16     , m_FileRead(false)
17     , m_AttrsRead(false)
18     , m_FileWriteTime({})
19     , m_dwFileAttributes(0)
20 {
21     m_FileSize.QuadPart = 0;
22 }
23 
24 const CStringW& CFontInfo::Name() const
25 {
26     return m_Name;
27 }
28 
29 const bool CFontInfo::Valid() const
30 {
31     return !m_Name.IsEmpty();
32 }
33 
34 const CStringW& CFontInfo::File()
35 {
36     if (!m_FileRead)
37     {
38         if (Valid())
39         {
40             // Read the filename stored in the registry.
41             // This can be either a filename or a full path
42             CRegKey key;
43             if (key.Open(FONT_HIVE, FONT_KEY, KEY_READ) == ERROR_SUCCESS)
44             {
45                 CStringW Value;
46                 DWORD dwAllocated = 128;
47                 LSTATUS Status;
48                 do
49                 {
50                     DWORD dwSize = dwAllocated;
51                     PWSTR Buffer = Value.GetBuffer(dwSize);
52                     Status = key.QueryStringValue(m_Name, Buffer, &dwSize);
53                     Value.ReleaseBuffer(dwSize);
54                     if (Status == ERROR_SUCCESS)
55                     {
56                         // Ensure we do not re-use the same string object, by passing it a PCWSTR
57                         m_File = Value.GetString();
58                         break;
59                     }
60                     dwAllocated += 128;
61                 } while (Status == ERROR_MORE_DATA);
62             }
63         }
64         m_FileRead = true;
65     }
66     return m_File;
67 }
68 
69 void CFontInfo::ReadAttrs()
70 {
71     CStringW File = g_FontCache->Filename(this, true);
72 
73     m_AttrsRead = true;
74 
75     WIN32_FIND_DATAW findFileData;
76     HANDLE hFile = FindFirstFileW(File, &findFileData);
77     if (hFile != INVALID_HANDLE_VALUE)
78     {
79 
80         // File write time
81         FileTimeToLocalFileTime(&findFileData.ftLastWriteTime, &m_FileWriteTime);
82 
83         // File size
84         m_FileSize.HighPart = findFileData.nFileSizeHigh;
85         m_FileSize.LowPart = findFileData.nFileSizeLow;
86 
87         m_dwFileAttributes = findFileData.dwFileAttributes;
88         FindClose(hFile);
89     }
90 }
91 
92 const LARGE_INTEGER& CFontInfo::FileSize()
93 {
94     if (!m_AttrsRead)
95         ReadAttrs();
96 
97     return m_FileSize;
98 }
99 
100 const FILETIME& CFontInfo::FileWriteTime()
101 {
102     if (!m_AttrsRead)
103         ReadAttrs();
104 
105     return m_FileWriteTime;
106 }
107 
108 DWORD CFontInfo::FileAttributes()
109 {
110     if (!m_AttrsRead)
111         ReadAttrs();
112 
113     return m_dwFileAttributes;
114 }
115 
116 CFontCache::CFontCache()
117 {
118 }
119 
120 void CFontCache::SetFontDir(const LPCWSTR Path)
121 {
122     if (m_FontFolderPath.IsEmpty())
123         m_FontFolderPath = Path;
124 }
125 
126 size_t CFontCache::Size()
127 {
128     if (m_Fonts.GetCount() == 0u)
129         Read();
130 
131     return m_Fonts.GetCount();
132 }
133 
134 CStringW CFontCache::Name(size_t Index)
135 {
136     if (m_Fonts.GetCount() == 0u)
137         Read();
138 
139     if (Index >= m_Fonts.GetCount())
140         return CStringW();
141 
142     return m_Fonts[Index].Name();
143 }
144 
145 CFontInfo* CFontCache::Find(const FontPidlEntry* fontEntry)
146 {
147     if (fontEntry->Index < m_Fonts.GetCount())
148     {
149         if (m_Fonts[fontEntry->Index].Name().CompareNoCase(fontEntry->Name) == 0)
150             return &m_Fonts[fontEntry->Index];
151     }
152 
153     for (UINT n = 0; n < Size(); ++n)
154     {
155         if (m_Fonts[n].Name().CompareNoCase(fontEntry->Name) == 0)
156         {
157             return &m_Fonts[n];
158         }
159     }
160     return nullptr;
161 }
162 
163 
164 CStringW CFontCache::Filename(CFontInfo* info, bool alwaysFullPath)
165 {
166     CStringW File;
167     if (info)
168     {
169         File = info->File();
170 
171         if (!File.IsEmpty() && alwaysFullPath)
172         {
173             // Ensure this is a full path
174             if (PathIsRelativeW(File))
175             {
176                 File = m_FontFolderPath + File;
177             }
178         }
179     }
180 
181     return File;
182 }
183 
184 void CFontCache::Insert(CAtlList<CFontInfo>& fonts, const CStringW& KeyName)
185 {
186     POSITION it = fonts.GetHeadPosition();
187     while (it != NULL)
188     {
189         POSITION lastit = it;
190         const CFontInfo& info = fonts.GetNext(it);
191         if (info.Name().CompareNoCase(KeyName) >= 0)
192         {
193             fonts.InsertBefore(lastit, CFontInfo(KeyName));
194             return;
195         }
196     }
197     fonts.AddTail(CFontInfo(KeyName));
198 }
199 
200 void CFontCache::Read()
201 {
202     CAtlList<CFontInfo> fonts;
203     CRegKey key;
204 
205     // Enumerate all registered font names
206     if (key.Open(FONT_HIVE, FONT_KEY, KEY_READ) == ERROR_SUCCESS)
207     {
208         LSTATUS Status;
209         DWORD dwAllocated = 128;
210         DWORD ilIndex = 0;
211         CStringW KeyName;
212         do
213         {
214             DWORD dwSize = dwAllocated;
215             PWSTR Buffer = KeyName.GetBuffer(dwSize);
216             Status = RegEnumValueW(key.m_hKey, ilIndex, Buffer, &dwSize, NULL, NULL, NULL, NULL);
217             KeyName.ReleaseBuffer(dwSize);
218             if (Status == ERROR_SUCCESS)
219             {
220                 // Insert will create an ordered list
221                 Insert(fonts, KeyName);
222                 ilIndex++;
223                 continue;
224             }
225             if (Status == ERROR_NO_MORE_ITEMS)
226                 break;
227             else if (Status == ERROR_MORE_DATA)
228             {
229                 dwAllocated += 128;
230             }
231         } while (Status == ERROR_MORE_DATA || Status == ERROR_SUCCESS);
232     }
233 
234     // Move the fonts from a list to an array (for easy indexing)
235     m_Fonts.SetCount(fonts.GetCount());
236     size_t Index = 0;
237     POSITION it = fonts.GetHeadPosition();
238     while (it != NULL)
239     {
240         m_Fonts[Index] = fonts.GetNext(it);
241         Index++;
242     }
243 }
244 
245