Skip to content

Commit d805e4e

Browse files
authored
Add the ReadiumAdapterMinizip library (#548)
1 parent 8d9844f commit d805e4e

File tree

18 files changed

+688
-108
lines changed

18 files changed

+688
-108
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ All notable changes to this project will be documented in this file. Take a look
1111
#### Shared
1212

1313
* Support for streaming ZIP packages over HTTP. This lets you open a remote EPUB, audiobook, or any other ZIP-based publication without needing to download it first.
14+
* A new `ReadiumAdapterMinizip` library ships the old `ArchiveOpener` using Minizip. Compared to the newer default `ZIPArchiveOpener`, it has the following differences:
15+
* It does not support HTTP streaming of ZIP packages.
16+
* It offers better performance for LCP-protected publications containing large resources that are `deflated` instead of `stored` in the archive, which is not recommended.
1417

1518
### Deprecated
1619

Package.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ let package = Package(
2121
// Adapters to third-party dependencies.
2222
.library(name: "ReadiumAdapterGCDWebServer", targets: ["ReadiumAdapterGCDWebServer"]),
2323
.library(name: "ReadiumAdapterLCPSQLite", targets: ["ReadiumAdapterLCPSQLite"]),
24+
.library(name: "ReadiumAdapterMinizip", targets: ["ReadiumAdapterMinizip"]),
2425
],
2526
dependencies: [
2627
.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", from: "1.8.0"),
28+
.package(url: "https://github.com/marmelroy/Zip.git", from: "2.1.0"),
2729
.package(url: "https://github.com/ra1028/DifferenceKit.git", from: "1.3.0"),
2830
.package(url: "https://github.com/readium/Fuzi.git", from: "4.0.0"),
2931
.package(url: "https://github.com/readium/GCDWebServer.git", from: "4.0.0"),
@@ -157,6 +159,23 @@ let package = Package(
157159
path: "Sources/Adapters/LCPSQLite"
158160
),
159161

162+
.target(
163+
name: "ReadiumAdapterMinizip",
164+
dependencies: [
165+
"ReadiumShared",
166+
"Zip",
167+
],
168+
path: "Sources/Adapters/Minizip"
169+
),
170+
.testTarget(
171+
name: "ReadiumAdapterMinizipTests",
172+
dependencies: ["ReadiumAdapterMinizip"],
173+
path: "Tests/Adapters/MinizipTests",
174+
resources: [
175+
.copy("Fixtures"),
176+
]
177+
),
178+
160179
.target(
161180
name: "ReadiumInternal",
162181
path: "Sources/Internal"

README.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,18 @@ Note that Carthage will build all Readium modules and their dependencies, but yo
5454

5555
Refer to the following table to know which dependencies are required for each Readium library.
5656

57-
| | `ReadiumShared` | `ReadiumStreamer` | `ReadiumNavigator` | `ReadiumOPDS` | `ReadiumLCP` | `ReadiumAdapterGCDWebServer` | `ReadiumAdapterLCPSQLite` |
58-
|------------------------|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:|------------------------------|---------------------------|
59-
| **`ReadiumShared`** | | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
60-
| **`ReadiumInternal`** | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | |
61-
| `CryptoSwift` | | :heavy_check_mark: | | | :heavy_check_mark: | | |
62-
| `DifferenceKit` | | | :heavy_check_mark: | | | | |
63-
| `ReadiumFuzi` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | |
64-
| `ReadiumGCDWebServer` | | | | | | :heavy_check_mark: | |
65-
| `ReadiumZIPFoundation` | :heavy_check_mark: | | | | :heavy_check_mark: | | |
66-
| `SQLite.swift` | | | | | | | :heavy_check_mark: |
67-
| `SwiftSoup` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | |
57+
| | `ReadiumShared` | `ReadiumStreamer` | `ReadiumNavigator` | `ReadiumOPDS` | `ReadiumLCP` | `ReadiumAdapterGCDWebServer` | `ReadiumAdapterLCPSQLite` | `ReadiumAdapterMinizip` |
58+
|------------------------|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:|------------------------------|---------------------------|-------------------------|
59+
| **`ReadiumShared`** | | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
60+
| **`ReadiumInternal`** | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | | |
61+
| `CryptoSwift` | | :heavy_check_mark: | | | :heavy_check_mark: | | | |
62+
| `DifferenceKit` | | | :heavy_check_mark: | | | | | |
63+
| `ReadiumFuzi` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | | |
64+
| `ReadiumGCDWebServer` | | | | | | :heavy_check_mark: | | |
65+
| `ReadiumZIPFoundation` | :heavy_check_mark: | | | | :heavy_check_mark: | | | |
66+
| `Minizip` | | | | | | | | :heavy_check_mark: |
67+
| `SQLite.swift` | | | | | | | :heavy_check_mark: | |
68+
| `SwiftSoup` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | | |
6869

6970
### CocoaPods
7071

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//
2+
// Copyright 2025 Readium Foundation. All rights reserved.
3+
// Use of this source code is governed by the BSD-style license
4+
// available in the top-level LICENSE file of the project.
5+
//
6+
7+
import Foundation
8+
import ReadiumShared
9+
10+
/// An ``ArchiveOpener`` able to open ZIP archives using Minizip.
11+
///
12+
/// Compared to the default ZIPFoundation implementation shipped with
13+
/// ReadiumShared, the ``MinizipArchiveOpener``:
14+
///
15+
/// - Does not support HTTP streaming of ZIP archives.
16+
/// - Has better performance when reading an LCP-protected package containing
17+
/// large deflated ZIP entries (instead of stored).
18+
public final class MinizipArchiveOpener: ArchiveOpener {
19+
public init() {}
20+
21+
public func open(resource: any Resource, format: Format) async -> Result<ContainerAsset, ArchiveOpenError> {
22+
guard
23+
format.conformsTo(.zip),
24+
let file = resource.sourceURL?.fileURL
25+
else {
26+
return .failure(.formatNotSupported(format))
27+
}
28+
29+
return await MinizipContainer.make(file: file)
30+
.mapError {
31+
switch $0 {
32+
case .notAZIP:
33+
return .formatNotSupported(format)
34+
case let .reading(error):
35+
return .reading(error)
36+
}
37+
}
38+
.map { ContainerAsset(container: $0, format: format) }
39+
}
40+
41+
public func sniffOpen(resource: any Resource) async -> Result<ContainerAsset, ArchiveSniffOpenError> {
42+
guard let file = resource.sourceURL?.fileURL else {
43+
return .failure(.formatNotRecognized)
44+
}
45+
46+
return await MinizipContainer.make(file: file)
47+
.mapError {
48+
switch $0 {
49+
case .notAZIP:
50+
return .formatNotRecognized
51+
case let .reading(error):
52+
return .reading(error)
53+
}
54+
}
55+
.map {
56+
ContainerAsset(
57+
container: $0,
58+
format: Format(
59+
specifications: .zip,
60+
mediaType: .zip,
61+
fileExtension: "zip"
62+
)
63+
)
64+
}
65+
}
66+
}

0 commit comments

Comments
 (0)