@@ -105,165 +105,109 @@ protected List<Bookmark> GetBookmarksFromPath(string placesPath)
105
105
}
106
106
107
107
private void LoadFaviconsFromDb ( string faviconDbPath , List < Bookmark > bookmarks )
108
- {
109
- try
110
108
{
111
- // Use a copy to avoid lock issues with the original file
112
- var tempDbPath = Path . Combine ( _faviconCacheDir , $ "tempfavicons_{ Guid . NewGuid ( ) } .sqlite") ;
113
- File . Copy ( faviconDbPath , tempDbPath , true ) ;
109
+ try
110
+ {
111
+ // Use a copy to avoid lock issues with the original file
112
+ var tempDbPath = Path . Combine ( _faviconCacheDir , $ "tempfavicons_{ Guid . NewGuid ( ) } .sqlite") ;
113
+ File . Copy ( faviconDbPath , tempDbPath , true ) ;
114
+
115
+ var defaultIconPath = Path . Combine (
116
+ Path . GetDirectoryName ( typeof ( FirefoxBookmarkLoaderBase ) . Assembly . Location ) ,
117
+ "bookmark.png" ) ;
114
118
115
- string dbPath = string . Format ( DbPathFormat , tempDbPath ) ;
116
- using var connection = new SqliteConnection ( dbPath ) ;
117
- connection . Open ( ) ;
119
+ string dbPath = string . Format ( DbPathFormat , tempDbPath ) ;
120
+ using var connection = new SqliteConnection ( dbPath ) ;
121
+ connection . Open ( ) ;
118
122
119
- // Get favicons based on bookmark URLs
120
- foreach ( var bookmark in bookmarks )
121
- {
122
- try
123
+ // Get favicons based on bookmark URLs
124
+ foreach ( var bookmark in bookmarks )
123
125
{
124
- if ( string . IsNullOrEmpty ( bookmark . Url ) )
125
- continue ;
126
+ try
127
+ {
128
+ if ( string . IsNullOrEmpty ( bookmark . Url ) )
129
+ continue ;
126
130
127
- // Extract domain from URL
128
- if ( ! Uri . TryCreate ( bookmark . Url , UriKind . Absolute , out Uri uri ) )
129
- continue ;
131
+ // Extract domain from URL
132
+ if ( ! Uri . TryCreate ( bookmark . Url , UriKind . Absolute , out Uri uri ) )
133
+ continue ;
130
134
131
- var domain = uri . Host ;
135
+ var domain = uri . Host ;
132
136
133
- // Query for latest Firefox version favicon structure
134
- using var cmd = connection . CreateCommand ( ) ;
135
- cmd . CommandText = @"
136
- SELECT i.data
137
- FROM moz_icons i
138
- JOIN moz_icons_to_pages ip ON i.id = ip.icon_id
139
- JOIN moz_pages_w_icons p ON ip.page_id = p.id
140
- WHERE p.page_url LIKE @url
141
- AND i.data IS NOT NULL
142
- ORDER BY i.width DESC -- Select largest icon available
143
- LIMIT 1" ;
137
+ // Query for latest Firefox version favicon structure
138
+ using var cmd = connection . CreateCommand ( ) ;
139
+ cmd . CommandText = @"
140
+ SELECT i.data
141
+ FROM moz_icons i
142
+ JOIN moz_icons_to_pages ip ON i.id = ip.icon_id
143
+ JOIN moz_pages_w_icons p ON ip.page_id = p.id
144
+ WHERE p.page_url LIKE @url
145
+ AND i.data IS NOT NULL
146
+ ORDER BY i.width DESC -- Select largest icon available
147
+ LIMIT 1" ;
144
148
145
- cmd . Parameters . AddWithValue ( "@url" , $ "%{ domain } %") ;
149
+ cmd . Parameters . AddWithValue ( "@url" , $ "%{ domain } %") ;
146
150
147
- using var reader = cmd . ExecuteReader ( ) ;
148
- if ( ! reader . Read ( ) || reader . IsDBNull ( 0 ) )
149
- continue ;
151
+ using var reader = cmd . ExecuteReader ( ) ;
152
+ if ( ! reader . Read ( ) || reader . IsDBNull ( 0 ) )
153
+ continue ;
150
154
151
- var imageData = ( byte [ ] ) reader [ "data" ] ;
155
+ var imageData = ( byte [ ] ) reader [ "data" ] ;
152
156
153
- if ( imageData is not { Length : > 0 } )
154
- continue ;
157
+ if ( imageData is not { Length : > 0 } )
158
+ continue ;
155
159
156
- var faviconPath = Path . Combine ( _faviconCacheDir , $ "firefox_{ domain } .png") ;
160
+ var faviconPath = Path . Combine ( _faviconCacheDir , $ "firefox_{ domain } .png") ;
157
161
158
- if ( ! File . Exists ( faviconPath ) )
159
- {
160
- if ( IsSvgData ( imageData ) )
162
+ if ( ! File . Exists ( faviconPath ) )
161
163
{
162
- // SVG를 PNG로 변환
163
- var pngData = ConvertSvgToPng ( imageData ) ;
164
- if ( pngData != null )
164
+ // SVG 파일인지 확인
165
+ if ( IsSvgData ( imageData ) )
165
166
{
166
- SaveBitmapData ( pngData , faviconPath ) ;
167
+ bookmark . FaviconPath = defaultIconPath ;
168
+ continue ;
167
169
}
168
170
else
169
171
{
170
- // Set empty string on conversion failure (will use default icon)
171
- bookmark . FaviconPath = string . Empty ;
172
- continue ;
172
+ SaveBitmapData ( imageData , faviconPath ) ;
173
173
}
174
174
}
175
- else
176
- {
177
- // Save PNG directly
178
- SaveBitmapData ( imageData , faviconPath ) ;
179
- }
175
+
176
+ bookmark . FaviconPath = faviconPath ;
180
177
}
178
+ catch ( Exception ex )
179
+ {
180
+ Log . Exception ( $ "Failed to extract Firefox favicon: { bookmark . Url } ", ex ) ;
181
+ }
182
+ }
181
183
182
- bookmark . FaviconPath = faviconPath ;
184
+ // https://github.com/dotnet/efcore/issues/26580
185
+ SqliteConnection . ClearPool ( connection ) ;
186
+ connection . Close ( ) ;
187
+
188
+ // Delete temporary file
189
+ try
190
+ {
191
+ File . Delete ( tempDbPath ) ;
183
192
}
184
193
catch ( Exception ex )
185
194
{
186
- Log . Exception ( $ "Failed to extract Firefox favicon: { bookmark . Url } ", ex ) ;
195
+ Log . Exception ( $ "Failed to delete temporary favicon DB : { tempDbPath } ", ex ) ;
187
196
}
188
197
}
189
-
190
- // https://github.com/dotnet/efcore/issues/26580
191
- SqliteConnection . ClearPool ( connection ) ;
192
- connection . Close ( ) ;
193
-
194
- // Delete temporary file
195
- try
196
- {
197
- File . Delete ( tempDbPath ) ;
198
- }
199
198
catch ( Exception ex )
200
199
{
201
- Log . Exception ( $ "Failed to delete temporary favicon DB: { tempDbPath } ", ex ) ;
202
- }
203
- }
204
- catch ( Exception ex )
205
- {
206
- Log . Exception ( $ "Failed to load Firefox favicon DB: { faviconDbPath } ", ex ) ;
207
- }
208
- }
209
-
210
- private byte [ ] ConvertSvgToPng ( byte [ ] svgData )
211
- {
212
- try
213
- {
214
- // Create SKSvg object from SVG data
215
- using var stream = new MemoryStream ( svgData ) ;
216
- var svg = new SkiaSharp . Extended . Svg . SKSvg ( ) ;
217
- svg . Load ( stream ) ;
218
-
219
- // Set default values if SVG size is invalid or missing
220
- float width = svg . Picture . CullRect . Width > 0 ? svg . Picture . CullRect . Width : 32 ;
221
- float height = svg . Picture . CullRect . Height > 0 ? svg . Picture . CullRect . Height : 32 ;
222
-
223
- // Calculate scale for 32x32 favicon size
224
- float scaleX = 32 / width ;
225
- float scaleY = 32 / height ;
226
- float scale = Math . Min ( scaleX , scaleY ) ;
227
-
228
- // Calculate final image dimensions (maintaining aspect ratio)
229
- int finalWidth = ( int ) ( width * scale ) ;
230
- int finalHeight = ( int ) ( height * scale ) ;
231
-
232
- // Render to PNG
233
- using var surface = SkiaSharp . SKSurface . Create ( new SkiaSharp . SKImageInfo ( finalWidth , finalHeight ) ) ;
234
- var canvas = surface . Canvas ;
235
-
236
- // Set transparent background
237
- canvas . Clear ( SkiaSharp . SKColors . Transparent ) ;
238
-
239
- // Draw SVG (scaled to fit)
240
- canvas . Scale ( scale ) ;
241
- canvas . DrawPicture ( svg . Picture ) ;
242
-
243
- // Extract image data
244
- using var image = surface . Snapshot ( ) ;
245
- using var data = image . Encode ( SkiaSharp . SKEncodedImageFormat . Png , 100 ) ;
246
- return data . ToArray ( ) ;
247
- }
248
- catch ( Exception ex )
249
- {
250
- Log . Exception ( "SVG to PNG conversion failed" , ex ) ;
251
- return null ;
200
+ Log . Exception ( $ "Failed to load Firefox favicon DB: { faviconDbPath } ", ex ) ;
252
201
}
253
202
}
254
203
255
204
private static bool IsSvgData ( byte [ ] data )
256
205
{
257
- if ( data == null || data . Length < 5 )
206
+ if ( data . Length < 5 )
258
207
return false ;
259
-
260
- // Check SVG file signature
261
- // Verify SVG XML header starting with ASCII
262
- string header = System . Text . Encoding . ASCII . GetString ( data , 0 , Math . Min ( data . Length , 200 ) ) . ToLower ( ) ;
263
-
264
- return header . Contains ( "<svg" ) ||
265
- header . StartsWith ( "<?xml" ) && header . Contains ( "<svg" ) ||
266
- header . Contains ( "image/svg+xml" ) ;
208
+ string start = System . Text . Encoding . ASCII . GetString ( data , 0 , Math . Min ( 100 , data . Length ) ) ;
209
+ return start . Contains ( "<svg" ) ||
210
+ ( start . StartsWith ( "<?xml" ) && start . Contains ( "<svg" ) ) ;
267
211
}
268
212
269
213
private static void SaveBitmapData ( byte [ ] imageData , string outputPath )
0 commit comments