Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the configuration option to ignore the error for missing manifest item referenced by EPUB spine item #122

Merged
merged 1 commit into from
Dec 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 97 additions & 58 deletions Source/VersOne.Epub.Test/Unit/Readers/SpineReaderTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using VersOne.Epub.Internal;
using VersOne.Epub.Options;
using VersOne.Epub.Schema;
using VersOne.Epub.Test.Unit.Mocks;

Expand All @@ -11,8 +12,8 @@ public void GetReadingOrderForMinimalSpineTest()
{
EpubSchema epubSchema = CreateEpubSchema();
EpubContentRef epubContentRef = new();
List<EpubLocalTextContentFileRef> expectedReadingOrder = new();
List<EpubLocalTextContentFileRef> actualReadingOrder = SpineReader.GetReadingOrder(epubSchema, epubContentRef);
List<EpubLocalTextContentFileRef> expectedReadingOrder = [];
List<EpubLocalTextContentFileRef> actualReadingOrder = SpineReader.GetReadingOrder(epubSchema, epubContentRef, new SpineReaderOptions());
Assert.Equal(expectedReadingOrder, actualReadingOrder);
}

Expand All @@ -23,119 +24,157 @@ public void GetReadingOrderForTypicalSpineTest()
(
manifest: new EpubManifest
(
items: new List<EpubManifestItem>()
{
new EpubManifestItem
items:
[
new
(
id: "item-1",
href: "chapter1.html",
mediaType: "application/xhtml+xml"
),
new EpubManifestItem
new
(
id: "item-2",
href: "chapter2.html",
mediaType: "application/xhtml+xml"
)
}
]
),
spine: new EpubSpine
(
items: new List<EpubSpineItemRef>()
{
new EpubSpineItemRef
items:
[
new
(
idRef: "item-1"
),
new EpubSpineItemRef
new
(
idRef: "item-2"
)
}
]
)
);
EpubLocalTextContentFileRef expectedHtmlFileRef1 = CreateTestHtmlFileRef("chapter1.html");
EpubLocalTextContentFileRef expectedHtmlFileRef2 = CreateTestHtmlFileRef("chapter2.html");
List<EpubLocalTextContentFileRef> expectedHtmlLocal = new()
{
List<EpubLocalTextContentFileRef> expectedHtmlLocal =
[
expectedHtmlFileRef1,
expectedHtmlFileRef2
};
];
EpubContentRef epubContentRef = new
(
html: new EpubContentCollectionRef<EpubLocalTextContentFileRef, EpubRemoteTextContentFileRef>(expectedHtmlLocal.AsReadOnly())
);
List<EpubLocalTextContentFileRef> expectedReadingOrder = new()
{
List<EpubLocalTextContentFileRef> expectedReadingOrder =
[
expectedHtmlFileRef1,
expectedHtmlFileRef2
};
List<EpubLocalTextContentFileRef> actualReadingOrder = SpineReader.GetReadingOrder(epubSchema, epubContentRef);
];
List<EpubLocalTextContentFileRef> actualReadingOrder = SpineReader.GetReadingOrder(epubSchema, epubContentRef, new SpineReaderOptions());
Assert.Equal(expectedReadingOrder, actualReadingOrder);
}

[Fact(DisplayName = "GetReadingOrder should throw EpubPackageException if there is no manifest item with ID matching to the ID ref of a spine item")]
public void GetReadingOrderWithMissingManifestItemTest()
[Fact(DisplayName = "GetReadingOrder should throw EpubPackageException if there is no manifest item with ID matching to the ID ref of a spine item and SpineReaderOptions.IgnoreMissingManifestItems is false")]
public void GetReadingOrderWithMissingManifestItemWithoutIgnoringErrorsTest()
{
EpubSchema epubSchema = CreateEpubSchema
(
manifest: new EpubManifest
(
items:
[
new
(
id: "item-2",
href: "chapter2.html",
mediaType: "application/xhtml+xml"
)
]
),
spine: new EpubSpine
(
items:
[
new
(
idRef: "item-1"
)
]
)
);
EpubContentRef epubContentRef = new();
Assert.Throws<EpubPackageException>(() => SpineReader.GetReadingOrder(epubSchema, epubContentRef, new SpineReaderOptions()));
}

[Fact(DisplayName = "GetReadingOrder should skip non-existent manifest items if SpineReaderOptions.IgnoreMissingManifestItems is true")]
public void GetReadingOrderWithMissingManifestItemWithIgnoringErrorsTest()
{
EpubSchema epubSchema = CreateEpubSchema
(
manifest: new EpubManifest
(
items: new List<EpubManifestItem>()
{
new EpubManifestItem
items:
[
new
(
id: "item-2",
href: "chapter2.html",
mediaType: "application/xhtml+xml"
)
}
]
),
spine: new EpubSpine
(
items: new List<EpubSpineItemRef>()
{
new EpubSpineItemRef
items:
[
new
(
idRef: "item-1"
)
}
]
)
);
EpubContentRef epubContentRef = new();
Assert.Throws<EpubPackageException>(() => SpineReader.GetReadingOrder(epubSchema, epubContentRef));
SpineReaderOptions spineReaderOptions = new()
{
IgnoreMissingManifestItems = true
};
List<EpubLocalTextContentFileRef> expectedReadingOrder = [];
List<EpubLocalTextContentFileRef> actualReadingOrder = SpineReader.GetReadingOrder(epubSchema, epubContentRef, spineReaderOptions);
Assert.Equal(expectedReadingOrder, actualReadingOrder);
}

[Fact(DisplayName = "GetReadingOrder should throw EpubPackageException if there is no HTML content file referenced by a manifest item")]
public void GetReadingOrderWithMissingHtmlContentFileTest()
{
EpubSchema epubSchema = CreateEpubSchema
(
manifest: new EpubManifest
manifest: new
(
items: new List<EpubManifestItem>()
{
new EpubManifestItem
items:
[
new
(
id: "item-1",
href: "chapter1.html",
mediaType: "application/xhtml+xml"
)
}
]
),
spine: new EpubSpine
spine: new
(
items: new List<EpubSpineItemRef>()
{
new EpubSpineItemRef
items:
[
new
(
idRef: "item-1"
)
}
]
)
);
EpubContentRef epubContentRef = new();
Assert.Throws<EpubPackageException>(() => SpineReader.GetReadingOrder(epubSchema, epubContentRef));
Assert.Throws<EpubPackageException>(() => SpineReader.GetReadingOrder(epubSchema, epubContentRef, new SpineReaderOptions()));
}

[Fact(DisplayName = "GetReadingOrder should throw EpubPackageException if the HTML content file referenced by a spine item is a remote resource")]
Expand All @@ -144,54 +183,54 @@ public void GetReadingOrderWithRemoteHtmlContentFileTest()
string remoteFileHref = "https://example.com/books/123/chapter1.html";
EpubSchema epubSchema = CreateEpubSchema
(
manifest: new EpubManifest
manifest: new
(
items: new List<EpubManifestItem>()
{
new EpubManifestItem
items:
[
new
(
id: "item-1",
href: remoteFileHref,
mediaType: "application/xhtml+xml"
)
}
]
),
spine: new EpubSpine
spine: new
(
items: new List<EpubSpineItemRef>()
{
new EpubSpineItemRef
items:
[
new
(
idRef: "item-1"
)
}
]
)
);
List<EpubRemoteTextContentFileRef> htmlRemote = new()
{
new EpubRemoteTextContentFileRef
List<EpubRemoteTextContentFileRef> htmlRemote =
[
new
(
metadata: new EpubContentFileRefMetadata
metadata: new
(
key: remoteFileHref,
contentType: EpubContentType.XHTML_1_1,
contentMimeType: "application/xhtml+xml"
),
epubContentLoader: new TestEpubContentLoader()
)
};
];
EpubContentRef epubContentRef = new
(
html: new EpubContentCollectionRef<EpubLocalTextContentFileRef, EpubRemoteTextContentFileRef>(null, htmlRemote.AsReadOnly())
);
Assert.Throws<EpubPackageException>(() => SpineReader.GetReadingOrder(epubSchema, epubContentRef));
Assert.Throws<EpubPackageException>(() => SpineReader.GetReadingOrder(epubSchema, epubContentRef, new SpineReaderOptions()));
}

private static EpubSchema CreateEpubSchema(EpubManifest? manifest = null, EpubSpine? spine = null)
{
return new
(
package: new EpubPackage
package: new
(
uniqueIdentifier: null,
epubVersion: EpubVersion.EPUB_3,
Expand Down
14 changes: 12 additions & 2 deletions Source/VersOne.Epub/Entities/EpubBookRef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading.Tasks;
using VersOne.Epub.Environment;
using VersOne.Epub.Internal;
using VersOne.Epub.Options;

namespace VersOne.Epub
{
Expand All @@ -24,12 +25,15 @@ public class EpubBookRef : IDisposable
/// <param name="description">The book's description or <c>null</c> if the description is not present in the book.</param>
/// <param name="schema">The parsed EPUB schema of the book.</param>
/// <param name="content">The collection of references to the book's content files within the EPUB archive.</param>
/// <param name="epubReaderOptions">Various options to configure the behavior of the EPUB reader.</param>
/// <exception cref="ArgumentNullException">The <paramref name="epubFile" /> parameter is <c>null</c>.</exception>
/// <exception cref="ArgumentNullException">The <paramref name="title" /> parameter is <c>null</c>.</exception>
/// <exception cref="ArgumentNullException">The <paramref name="author" /> parameter is <c>null</c>.</exception>
/// <exception cref="ArgumentNullException">The <paramref name="schema" /> parameter is <c>null</c>.</exception>
/// <exception cref="ArgumentNullException">The <paramref name="content" /> parameter is <c>null</c>.</exception>
public EpubBookRef(IZipFile epubFile, string? filePath, string title, string author, List<string>? authorList, string? description, EpubSchema schema, EpubContentRef content)
public EpubBookRef(
IZipFile epubFile, string? filePath, string title, string author, List<string>? authorList, string? description, EpubSchema schema,
EpubContentRef content, EpubReaderOptions? epubReaderOptions = null)
{
EpubFile = epubFile ?? throw new ArgumentNullException(nameof(epubFile));
FilePath = filePath;
Expand All @@ -39,6 +43,7 @@ public EpubBookRef(IZipFile epubFile, string? filePath, string title, string aut
Description = description;
Schema = schema ?? throw new ArgumentNullException(nameof(schema));
Content = content ?? throw new ArgumentNullException(nameof(content));
EpubReaderOptions = epubReaderOptions ?? new EpubReaderOptions();
isDisposed = false;
}

Expand Down Expand Up @@ -90,6 +95,11 @@ public EpubBookRef(IZipFile epubFile, string? filePath, string title, string aut
/// </summary>
public IZipFile EpubFile { get; }

/// <summary>
/// Gets the options that configure the behavior of the EPUB reader.
/// </summary>
public EpubReaderOptions EpubReaderOptions { get; }

/// <summary>
/// Loads the book's cover image from the EPUB file.
/// </summary>
Expand Down Expand Up @@ -132,7 +142,7 @@ public List<EpubLocalTextContentFileRef> GetReadingOrder()
/// </returns>
public async Task<List<EpubLocalTextContentFileRef>> GetReadingOrderAsync()
{
return await Task.Run(() => SpineReader.GetReadingOrder(Schema, Content)).ConfigureAwait(false);
return await Task.Run(() => SpineReader.GetReadingOrder(Schema, Content, EpubReaderOptions.SpineReaderOptions)).ConfigureAwait(false);
}

/// <summary>
Expand Down
18 changes: 12 additions & 6 deletions Source/VersOne.Epub/Options/EpubReaderOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,15 @@ public class EpubReaderOptions
/// <param name="preset">An optional preset to initialize the <see cref="EpubReaderOptions" /> class with a predefined set of options.</param>
public EpubReaderOptions(EpubReaderOptionsPreset? preset = null)
{
BookCoverReaderOptions = new BookCoverReaderOptions(preset);
PackageReaderOptions = new PackageReaderOptions(preset);
ContentReaderOptions = new ContentReaderOptions(preset);
ContentDownloaderOptions = new ContentDownloaderOptions(preset);
BookCoverReaderOptions = new BookCoverReaderOptions(preset);
SpineReaderOptions = new SpineReaderOptions(preset);
Epub2NcxReaderOptions = new Epub2NcxReaderOptions(preset);
XmlReaderOptions = new XmlReaderOptions(preset);
}

/// <summary>
/// Gets or sets EPUB content reader options which is used for loading the EPUB book cover image.
/// </summary>
public BookCoverReaderOptions BookCoverReaderOptions { get; set; }

/// <summary>
/// Gets or sets EPUB OPF package reader options.
/// </summary>
Expand All @@ -39,6 +35,16 @@ public EpubReaderOptions(EpubReaderOptionsPreset? preset = null)
/// </summary>
public ContentDownloaderOptions ContentDownloaderOptions { get; set; }

/// <summary>
/// Gets or sets EPUB content reader options which is used for loading the EPUB book cover image.
/// </summary>
public BookCoverReaderOptions BookCoverReaderOptions { get; set; }

/// <summary>
/// Gets or sets EPUB spine reader options which is used for parsing the default reading order of the EPUB book.
/// </summary>
public SpineReaderOptions SpineReaderOptions { get; set; }

/// <summary>
/// Gets or sets EPUB 2 NCX navigation document reader options.
/// </summary>
Expand Down
Loading