@@ -105,165 +105,109 @@ protected List<Bookmark> GetBookmarksFromPath(string placesPath)
105105 }
106106
107107 private void LoadFaviconsFromDb ( string faviconDbPath , List < Bookmark > bookmarks )
108- {
109- try
110108 {
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" ) ;
114118
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 ( ) ;
118122
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 )
123125 {
124- if ( string . IsNullOrEmpty ( bookmark . Url ) )
125- continue ;
126+ try
127+ {
128+ if ( string . IsNullOrEmpty ( bookmark . Url ) )
129+ continue ;
126130
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 ;
130134
131- var domain = uri . Host ;
135+ var domain = uri . Host ;
132136
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" ;
144148
145- cmd . Parameters . AddWithValue ( "@url" , $ "%{ domain } %") ;
149+ cmd . Parameters . AddWithValue ( "@url" , $ "%{ domain } %") ;
146150
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 ;
150154
151- var imageData = ( byte [ ] ) reader [ "data" ] ;
155+ var imageData = ( byte [ ] ) reader [ "data" ] ;
152156
153- if ( imageData is not { Length : > 0 } )
154- continue ;
157+ if ( imageData is not { Length : > 0 } )
158+ continue ;
155159
156- var faviconPath = Path . Combine ( _faviconCacheDir , $ "firefox_{ domain } .png") ;
160+ var faviconPath = Path . Combine ( _faviconCacheDir , $ "firefox_{ domain } .png") ;
157161
158- if ( ! File . Exists ( faviconPath ) )
159- {
160- if ( IsSvgData ( imageData ) )
162+ if ( ! File . Exists ( faviconPath ) )
161163 {
162- // SVG를 PNG로 변환
163- var pngData = ConvertSvgToPng ( imageData ) ;
164- if ( pngData != null )
164+ // SVG 파일인지 확인
165+ if ( IsSvgData ( imageData ) )
165166 {
166- SaveBitmapData ( pngData , faviconPath ) ;
167+ bookmark . FaviconPath = defaultIconPath ;
168+ continue ;
167169 }
168170 else
169171 {
170- // Set empty string on conversion failure (will use default icon)
171- bookmark . FaviconPath = string . Empty ;
172- continue ;
172+ SaveBitmapData ( imageData , faviconPath ) ;
173173 }
174174 }
175- else
176- {
177- // Save PNG directly
178- SaveBitmapData ( imageData , faviconPath ) ;
179- }
175+
176+ bookmark . FaviconPath = faviconPath ;
180177 }
178+ catch ( Exception ex )
179+ {
180+ Log . Exception ( $ "Failed to extract Firefox favicon: { bookmark . Url } ", ex ) ;
181+ }
182+ }
181183
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 ) ;
183192 }
184193 catch ( Exception ex )
185194 {
186- Log . Exception ( $ "Failed to extract Firefox favicon: { bookmark . Url } ", ex ) ;
195+ Log . Exception ( $ "Failed to delete temporary favicon DB : { tempDbPath } ", ex ) ;
187196 }
188197 }
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- }
199198 catch ( Exception ex )
200199 {
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 ) ;
252201 }
253202 }
254203
255204 private static bool IsSvgData ( byte [ ] data )
256205 {
257- if ( data == null || data . Length < 5 )
206+ if ( data . Length < 5 )
258207 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" ) ) ;
267211 }
268212
269213 private static void SaveBitmapData ( byte [ ] imageData , string outputPath )
0 commit comments