Skip to content

Commit

Permalink
Add SpineReaderOptions.IgnoreMissingManifestItems (#122)
Browse files Browse the repository at this point in the history
  • Loading branch information
vers-one authored Dec 25, 2024
1 parent 6162eee commit a6a3bd6
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 69 deletions.
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

0 comments on commit a6a3bd6

Please sign in to comment.