diff --git a/src/NexusReader.Infrastructure/Services/BookStorageService.cs b/src/NexusReader.Infrastructure/Services/BookStorageService.cs index 9af4ee5..d1f844f 100644 --- a/src/NexusReader.Infrastructure/Services/BookStorageService.cs +++ b/src/NexusReader.Infrastructure/Services/BookStorageService.cs @@ -27,6 +27,7 @@ public class BookStorageService : IBookStorageService var uploadsFolder = Path.Combine(_environment.WebRootPath, "uploads"); EnsureDirectoryExists(uploadsFolder); + fileName = SanitizeFileName(fileName); var uniqueFileName = $"{Guid.NewGuid()}_{fileName}"; var filePath = Path.Combine(uploadsFolder, uniqueFileName); @@ -52,6 +53,7 @@ public class BookStorageService : IBookStorageService var coversFolder = Path.Combine(_environment.WebRootPath, "covers"); EnsureDirectoryExists(coversFolder); + fileName = SanitizeFileName(fileName); var uniqueFileName = $"{Guid.NewGuid()}_{fileName}"; var filePath = Path.Combine(coversFolder, uniqueFileName); @@ -63,6 +65,25 @@ public class BookStorageService : IBookStorageService return $"covers/{uniqueFileName}"; } + private string SanitizeFileName(string fileName) + { + if (string.IsNullOrEmpty(fileName)) return fileName; + + var sanitized = fileName + .Replace('\u00A0', ' ') + .Replace('\u2007', ' ') + .Replace('\u200B', ' ') + .Replace('\u202F', ' '); + + var invalidChars = Path.GetInvalidFileNameChars(); + foreach (var c in invalidChars) + { + sanitized = sanitized.Replace(c, '_'); + } + + return sanitized; + } + private void EnsureDirectoryExists(string path) { if (!Directory.Exists(path)) diff --git a/src/NexusReader.Infrastructure/Services/EpubReaderService.cs b/src/NexusReader.Infrastructure/Services/EpubReaderService.cs index 6079d42..4154640 100644 --- a/src/NexusReader.Infrastructure/Services/EpubReaderService.cs +++ b/src/NexusReader.Infrastructure/Services/EpubReaderService.cs @@ -18,7 +18,7 @@ public class EpubReaderService : IEpubReader private readonly ILogger _logger; private const int WordThreshold = 1000; - private static readonly Regex ImageTagRegex = new(@"[^>]*?\bsrc=[""'])(?[^""']*?)(?[""'][^>]*?>)", RegexOptions.IgnoreCase | RegexOptions.Compiled); + private static readonly Regex ImageTagRegex = new(@"(?]*?\bsrc=[""'])(?[^""']*?)(?[""'][^>]*?>)", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static readonly Regex BodyMatchRegex = new(@"]*>(.*?)", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled); private static readonly Regex ParagraphMatchRegex = new(@"<(p|h[1-6]|ul|ol|blockquote|pre)\b[^>]*>.*?|]*>|]*>", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled); private static readonly Regex StyleScriptRegex = new(@"<(style|script)\b[^>]*>.*?", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled); @@ -27,6 +27,9 @@ public class EpubReaderService : IEpubReader private static readonly Regex ImgTagSanitizerRegex = new(@"]*>", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static readonly Regex SrcAttributeRegex = new(@"\bsrc=[""'](?[^""']*)[""']", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static readonly Regex AltAttributeRegex = new(@"\balt=[""'](?[^""']*)[""']", RegexOptions.IgnoreCase | RegexOptions.Compiled); + private static readonly Regex SvgImageTagRegex = new(@"[^>]*?)>", RegexOptions.IgnoreCase | RegexOptions.Compiled); + private static readonly Regex HrefAttributeRegex = new(@"\b(xlink:)?href=[""'](?[^""']*)[""']", RegexOptions.IgnoreCase | RegexOptions.Compiled); + private static readonly Regex EmptyBlockRegex = new(@"^(]*>| |\s)*$", RegexOptions.IgnoreCase | RegexOptions.Compiled); public EpubReaderService( IDbContextFactory dbContextFactory, @@ -102,7 +105,7 @@ public class EpubReaderService : IEpubReader foreach (var p in paragraphs) { var sanitizedContent = SanitizeParagraph(p); - if (string.IsNullOrWhiteSpace(sanitizedContent)) continue; + if (string.IsNullOrWhiteSpace(sanitizedContent) || EmptyBlockRegex.IsMatch(sanitizedContent)) continue; blocks.Add(new TextSegmentBlock($"seg-{blockCounter++}", sanitizedContent)); @@ -236,7 +239,9 @@ public class EpubReaderService : IEpubReader { if (string.IsNullOrEmpty(html)) return html; - return ImageTagRegex.Replace(html, match => + var normalizedHtml = NormalizeSvgImageTags(html); + + return ImageTagRegex.Replace(normalizedHtml, match => { var rawSrc = match.Groups["src"].Value; @@ -258,6 +263,31 @@ public class EpubReaderService : IEpubReader }); } + private static string NormalizeSvgImageTags(string html) + { + if (string.IsNullOrEmpty(html)) return html; + + return SvgImageTagRegex.Replace(html, match => + { + var attrs = match.Groups["attrs"].Value; + + if (SrcAttributeRegex.IsMatch(attrs)) + { + return $""; + } + + var hrefMatch = HrefAttributeRegex.Match(attrs); + if (hrefMatch.Success) + { + var hrefVal = hrefMatch.Groups["href"].Value; + var cleanedAttrs = HrefAttributeRegex.Replace(attrs, ""); + return $""; + } + + return match.Value; + }); + } + private static string ResolveRelativePath(string basePath, string relativePath) { if (string.IsNullOrEmpty(relativePath)) return string.Empty; diff --git a/src/NexusReader.UI.Shared/Components/Organisms/MobileReaderToolbar.razor b/src/NexusReader.UI.Shared/Components/Organisms/MobileReaderToolbar.razor index fb79ca0..673f552 100644 --- a/src/NexusReader.UI.Shared/Components/Organisms/MobileReaderToolbar.razor +++ b/src/NexusReader.UI.Shared/Components/Organisms/MobileReaderToolbar.razor @@ -8,7 +8,7 @@ @inject IReaderStateService StateService @inject IThemeService ThemeService -
+