├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── Integrations ├── Carthage │ ├── Cartfile │ ├── Cartfile+lcp │ ├── project+lcp.yml │ └── project.yml ├── CocoaPods │ ├── Podfile │ ├── Podfile+lcp │ ├── project+lcp.yml │ └── project.yml ├── SPM │ ├── project+lcp.yml │ └── project.yml └── Submodules │ ├── project+lcp.yml │ └── project.yml ├── LICENSE ├── Makefile ├── README.md └── Sources ├── About └── About.storyboard ├── App ├── AboutTableViewController.swift ├── AppModule.swift └── Base.lproj │ └── App.storyboard ├── AppDelegate.swift ├── Common ├── Paths.swift ├── Publication.swift └── Toolkit │ ├── BarButtonItem.swift │ ├── Extensions │ ├── AnyPublisher.swift │ ├── Future.swift │ ├── HTTPClient.swift │ ├── Locator.swift │ ├── String.swift │ ├── UIImage.swift │ └── UIViewController.swift │ └── ScreenOrientation.swift ├── Data ├── Book.swift ├── Bookmark.swift └── Database.swift ├── Info.plist ├── Library ├── Base.lproj │ ├── Library.storyboard │ ├── PublicationCollectionViewCell.xib │ └── PublicationMenuViewController.xib ├── DRM │ ├── DRMLibraryService.swift │ └── LCPLibraryService.swift ├── DetailsTableViewController.swift ├── LibraryError.swift ├── LibraryFactory.swift ├── LibraryModule.swift ├── LibraryService.swift ├── LibraryViewController.swift ├── PublicationCollectionViewCell.swift ├── PublicationMenuViewController.swift └── en.lproj │ └── PublicationMenuViewController.strings ├── OPDS ├── Base.lproj │ └── OPDS.storyboard ├── OPDSCatalogSelectorViewController.swift ├── OPDSFacetViewController.swift ├── OPDSFactory.swift ├── OPDSGroupCollectionViewCell.swift ├── OPDSGroupTableViewCell.swift ├── OPDSModule.swift ├── OPDSNavigationTableViewCell.swift ├── OPDSPlaceholderView.swift ├── OPDSPublicationInfoViewController.swift ├── OPDSPublicationTableViewCell.swift └── OPDSRootTableViewController.swift ├── Reader ├── CBZ │ ├── CBZModule.swift │ └── CBZViewController.swift ├── Common │ ├── Bookmark │ │ └── BookmarkCell.swift │ ├── DRM │ │ ├── Base.lproj │ │ │ └── DRM.storyboard │ │ ├── DRMManagementTableViewController.swift │ │ ├── DRMViewModel.swift │ │ └── LCPViewModel.swift │ ├── Outline │ │ ├── Base.lproj │ │ │ └── Outline.storyboard │ │ └── OutlineTableViewController.swift │ └── ReaderViewController.swift ├── EPUB │ ├── AssociatedColors.swift │ ├── EPUBModule.swift │ ├── EPUBViewController.swift │ └── User Settings │ │ ├── AdvancedSettingsViewController.swift │ │ ├── Base.lproj │ │ └── UserSettings.storyboard │ │ ├── FontSelectionViewController.swift │ │ ├── UserSettingsNavigationController.swift │ │ ├── UserSettingsTableViewController.swift │ │ └── en.lproj │ │ └── UserSettings.strings ├── PDF │ ├── PDFModule.swift │ └── PDFViewController.swift ├── ReaderError.swift ├── ReaderFactory.swift ├── ReaderFormatModule.swift ├── ReaderModule.swift └── Toast.swift ├── Resources ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── AppIcon29x29.png │ │ ├── AppIcon29x29@2x-1.png │ │ ├── AppIcon29x29@2x.png │ │ ├── AppIcon29x29@3x.png │ │ ├── AppIcon40x40.png │ │ ├── AppIcon40x40@2x-1.png │ │ ├── AppIcon40x40@2x.png │ │ ├── AppIcon40x40@3x.png │ │ ├── AppIcon60x60@2x.png │ │ ├── AppIcon60x60@3x.png │ │ ├── AppIcon76x76.png │ │ ├── AppIcon76x76@2x.png │ │ ├── Contents.json │ │ ├── icon_1024x1024.png │ │ └── readiumlogo_2048-83.5@2x.png │ ├── Contents.json │ ├── about.imageset │ │ ├── Contents.json │ │ ├── glyphicons-196-info-sign.png │ │ ├── glyphicons-196-info-sign@2x.png │ │ └── glyphicons-196-info-sign@3x.png │ ├── alignLeft.imageset │ │ ├── Contents.json │ │ ├── glyphicons-111-align-left.png │ │ ├── glyphicons-111-align-left@2x.png │ │ └── glyphicons-111-align-left@3x.png │ ├── alignRight.imageset │ │ ├── Contents.json │ │ ├── glyphicons-113-align-right.png │ │ ├── glyphicons-113-align-right@2x.png │ │ └── glyphicons-113-align-right@3x.png │ ├── bookIcon.imageset │ │ ├── Contents.json │ │ ├── ic_book_48pt.png │ │ ├── ic_book_48pt_2x.png │ │ └── ic_book_48pt_3x.png │ ├── bookmark.imageset │ │ ├── Contents.json │ │ ├── bookmark-white-20.png │ │ ├── bookmark-white-20@2x.png │ │ └── bookmark-white-20@3x.png │ ├── bookshelf.imageset │ │ ├── Contents.json │ │ ├── bookshelf_1.png │ │ ├── bookshelf_2.png │ │ └── bookshelf_3.png │ ├── catalogs.imageset │ │ ├── Contents.json │ │ ├── ic_view_list_1x.png │ │ ├── ic_view_list_2x.png │ │ └── ic_view_list_3x.png │ ├── center.imageset │ │ ├── Contents.json │ │ ├── glyphicons-112-align-center.png │ │ ├── glyphicons-112-align-center@2x.png │ │ └── glyphicons-112-align-center@3x.png │ ├── drm.imageset │ │ ├── Contents.json │ │ ├── glyphicons-241-rotation-lock.png │ │ ├── glyphicons-241-rotation-lock@2x.png │ │ └── glyphicons-241-rotation-lock@3x.png │ ├── edrlab-logo.imageset │ │ ├── Contents.json │ │ └── edrlab-logo.png │ ├── gradient.imageset │ │ ├── Contents.json │ │ └── gradient.png │ ├── higherBrightness.imageset │ │ ├── Contents.json │ │ ├── glyphicons-190-brightness-increase.png │ │ ├── glyphicons-190-brightness-increase@2x.png │ │ └── glyphicons-190-brightness-increase@3x.png │ ├── justify.imageset │ │ ├── Contents.json │ │ ├── glyphicons-114-justify.png │ │ ├── glyphicons-114-justify@2x.png │ │ └── glyphicons-114-justify@3x.png │ ├── lowerBrightness.imageset │ │ ├── Contents.json │ │ ├── glyphicons-189-brightness-reduce.png │ │ ├── glyphicons-189-brightness-reduce@2x.png │ │ └── glyphicons-189-brightness-reduce@3x.png │ ├── menuIcon.imageset │ │ ├── Contents.json │ │ ├── glyphicons-517-menu-hamburger.png │ │ ├── glyphicons-517-menu-hamburger@2x.png │ │ └── glyphicons-517-menu-hamburger@3x.png │ ├── minus.imageset │ │ ├── Contents.json │ │ ├── glyphicons-434-minus.png │ │ ├── glyphicons-434-minus@2x.png │ │ └── glyphicons-434-minus@3x.png │ ├── plus.imageset │ │ ├── Contents.json │ │ ├── glyphicons-433-plus.png │ │ ├── glyphicons-433-plus@2x.png │ │ └── glyphicons-433-plus@3x.png │ ├── r2-logo.imageset │ │ ├── Contents.json │ │ └── r2-logo.png │ ├── readium-icon.imageset │ │ ├── Contents.json │ │ └── readium-icon.png │ ├── rf.imageset │ │ ├── Contents.json │ │ ├── rf.png │ │ ├── rf@2x.png │ │ └── rf@3x.png │ └── settingsIcon.imageset │ │ ├── Contents.json │ │ ├── glyphicons-101-font.png │ │ ├── glyphicons-101-font@2x.png │ │ └── glyphicons-101-font@3x.png ├── Base.lproj │ └── LaunchScreen.storyboard ├── Samples │ ├── 1.cbz │ ├── 1.epub │ ├── 2.epub │ ├── 3.epub │ ├── 4.epub │ ├── 5.epub │ └── 6.epub └── en.lproj │ └── Localizable.strings └── r2-testapp-swift.entitlements /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj merge=union 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Bug Report 11 | 12 | 15 | 16 | ### What happened? 17 | 18 | 23 | 24 | ### Expected behavior 25 | 26 | 27 | 28 | ### How to reproduce? 29 | 30 | 40 | 41 | ### Environment 42 | 43 | 44 | 45 | #### Readium versions 46 | 47 | 48 | 49 | * `r2-shared-swift`: 50 | * `r2-streamer-swift`: 51 | * `r2-navigator-swift`: 52 | * `r2-opds-swift`: 53 | * `r2-lcp-swift`: 54 | 55 | #### Development environment 56 | 57 | 66 | 67 | #### Testing device 68 | 69 | * iOS version: 70 | * Model (e.g. iPhone 11 Pro Max): 71 | * Is it an emulator? Yes or No 72 | 73 | ### Additional context 74 | 75 | * Are you willing to fix the problem and contribute a pull request? Yes or No 76 | 77 | 84 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ develop ] 7 | pull_request: 8 | branches: [ develop ] 9 | 10 | env: 11 | platform: ${{ 'iOS Simulator' }} 12 | device: ${{ 'iPhone 13' }} 13 | 14 | jobs: 15 | spm: 16 | name: SPM integration 17 | runs-on: macos-latest 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | - name: Install dependencies 22 | run: brew install xcodegen 23 | - name: Generate project 24 | run: make spm 25 | - name: Build 26 | run: | 27 | xcodebuild build -scheme R2TestApp -destination "platform=$platform,name=$device" 28 | 29 | spm_lcp: 30 | name: SPM integration (LCP) 31 | runs-on: macos-latest 32 | environment: LCP 33 | steps: 34 | - name: Checkout 35 | uses: actions/checkout@v2 36 | - name: Install dependencies 37 | run: brew install xcodegen 38 | - name: Generate project 39 | run: make spm lcp=${{ secrets.LCP_URL_CARTHAGE }} 40 | - name: Build 41 | run: | 42 | xcodebuild build -scheme R2TestApp -destination "platform=$platform,name=$device" 43 | 44 | carthage: 45 | name: Carthage integration 46 | runs-on: macos-latest 47 | steps: 48 | - name: Checkout 49 | uses: actions/checkout@v2 50 | - name: Install dependencies 51 | run: brew install xcodegen 52 | - name: Generate project 53 | run: make carthage 54 | - name: Build 55 | run: | 56 | xcodebuild build -scheme R2TestApp -destination "platform=$platform,name=$device" 57 | 58 | carthage_lcp: 59 | name: Carthage integration (LCP) 60 | runs-on: macos-latest 61 | environment: LCP 62 | steps: 63 | - name: Checkout 64 | uses: actions/checkout@v2 65 | - name: Install dependencies 66 | run: brew install xcodegen 67 | - name: Generate project 68 | run: make carthage lcp=${{ secrets.LCP_URL_CARTHAGE }} 69 | - name: Build 70 | run: | 71 | xcodebuild build -scheme R2TestApp -destination "platform=$platform,name=$device" 72 | 73 | cocoapods: 74 | name: CocoaPods integration 75 | runs-on: macos-latest 76 | steps: 77 | - name: Checkout 78 | uses: actions/checkout@v2 79 | - name: Install dependencies 80 | run: brew install xcodegen 81 | - name: Generate project 82 | run: make cocoapods 83 | - name: Build 84 | run: | 85 | xcodebuild build -workspace R2TestApp.xcworkspace -scheme R2TestApp -destination "platform=$platform,name=$device" 86 | 87 | cocoapods_lcp: 88 | name: CocoaPods integration (LCP) 89 | runs-on: macos-latest 90 | environment: LCP 91 | steps: 92 | - name: Checkout 93 | uses: actions/checkout@v2 94 | - name: Install dependencies 95 | run: brew install xcodegen 96 | - name: Generate project 97 | run: make cocoapods lcp=${{ secrets.LCP_URL_COCOAPODS }} 98 | - name: Build 99 | run: | 100 | xcodebuild build -workspace R2TestApp.xcworkspace -scheme R2TestApp -destination "platform=$platform,name=$device" 101 | 102 | dev: 103 | name: Submodules integration 104 | runs-on: macos-latest 105 | steps: 106 | - name: Checkout 107 | uses: actions/checkout@v2 108 | - name: Install dependencies 109 | run: brew install xcodegen 110 | - name: Generate project 111 | run: make dev 112 | - name: Build 113 | run: | 114 | xcodebuild build -scheme R2TestApp -destination "platform=$platform,name=$device" 115 | 116 | dev_lcp: 117 | name: Submodules integration (LCP) 118 | runs-on: macos-latest 119 | environment: LCP 120 | steps: 121 | - name: Checkout 122 | uses: actions/checkout@v2 123 | - name: Install dependencies 124 | run: brew install xcodegen 125 | - name: Generate project 126 | run: make dev lcp=${{ secrets.LCP_URL_CARTHAGE }} 127 | - name: Build 128 | run: | 129 | xcodebuild build -scheme R2TestApp -destination "platform=$platform,name=$device" 130 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | # Xcode projects are generated with https://github.com/yonaskolb/XcodeGen 21 | *.xcodeproj 22 | *.xcworkspace 23 | 24 | ## Other 25 | *.moved-aside 26 | *.xccheckout 27 | *.xcscmblueprint 28 | 29 | ## Obj-C/Swift specific 30 | *.hmap 31 | *.ipa 32 | *.dSYM.zip 33 | *.dSYM 34 | 35 | ## Playgrounds 36 | timeline.xctimeline 37 | playground.xcworkspace 38 | 39 | # Swift Package Manager 40 | # 41 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 42 | # Packages/ 43 | # Package.pins 44 | .build/ 45 | 46 | /Carthage/Build 47 | /Carthage 48 | /Cartfile 49 | /Podfile 50 | /Pods 51 | /project.yml 52 | /Cartfile.resolved 53 | /Podfile.lock 54 | 55 | # IntelliJ's AppCode 56 | .idea 57 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Packages/r2-shared-swift"] 2 | path = Integrations/Submodules/r2-shared-swift 3 | url = https://github.com/readium/r2-shared-swift.git 4 | [submodule "Packages/r2-streamer-swift"] 5 | path = Integrations/Submodules/r2-streamer-swift 6 | url = https://github.com/readium/r2-streamer-swift.git 7 | [submodule "Packages/r2-navigator-swift"] 8 | path = Integrations/Submodules/r2-navigator-swift 9 | url = https://github.com/readium/r2-navigator-swift.git 10 | [submodule "Packages/r2-lcp-swift"] 11 | path = Integrations/Submodules/r2-lcp-swift 12 | url = https://github.com/readium/r2-lcp-swift.git 13 | [submodule "Packages/r2-opds-swift"] 14 | path = Integrations/Submodules/r2-opds-swift 15 | url = https://github.com/readium/r2-opds-swift.git 16 | -------------------------------------------------------------------------------- /Integrations/Carthage/Cartfile: -------------------------------------------------------------------------------- 1 | # Readium 2 dependencies 2 | github "readium/r2-shared-swift" ~> 2.2.0 3 | github "readium/r2-streamer-swift" ~> 2.2.0 4 | github "readium/r2-navigator-swift" ~> 2.2.0 5 | github "readium/r2-opds-swift" ~> 2.2.0 6 | -------------------------------------------------------------------------------- /Integrations/Carthage/Cartfile+lcp: -------------------------------------------------------------------------------- 1 | # Readium 2 dependencies 2 | github "readium/r2-shared-swift" ~> 2.2.0 3 | github "readium/r2-streamer-swift" ~> 2.2.0 4 | github "readium/r2-navigator-swift" ~> 2.2.0 5 | github "readium/r2-opds-swift" ~> 2.2.0 6 | github "readium/r2-lcp-swift" ~> 2.2.0 7 | -------------------------------------------------------------------------------- /Integrations/Carthage/project+lcp.yml: -------------------------------------------------------------------------------- 1 | name: R2TestApp 2 | options: 3 | bundleIdPrefix: org.readium 4 | packages: 5 | GRDB: 6 | url: https://github.com/groue/GRDB.swift.git 7 | from: 5.8.0 8 | Kingfisher: 9 | url: https://github.com/onevcat/Kingfisher.git 10 | from: 5.15.8 11 | MBProgressHUD: 12 | url: https://github.com/jdg/MBProgressHUD.git 13 | from: 1.2.0 14 | targets: 15 | R2TestApp: 16 | type: application 17 | platform: iOS 18 | deploymentTarget: "13.6" 19 | sources: 20 | - path: Sources 21 | excludes: 22 | - Resources/Samples 23 | - path: Sources/Resources/Samples 24 | type: folder 25 | dependencies: 26 | - carthage: R2LCPClient 27 | - framework: Carthage/Build/CryptoSwift.xcframework 28 | - framework: Carthage/Build/Fuzi.xcframework 29 | - framework: Carthage/Build/GCDWebServer.xcframework 30 | - framework: Carthage/Build/Minizip.xcframework 31 | - framework: Carthage/Build/R2Navigator.xcframework 32 | - framework: Carthage/Build/R2Shared.xcframework 33 | - framework: Carthage/Build/R2Streamer.xcframework 34 | - framework: Carthage/Build/ReadiumLCP.xcframework 35 | - framework: Carthage/Build/ReadiumOPDS.xcframework 36 | - framework: Carthage/Build/SQLite.xcframework 37 | - framework: Carthage/Build/SwiftSoup.xcframework 38 | - framework: Carthage/Build/ZIPFoundation.xcframework 39 | - package: GRDB 40 | - package: Kingfisher 41 | - package: MBProgressHUD 42 | settings: 43 | LIBRARY_SEARCH_PATHS: $(PROJECT_DIR)/Carthage 44 | OTHER_SWIFT_FLAGS: -DLCP -------------------------------------------------------------------------------- /Integrations/Carthage/project.yml: -------------------------------------------------------------------------------- 1 | name: R2TestApp 2 | options: 3 | bundleIdPrefix: org.readium 4 | packages: 5 | GRDB: 6 | url: https://github.com/groue/GRDB.swift.git 7 | from: 5.8.0 8 | Kingfisher: 9 | url: https://github.com/onevcat/Kingfisher.git 10 | from: 5.15.8 11 | MBProgressHUD: 12 | url: https://github.com/jdg/MBProgressHUD.git 13 | from: 1.2.0 14 | targets: 15 | R2TestApp: 16 | type: application 17 | platform: iOS 18 | deploymentTarget: "13.6" 19 | sources: 20 | - path: Sources 21 | excludes: 22 | - Resources/Samples 23 | - path: Sources/Resources/Samples 24 | type: folder 25 | dependencies: 26 | - framework: Carthage/Build/CryptoSwift.xcframework 27 | - framework: Carthage/Build/Fuzi.xcframework 28 | - framework: Carthage/Build/GCDWebServer.xcframework 29 | - framework: Carthage/Build/Minizip.xcframework 30 | - framework: Carthage/Build/R2Navigator.xcframework 31 | - framework: Carthage/Build/R2Shared.xcframework 32 | - framework: Carthage/Build/R2Streamer.xcframework 33 | - framework: Carthage/Build/ReadiumOPDS.xcframework 34 | - framework: Carthage/Build/SwiftSoup.xcframework 35 | - package: GRDB 36 | - package: Kingfisher 37 | - package: MBProgressHUD 38 | settings: 39 | LIBRARY_SEARCH_PATHS: $(PROJECT_DIR)/Carthage -------------------------------------------------------------------------------- /Integrations/CocoaPods/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '10.3.1' 2 | 3 | target 'R2TestApp' do 4 | # Comment the next line if you don't want to use dynamic frameworks 5 | use_frameworks! 6 | 7 | pod 'R2Shared', podspec: 'https://raw.githubusercontent.com/readium/r2-shared-swift/2.2.0/R2Shared.podspec' 8 | pod 'R2Streamer', podspec: 'https://raw.githubusercontent.com/readium/r2-streamer-swift/2.2.0/R2Streamer.podspec' 9 | pod 'R2Navigator', podspec: 'https://raw.githubusercontent.com/readium/r2-navigator-swift/2.2.0/R2Navigator.podspec' 10 | pod 'ReadiumOPDS', podspec: 'https://raw.githubusercontent.com/readium/r2-opds-swift/2.2.0/ReadiumOPDS.podspec' 11 | pod 'ReadiumLCP', podspec: 'https://raw.githubusercontent.com/readium/r2-lcp-swift/2.2.0/ReadiumLCP.podspec' 12 | 13 | pod 'GCDWebServer', podspec: 'https://raw.githubusercontent.com/readium/GCDWebServer/3.6.3/GCDWebServer.podspec' 14 | pod 'GRDB.swift' 15 | pod 'Kingfisher' 16 | pod 'MBProgressHUD' 17 | pod 'SwiftSoup' 18 | end 19 | 20 | post_install do |installer| 21 | installer.pods_project.targets.each do |target| 22 | target.build_configurations.each do |config| 23 | config.build_settings['SWIFT_VERSION'] = '5.1.4' 24 | config.build_settings['ENABLE_BITCODE'] = 'NO' 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /Integrations/CocoaPods/Podfile+lcp: -------------------------------------------------------------------------------- 1 | platform :ios, '10.3.1' 2 | 3 | target 'R2TestApp' do 4 | # Comment the next line if you don't want to use dynamic frameworks 5 | use_frameworks! 6 | 7 | pod 'R2Shared', podspec: 'https://raw.githubusercontent.com/readium/r2-shared-swift/2.2.0/R2Shared.podspec' 8 | pod 'R2Streamer', podspec: 'https://raw.githubusercontent.com/readium/r2-streamer-swift/2.2.0/R2Streamer.podspec' 9 | pod 'R2Navigator', podspec: 'https://raw.githubusercontent.com/readium/r2-navigator-swift/2.2.0/R2Navigator.podspec' 10 | pod 'ReadiumOPDS', podspec: 'https://raw.githubusercontent.com/readium/r2-opds-swift/2.2.0/ReadiumOPDS.podspec' 11 | pod 'ReadiumLCP', podspec: 'https://raw.githubusercontent.com/readium/r2-lcp-swift/2.2.0/ReadiumLCP.podspec' 12 | pod 'R2LCPClient', podspec: 'LCP_URL' 13 | 14 | pod 'GCDWebServer', podspec: 'https://raw.githubusercontent.com/readium/GCDWebServer/3.6.3/GCDWebServer.podspec' 15 | pod 'GRDB.swift' 16 | pod 'Kingfisher' 17 | pod 'MBProgressHUD' 18 | pod 'SwiftSoup' 19 | end 20 | 21 | post_install do |installer| 22 | installer.pods_project.targets.each do |target| 23 | target.build_configurations.each do |config| 24 | config.build_settings['SWIFT_VERSION'] = '5.1.4' 25 | config.build_settings['ENABLE_BITCODE'] = 'NO' 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /Integrations/CocoaPods/project+lcp.yml: -------------------------------------------------------------------------------- 1 | name: R2TestApp 2 | options: 3 | bundleIdPrefix: org.readium 4 | targets: 5 | R2TestApp: 6 | type: application 7 | platform: iOS 8 | deploymentTarget: "13.6" 9 | sources: 10 | - path: Sources 11 | excludes: 12 | - Resources/Samples 13 | - path: Sources/Resources/Samples 14 | type: folder 15 | settings: 16 | OTHER_SWIFT_FLAGS: $(inherited) -DLCP 17 | -------------------------------------------------------------------------------- /Integrations/CocoaPods/project.yml: -------------------------------------------------------------------------------- 1 | name: R2TestApp 2 | options: 3 | bundleIdPrefix: org.readium 4 | targets: 5 | R2TestApp: 6 | type: application 7 | platform: iOS 8 | deploymentTarget: "13.6" 9 | sources: 10 | - path: Sources 11 | excludes: 12 | - Resources/Samples 13 | - path: Sources/Resources/Samples 14 | type: folder 15 | -------------------------------------------------------------------------------- /Integrations/SPM/project+lcp.yml: -------------------------------------------------------------------------------- 1 | name: R2TestApp 2 | options: 3 | bundleIdPrefix: org.readium 4 | packages: 5 | R2Shared: 6 | url: https://github.com/readium/r2-shared-swift.git 7 | from: 2.2.0 8 | R2Streamer: 9 | url: https://github.com/readium/r2-streamer-swift.git 10 | from: 2.2.0 11 | R2Navigator: 12 | url: https://github.com/readium/r2-navigator-swift.git 13 | from: 2.2.0 14 | ReadiumOPDS: 15 | url: https://github.com/readium/r2-opds-swift.git 16 | from: 2.2.0 17 | ReadiumLCP: 18 | url: https://github.com/readium/r2-lcp-swift.git 19 | from: 2.2.0 20 | GRDB: 21 | url: https://github.com/groue/GRDB.swift.git 22 | from: 5.8.0 23 | Kingfisher: 24 | url: https://github.com/onevcat/Kingfisher.git 25 | from: 5.15.8 26 | MBProgressHUD: 27 | url: https://github.com/jdg/MBProgressHUD.git 28 | from: 1.2.0 29 | SwiftSoup: 30 | url: https://github.com/scinfu/SwiftSoup.git 31 | from: 2.3.2 32 | targets: 33 | R2TestApp: 34 | type: application 35 | platform: iOS 36 | deploymentTarget: "13.6" 37 | sources: 38 | - path: Sources 39 | excludes: 40 | - Resources/Samples 41 | - path: Sources/Resources/Samples 42 | type: folder 43 | dependencies: 44 | - carthage: R2LCPClient 45 | - package: R2Shared 46 | - package: R2Streamer 47 | - package: R2Navigator 48 | - package: ReadiumOPDS 49 | - package: ReadiumLCP 50 | - package: GRDB 51 | - package: Kingfisher 52 | - package: MBProgressHUD 53 | - package: SwiftSoup 54 | settings: 55 | OTHER_SWIFT_FLAGS: -DLCP 56 | 57 | -------------------------------------------------------------------------------- /Integrations/SPM/project.yml: -------------------------------------------------------------------------------- 1 | name: R2TestApp 2 | options: 3 | bundleIdPrefix: org.readium 4 | packages: 5 | R2Shared: 6 | url: https://github.com/readium/r2-shared-swift.git 7 | from: 2.2.0 8 | R2Streamer: 9 | url: https://github.com/readium/r2-streamer-swift.git 10 | from: 2.2.0 11 | R2Navigator: 12 | url: https://github.com/readium/r2-navigator-swift.git 13 | from: 2.2.0 14 | ReadiumOPDS: 15 | url: https://github.com/readium/r2-opds-swift.git 16 | from: 2.2.0 17 | GRDB: 18 | url: https://github.com/groue/GRDB.swift.git 19 | from: 5.8.0 20 | Kingfisher: 21 | url: https://github.com/onevcat/Kingfisher.git 22 | from: 5.15.8 23 | MBProgressHUD: 24 | url: https://github.com/jdg/MBProgressHUD.git 25 | from: 1.2.0 26 | SwiftSoup: 27 | url: https://github.com/scinfu/SwiftSoup.git 28 | from: 2.3.2 29 | targets: 30 | R2TestApp: 31 | type: application 32 | platform: iOS 33 | deploymentTarget: "13.6" 34 | sources: 35 | - path: Sources 36 | excludes: 37 | - Resources/Samples 38 | - path: Sources/Resources/Samples 39 | type: folder 40 | dependencies: 41 | - package: R2Shared 42 | - package: R2Streamer 43 | - package: R2Navigator 44 | - package: ReadiumOPDS 45 | - package: GRDB 46 | - package: Kingfisher 47 | - package: MBProgressHUD 48 | 49 | -------------------------------------------------------------------------------- /Integrations/Submodules/project+lcp.yml: -------------------------------------------------------------------------------- 1 | name: R2TestApp 2 | options: 3 | bundleIdPrefix: org.readium 4 | packages: 5 | R2Shared: 6 | path: Integrations/Submodules/r2-shared-swift 7 | R2Streamer: 8 | path: Integrations/Submodules/r2-streamer-swift 9 | R2Navigator: 10 | path: Integrations/Submodules/r2-navigator-swift 11 | ReadiumOPDS: 12 | path: Integrations/Submodules/r2-opds-swift 13 | ReadiumLCP: 14 | path: Integrations/Submodules/r2-lcp-swift 15 | GRDB: 16 | url: https://github.com/groue/GRDB.swift.git 17 | from: 5.8.0 18 | Kingfisher: 19 | url: https://github.com/onevcat/Kingfisher.git 20 | from: 5.15.8 21 | MBProgressHUD: 22 | url: https://github.com/jdg/MBProgressHUD.git 23 | from: 1.2.0 24 | SwiftSoup: 25 | url: https://github.com/scinfu/SwiftSoup.git 26 | from: 2.3.2 27 | targets: 28 | R2TestApp: 29 | type: application 30 | platform: iOS 31 | deploymentTarget: "13.6" 32 | sources: 33 | - path: Sources 34 | excludes: 35 | - Resources/Samples 36 | - path: Sources/Resources/Samples 37 | type: folder 38 | dependencies: 39 | - carthage: R2LCPClient 40 | - package: R2Shared 41 | - package: R2Streamer 42 | - package: R2Navigator 43 | - package: ReadiumOPDS 44 | - package: ReadiumLCP 45 | - package: GRDB 46 | - package: Kingfisher 47 | - package: MBProgressHUD 48 | - package: SwiftSoup 49 | settings: 50 | OTHER_SWIFT_FLAGS: -DLCP 51 | -------------------------------------------------------------------------------- /Integrations/Submodules/project.yml: -------------------------------------------------------------------------------- 1 | name: R2TestApp 2 | options: 3 | bundleIdPrefix: org.readium 4 | packages: 5 | R2Shared: 6 | path: Integrations/Submodules/r2-shared-swift 7 | R2Streamer: 8 | path: Integrations/Submodules/r2-streamer-swift 9 | R2Navigator: 10 | path: Integrations/Submodules/r2-navigator-swift 11 | ReadiumOPDS: 12 | path: Integrations/Submodules/r2-opds-swift 13 | GRDB: 14 | url: https://github.com/groue/GRDB.swift.git 15 | from: 5.8.0 16 | Kingfisher: 17 | url: https://github.com/onevcat/Kingfisher.git 18 | from: 5.15.8 19 | MBProgressHUD: 20 | url: https://github.com/jdg/MBProgressHUD.git 21 | from: 1.2.0 22 | SwiftSoup: 23 | url: https://github.com/scinfu/SwiftSoup.git 24 | from: 2.3.2 25 | targets: 26 | R2TestApp: 27 | type: application 28 | platform: iOS 29 | deploymentTarget: "13.6" 30 | sources: 31 | - path: Sources 32 | excludes: 33 | - Resources/Samples 34 | - path: Sources/Resources/Samples 35 | type: folder 36 | dependencies: 37 | - package: R2Shared 38 | - package: R2Streamer 39 | - package: R2Navigator 40 | - package: ReadiumOPDS 41 | - package: GRDB 42 | - package: Kingfisher 43 | - package: MBProgressHUD 44 | - package: SwiftSoup 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Readium 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | help: 2 | @echo "Usage: make [lcp=]\n\n\ 3 | Choose one of the following targets to generate:\n\ 4 | spm\t\t(recommended) Integration with Swift Package Manager\n\ 5 | carthage\tIntegration with Carthage\n\ 6 | cocoapods\tIntegration with CocoaPods\n\ 7 | dev\t\tIntegration with Git submodules and SPM, for contributors\n\n\ 8 | To enable Readium LCP, provide the liblcp URL EDRLab gave you, e.g.\n\ 9 | $$ make spm lcp=https://...\n\ 10 | " 11 | 12 | clean: 13 | @rm -f project.yml 14 | @rm -f Podfile* 15 | @rm -f Cartfile* 16 | @rm -rf Carthage 17 | @rm -rf Pods 18 | @rm -rf R2TestApp.xcodeproj 19 | @rm -rf R2TestApp.xcworkspace 20 | 21 | spm: clean 22 | ifdef lcp 23 | @echo "binary \"$(lcp)\"" > Cartfile 24 | carthage update --platform ios --cache-builds 25 | @cp Integrations/SPM/project+lcp.yml project.yml 26 | else 27 | @cp Integrations/SPM/project.yml . 28 | endif 29 | xcodegen generate 30 | @echo "\nopen R2TestApp.xcodeproj" 31 | 32 | carthage: clean 33 | ifdef lcp 34 | @cp Integrations/Carthage/project+lcp.yml project.yml 35 | @cp Integrations/Carthage/Cartfile+lcp Cartfile 36 | @echo "binary \"$(lcp)\"" >> Cartfile 37 | else 38 | @cp Integrations/Carthage/project.yml . 39 | @cp Integrations/Carthage/Cartfile . 40 | endif 41 | carthage update --platform ios --use-xcframeworks --cache-builds 42 | xcodegen generate 43 | @echo "\nopen R2TestApp.xcodeproj" 44 | 45 | cocoapods: clean 46 | ifdef lcp 47 | @sed -e "s>LCP_URL>$(lcp)>g" Integrations/CocoaPods/Podfile+lcp > Podfile 48 | @cp Integrations/CocoaPods/project+lcp.yml project.yml 49 | else 50 | @cp Integrations/CocoaPods/project.yml . 51 | @cp Integrations/CocoaPods/Podfile . 52 | endif 53 | xcodegen generate 54 | pod install 55 | @echo "\nopen R2TestApp.xcworkspace" 56 | 57 | dev: clean 58 | ifdef lcp 59 | @cp Integrations/Submodules/project+lcp.yml project.yml 60 | @echo "binary \"$(lcp)\"" > Cartfile 61 | carthage update --platform ios --cache-builds 62 | else 63 | @cp Integrations/Submodules/project.yml . 64 | endif 65 | git submodule update --init --recursive 66 | xcodegen generate 67 | @echo "\nopen R2TestApp.xcodeproj" 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > **:warning: ᴛʜɪs ʀᴇᴘᴏsɪᴛᴏʀʏ ɪs ᴅᴇᴘʀᴇᴄᴀᴛᴇᴅ :warning:** 2 | > 3 | > We moved all the `r2-*-swift` modules to a single repository: [`swift-toolkit`](https://github.com/readium/swift-toolkit). 4 | 5 | 6 | -------------------------------------------------------------------------------- /Sources/App/AboutTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AboutTableViewController.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Geoffrey Bugniot on 27/04/2018. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import UIKit 14 | 15 | 16 | class AboutTableViewController: UITableViewController { 17 | 18 | @IBOutlet weak var versionNumberCell: UITableViewCell! 19 | @IBOutlet weak var buildNumberCell: UITableViewCell! 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | 24 | versionNumberCell.textLabel?.text = NSLocalizedString("app_version_caption", comment: "Caption for the app version in About screen") 25 | versionNumberCell.detailTextLabel?.text = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String 26 | 27 | buildNumberCell.textLabel?.text = NSLocalizedString("build_version_caption", comment: "Caption for the build version in About screen") 28 | buildNumberCell.detailTextLabel?.text = Bundle.main.infoDictionary?["CFBundleVersion"] as? String 29 | } 30 | 31 | // MARK: - Table view data source 32 | 33 | override func numberOfSections(in tableView: UITableView) -> Int { 34 | return 3 35 | } 36 | 37 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 38 | return 2 39 | } 40 | 41 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 42 | var url: URL? 43 | 44 | if indexPath.section == 1 { 45 | if indexPath.row == 0 { 46 | url = URL(string: "https://www.edrlab.org/") 47 | } else { 48 | url = URL(string: "https://opensource.org/licenses/BSD-3-Clause") 49 | } 50 | } 51 | 52 | if let url = url { 53 | UIApplication.shared.open(url) 54 | } 55 | 56 | tableView.deselectRow(at: indexPath, animated: true) 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /Sources/App/AppModule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppModule.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 20.02.19. 6 | // 7 | // Copyright 2019 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Combine 14 | import Foundation 15 | import UIKit 16 | import R2Shared 17 | import R2Streamer 18 | 19 | 20 | /// Base module delegate, that sub-modules' delegate can extend. 21 | /// Provides basic shared functionalities. 22 | protocol ModuleDelegate: AnyObject { 23 | func presentAlert(_ title: String, message: String, from viewController: UIViewController) 24 | func presentError(_ error: Error?, from viewController: UIViewController) 25 | } 26 | 27 | 28 | /// Main application module, it: 29 | /// - owns the sub-modules (library, reader, etc.) 30 | /// - orchestrates the communication between its sub-modules, through the modules' delegates. 31 | final class AppModule { 32 | 33 | // App modules 34 | var library: LibraryModuleAPI! = nil 35 | var reader: ReaderModuleAPI! = nil 36 | var opds: OPDSModuleAPI! = nil 37 | 38 | init() throws { 39 | guard let server = PublicationServer() else { 40 | /// FIXME: we should recover properly if the publication server can't start, maybe this should only forbid opening a publication? 41 | fatalError("Can't start publication server") 42 | } 43 | 44 | let httpClient = DefaultHTTPClient() 45 | let db = try Database(file: Paths.library.appendingPathComponent("database.db")) 46 | let books = BookRepository(db: db) 47 | let bookmarks = BookmarkRepository(db: db) 48 | 49 | library = LibraryModule(delegate: self, books: books, server: server, httpClient: httpClient) 50 | reader = ReaderModule(delegate: self, books: books, bookmarks: bookmarks, resourcesServer: server) 51 | opds = OPDSModule(delegate: self) 52 | 53 | // Set Readium 2's logging minimum level. 54 | R2EnableLog(withMinimumSeverityLevel: .debug) 55 | 56 | library.preloadSamples() 57 | } 58 | 59 | private(set) lazy var aboutViewController: UIViewController = { 60 | let storyboard = UIStoryboard(name: "App", bundle: nil) 61 | let aboutViewController = storyboard.instantiateViewController(withIdentifier: "AboutTableViewController") as! AboutTableViewController 62 | return UINavigationController(rootViewController: aboutViewController) 63 | }() 64 | 65 | } 66 | 67 | 68 | extension AppModule: ModuleDelegate { 69 | 70 | func presentAlert(_ title: String, message: String, from viewController: UIViewController) { 71 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) 72 | let dismissButton = UIAlertAction(title: NSLocalizedString("ok_button", comment: "Alert button"), style: .cancel) 73 | alert.addAction(dismissButton) 74 | viewController.present(alert, animated: true) 75 | } 76 | 77 | func presentError(_ error: Error?, from viewController: UIViewController) { 78 | guard let error = error else { return } 79 | if case LibraryError.cancelled = error { return } 80 | presentAlert( 81 | NSLocalizedString("error_title", comment: "Alert title for errors"), 82 | message: error.localizedDescription, 83 | from: viewController 84 | ) 85 | } 86 | 87 | } 88 | 89 | 90 | extension AppModule: LibraryModuleDelegate { 91 | 92 | func libraryDidSelectPublication(_ publication: Publication, book: Book, completion: @escaping () -> Void) { 93 | reader.presentPublication(publication: publication, book: book, in: library.rootViewController, completion: completion) 94 | } 95 | 96 | } 97 | 98 | 99 | extension AppModule: ReaderModuleDelegate { 100 | } 101 | 102 | 103 | extension AppModule: OPDSModuleDelegate { 104 | 105 | func opdsDownloadPublication(_ publication: Publication?, at link: Link, sender: UIViewController) -> AnyPublisher { 106 | guard let url = link.url(relativeTo: publication?.baseURL) else { 107 | return .fail(.cancelled) 108 | } 109 | 110 | return library.importPublication(from: url, sender: sender) 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /Sources/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Alexandre Camilleri on 6/12/17. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Combine 14 | import UIKit 15 | 16 | @UIApplicationMain 17 | class AppDelegate: UIResponder, UIApplicationDelegate { 18 | 19 | var window: UIWindow? 20 | 21 | private var app: AppModule! 22 | private var subscriptions = Set() 23 | 24 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 25 | app = try! AppModule() 26 | 27 | func makeItem(title: String, image: String) -> UITabBarItem { 28 | return UITabBarItem( 29 | title: NSLocalizedString(title, comment: "Library tab title"), 30 | image: UIImage(named: image), 31 | tag: 0 32 | ) 33 | } 34 | 35 | // Library 36 | let libraryViewController = app.library.rootViewController 37 | libraryViewController.tabBarItem = makeItem(title: "bookshelf_tab", image: "bookshelf") 38 | 39 | // OPDS Feeds 40 | let opdsViewController = app.opds.rootViewController 41 | opdsViewController.tabBarItem = makeItem(title: "catalogs_tab", image: "catalogs") 42 | 43 | // About 44 | let aboutViewController = app.aboutViewController 45 | aboutViewController.tabBarItem = makeItem(title: "about_tab", image: "about") 46 | 47 | let tabBarController = UITabBarController() 48 | tabBarController.viewControllers = [ 49 | libraryViewController, 50 | opdsViewController, 51 | aboutViewController 52 | ] 53 | tabBarController.tabBar.tintColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) 54 | tabBarController.tabBar.isTranslucent = false 55 | 56 | window = UIWindow(frame: UIScreen.main.bounds) 57 | window?.rootViewController = tabBarController 58 | window?.makeKeyAndVisible() 59 | 60 | return true 61 | } 62 | 63 | func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { 64 | app.library.importPublication(from: url, sender: window!.rootViewController!) 65 | .assertNoFailure() 66 | .sink { _ in } 67 | .store(in: &subscriptions) 68 | return true 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /Sources/Common/Paths.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021 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 Combine 8 | import Foundation 9 | import R2Shared 10 | 11 | final class Paths { 12 | private init() {} 13 | 14 | static let home: URL = 15 | URL(fileURLWithPath: NSHomeDirectory(), isDirectory: true) 16 | 17 | static let temporary: URL = 18 | URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) 19 | 20 | static let documents: URL = 21 | FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! 22 | 23 | static let samples = Bundle.main.resourceURL!.appendingPathComponent("Samples") 24 | 25 | static let library: URL = 26 | FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first! 27 | 28 | static let covers: URL = { 29 | let url = library.appendingPathComponent("Covers") 30 | try! FileManager.default.createDirectory(at: url, withIntermediateDirectories: true) 31 | return url 32 | }() 33 | 34 | static func makeDocumentURL(for source: URL? = nil, title: String?, mediaType: MediaType) -> AnyPublisher { 35 | Future(on: .global()) { promise in 36 | // Is the file already in Documents/? 37 | if let source = source, source.standardizedFileURL.deletingLastPathComponent() == documents.standardizedFileURL { 38 | promise(.success(source)) 39 | } else { 40 | let title = title.takeIf { !$0.isEmpty } ?? UUID().uuidString 41 | let ext = mediaType.fileExtension?.addingPrefix(".") ?? "" 42 | let filename = "\(title)\(ext)".sanitizedPathComponent 43 | promise(.success(documents.appendingUniquePathComponent(filename))) 44 | } 45 | }.eraseToAnyPublisher() 46 | } 47 | 48 | static func makeTemporaryURL() -> AnyPublisher { 49 | Future(on: .global()) { promise in 50 | promise(.success(temporary.appendingUniquePathComponent())) 51 | }.eraseToAnyPublisher() 52 | } 53 | 54 | /// Returns whether the given `url` locates a file that is under the app's home directory. 55 | static func isAppFile(at url: URL) -> Bool { 56 | home.isParentOf(url) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/Common/Publication.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Publication.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 26.06.19. 6 | // 7 | // Copyright 2019 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import CoreServices 14 | import Foundation 15 | import R2Shared 16 | 17 | extension Publication { 18 | 19 | /// Finds all the downloadable links for this publication. 20 | var downloadLinks: [Link] { 21 | links.filter { 22 | return DocumentTypes.main.supportsMediaType($0.type) 23 | || DocumentTypes.main.supportsFileExtension($0.url(relativeTo: nil)?.pathExtension) 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Common/Toolkit/BarButtonItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BarButtonItem.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Matt McCullough on 28/04/2020. 6 | // 7 | // Copyright 2020 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Foundation 14 | import UIKit 15 | 16 | class BarButtonItem: UIBarButtonItem { 17 | typealias ActionFunc = (UIBarButtonItem) -> Void 18 | 19 | private var actionFunc: ActionFunc? 20 | 21 | convenience init(title: String?, style: UIBarButtonItem.Style, actionHandler: ActionFunc?) { 22 | self.init(title: title, style: style, target: nil, action: #selector(handlePress)) 23 | target = self 24 | self.actionFunc = actionHandler 25 | } 26 | 27 | convenience init(image: UIImage?, style: UIBarButtonItem.Style, actionFunc: ActionFunc?) { 28 | self.init(image: image, style: style, target: nil, action: #selector(handlePress)) 29 | target = self 30 | self.actionFunc = actionFunc 31 | } 32 | 33 | convenience init(barButtonSystemItem systemItem: UIBarButtonItem.SystemItem, actionHandler: ActionFunc?) { 34 | self.init(barButtonSystemItem: systemItem, target: nil, action: #selector(handlePress)) 35 | target = self 36 | self.actionFunc = actionHandler 37 | } 38 | 39 | @objc func handlePress(sender: UIBarButtonItem) { 40 | actionFunc?(sender) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Common/Toolkit/Extensions/AnyPublisher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021 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 Combine 8 | 9 | extension AnyPublisher { 10 | 11 | public static func just(_ value: Output) -> Self { 12 | Just(value) 13 | .setFailureType(to: Failure.self) 14 | .eraseToAnyPublisher() 15 | } 16 | 17 | public static func fail(_ error: Failure) -> Self { 18 | Fail(error: error).eraseToAnyPublisher() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/Common/Toolkit/Extensions/Future.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021 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 Combine 8 | import Foundation 9 | 10 | extension Future { 11 | /// Creates a `Future` which runs asynchronously on the given `queue`. 12 | public convenience init(on queue: DispatchQueue, _ attemptToFulfill: @escaping (@escaping Future.Promise) -> Void) { 13 | self.init { promise in 14 | queue.async { 15 | attemptToFulfill(promise) 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/Common/Toolkit/Extensions/HTTPClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021 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 Combine 8 | import Foundation 9 | import R2Shared 10 | 11 | struct HTTPDownload { 12 | let file: URL 13 | let response: HTTPResponse 14 | } 15 | 16 | extension HTTPClient { 17 | 18 | func fetch(_ request: HTTPRequestConvertible) -> AnyPublisher { 19 | var cancellable: R2Shared.Cancellable? = nil 20 | return Future { promise in 21 | cancellable = self.fetch(request, completion: promise) 22 | } 23 | .handleEvents(receiveCancel: { cancellable?.cancel() }) 24 | .eraseToAnyPublisher() 25 | } 26 | 27 | func download(_ request: HTTPRequestConvertible, progress: @escaping (Double) -> Void) -> AnyPublisher { 28 | openTemporaryFileForWriting() 29 | .flatMap { (destination, handle) -> AnyPublisher in 30 | var cancellable: R2Shared.Cancellable? = nil 31 | 32 | return Future { promise in 33 | cancellable = self.stream(request, 34 | consume: { data, progression in 35 | if let progression = progression { 36 | progress(progression) 37 | } 38 | handle.write(data) 39 | }, 40 | completion: { result in 41 | do { 42 | try handle.close() 43 | promise(.success(HTTPDownload(file: destination, response: try result.get()))) 44 | } catch { 45 | try? FileManager.default.removeItem(at: destination) 46 | promise(.failure(HTTPError(error: error))) 47 | } 48 | }) 49 | } 50 | .handleEvents(receiveCancel: { 51 | cancellable?.cancel() 52 | try? handle.close() 53 | try? FileManager.default.removeItem(at: destination) 54 | }) 55 | .eraseToAnyPublisher() 56 | } 57 | .eraseToAnyPublisher() 58 | } 59 | 60 | private func openTemporaryFileForWriting() -> AnyPublisher<(URL, FileHandle), HTTPError> { 61 | Paths.makeTemporaryURL() 62 | .tryMap { destination in 63 | // Makes sure the file exists. 64 | try "".write(to: destination, atomically: true, encoding: .utf8) 65 | let handle = try FileHandle(forWritingTo: destination) 66 | return (destination, handle) 67 | } 68 | .mapError { HTTPError(kind: .other, cause: $0) } 69 | .eraseToAnyPublisher() 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Sources/Common/Toolkit/Extensions/Locator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021 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 R2Shared 9 | 10 | extension Locator: Codable { 11 | public init(from decoder: Decoder) throws { 12 | let json = try decoder.singleValueContainer().decode(String.self) 13 | try self.init(jsonString: json)! 14 | } 15 | 16 | public func encode(to encoder: Encoder) throws { 17 | var container = encoder.singleValueContainer() 18 | try container.encode(jsonString) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/Common/Toolkit/Extensions/String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021 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 | 9 | extension String { 10 | 11 | /// Returns this string after removing any character forbidden in a single path component. 12 | var sanitizedPathComponent: String { 13 | // See https://superuser.com/a/358861 14 | let invalidCharacters = CharacterSet(charactersIn: "\\/:*?\"<>|") 15 | .union(.newlines) 16 | .union(.illegalCharacters) 17 | .union(.controlCharacters) 18 | 19 | return components(separatedBy: invalidCharacters) 20 | .joined(separator: " ") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Common/Toolkit/Extensions/UIImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Alexandre Camilleri on 8/24/17. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import UIKit 14 | 15 | /// SO 33545910/1585121 - Heberti Almeida (inspired). 16 | extension UIImage { 17 | class func imageWithTextView(textView: UITextView) -> UIImage { 18 | UIGraphicsBeginImageContextWithOptions(textView.bounds.size, false, 0.0) 19 | textView.layer.render(in: UIGraphicsGetCurrentContext()!) 20 | let image = UIGraphicsGetImageFromCurrentImageContext() 21 | UIGraphicsEndImageContext() 22 | return image! 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/Common/Toolkit/Extensions/UIViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 20.02.19. 6 | // 7 | // Copyright 2019 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Foundation 14 | import UIKit 15 | 16 | extension UIViewController { 17 | 18 | /// Finds the first child view controller with the given type, recursively. 19 | func findChildViewController() -> T? { 20 | for childViewController in children { 21 | if let found = childViewController as? T { 22 | return found 23 | } 24 | if let found: T = childViewController.findChildViewController() { 25 | return found 26 | } 27 | } 28 | return nil 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Sources/Common/Toolkit/ScreenOrientation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScreenOrientation.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 16/04/2020. 6 | // 7 | // Copyright 2020 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Foundation 14 | import UIKit 15 | 16 | enum ScreenOrientation: String { 17 | case landscape 18 | case portrait 19 | 20 | static var current: ScreenOrientation { 21 | let orientation = UIDevice.current.orientation 22 | return orientation.isLandscape ? .landscape : .portrait 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Data/Book.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021 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 Combine 8 | import Foundation 9 | import GRDB 10 | import R2Shared 11 | 12 | struct Book: Codable { 13 | struct Id: EntityId { let rawValue: Int64 } 14 | 15 | let id: Id? 16 | /// Canonical identifier for the publication, extracted from its metadata. 17 | var identifier: String? 18 | /// Title of the publication, extracted from its metadata. 19 | var title: String 20 | /// Authors of the publication, separated by commas. 21 | var authors: String? 22 | /// Media type associated to the publication. 23 | var type: String 24 | /// Location of the packaged publication or a manifest. 25 | var path: String 26 | /// Location of the cover. 27 | var coverPath: String? 28 | /// Last read location in the publication. 29 | var locator: Locator? { 30 | didSet { progression = locator?.locations.totalProgression ?? 0 } 31 | } 32 | /// Current progression in the publication, extracted from the locator. 33 | var progression: Double 34 | /// Date of creation. 35 | var created: Date 36 | 37 | var mediaType: MediaType { MediaType.of(mediaType: type) ?? .binary } 38 | 39 | init(id: Id? = nil, identifier: String? = nil, title: String, authors: String? = nil, type: String, path: String, coverPath: String? = nil, locator: Locator? = nil, created: Date = Date()) { 40 | self.id = id 41 | self.identifier = identifier 42 | self.title = title 43 | self.authors = authors 44 | self.type = type 45 | self.path = path 46 | self.coverPath = coverPath 47 | self.locator = locator 48 | self.progression = locator?.locations.totalProgression ?? 0 49 | self.created = created 50 | } 51 | 52 | var cover: URL? { 53 | coverPath.map { Paths.covers.appendingPathComponent($0) } 54 | } 55 | } 56 | 57 | extension Book: TableRecord, FetchableRecord, PersistableRecord { 58 | enum Columns: String, ColumnExpression { 59 | case id, identifier, title, type, path, coverPath, locator, progression, created 60 | } 61 | } 62 | 63 | final class BookRepository { 64 | private let db: Database 65 | 66 | init(db: Database) { 67 | self.db = db 68 | } 69 | 70 | func all() -> AnyPublisher<[Book], Error> { 71 | db.observe { db in 72 | try Book.order(Book.Columns.created).fetchAll(db) 73 | } 74 | } 75 | 76 | func add(_ book: Book) -> AnyPublisher { 77 | return db.write { db in 78 | try book.insert(db) 79 | return Book.Id(rawValue: db.lastInsertedRowID) 80 | }.eraseToAnyPublisher() 81 | } 82 | 83 | func remove(_ id: Book.Id) -> AnyPublisher { 84 | db.write { db in try Book.deleteOne(db, key: id) } 85 | } 86 | 87 | func saveProgress(for id: Book.Id, locator: Locator) -> AnyPublisher { 88 | guard let json = locator.jsonString else { 89 | return .just(()) 90 | } 91 | 92 | return db.write { db in 93 | try db.execute(literal: """ 94 | UPDATE book 95 | SET locator = \(json), progression = \(locator.locations.totalProgression ?? 0) 96 | WHERE id = \(id) 97 | """) 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Sources/Data/Bookmark.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021 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 Combine 8 | import Foundation 9 | import GRDB 10 | import R2Shared 11 | 12 | struct Bookmark: Codable { 13 | struct Id: EntityId { let rawValue: Int64 } 14 | 15 | let id: Id? 16 | /// Foreign key to the publication. 17 | var bookId: Book.Id 18 | /// Location in the publication. 19 | var locator: Locator 20 | /// Progression in the publication, extracted from the locator. 21 | var progression: Double? 22 | /// Date of creation. 23 | var created: Date = Date() 24 | 25 | init(id: Id? = nil, bookId: Book.Id, locator: Locator, created: Date = Date()) { 26 | self.id = id 27 | self.bookId = bookId 28 | self.locator = locator 29 | self.progression = locator.locations.totalProgression 30 | self.created = created 31 | } 32 | } 33 | 34 | extension Bookmark: TableRecord, FetchableRecord, PersistableRecord { 35 | enum Columns: String, ColumnExpression { 36 | case id, bookId, locator, progression, created 37 | } 38 | } 39 | 40 | final class BookmarkRepository { 41 | private let db: Database 42 | 43 | init(db: Database) { 44 | self.db = db 45 | } 46 | 47 | func all(for bookId: Book.Id) -> AnyPublisher<[Bookmark], Error> { 48 | db.observe { db in 49 | try Bookmark 50 | .filter(Bookmark.Columns.bookId == bookId) 51 | .order(Bookmark.Columns.progression) 52 | .fetchAll(db) 53 | } 54 | } 55 | 56 | func add(_ bookmark: Bookmark) -> AnyPublisher { 57 | return db.write { db in 58 | try bookmark.insert(db) 59 | return Bookmark.Id(rawValue: db.lastInsertedRowID) 60 | }.eraseToAnyPublisher() 61 | } 62 | 63 | func remove(_ id: Bookmark.Id) -> AnyPublisher { 64 | db.write { db in try Bookmark.deleteOne(db, key: id) } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/Data/Database.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021 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 Combine 8 | import Foundation 9 | import GRDB 10 | 11 | final class Database { 12 | 13 | convenience init(file: URL) throws { 14 | try self.init(writer: try DatabaseQueue(path: file.path)) 15 | } 16 | 17 | private let writer: DatabaseWriter 18 | 19 | private init(writer: DatabaseWriter = DatabaseQueue()) throws { 20 | self.writer = writer 21 | 22 | try writer.write { db in 23 | try db.create(table: "book", ifNotExists: true) { t in 24 | t.autoIncrementedPrimaryKey("id") 25 | t.column("identifier", .text) 26 | t.column("title", .text).notNull() 27 | t.column("authors", .text) 28 | t.column("type", .text).notNull() 29 | t.column("path", .text).notNull() 30 | t.column("coverPath", .text) 31 | t.column("locator", .text) 32 | t.column("progression", .integer).notNull().defaults(to: 0) 33 | t.column("created", .datetime).notNull() 34 | } 35 | 36 | try db.create(table: "bookmark", ifNotExists: true) { t in 37 | t.autoIncrementedPrimaryKey("id") 38 | t.column("bookId", .integer).references("book", onDelete: .cascade).notNull() 39 | t.column("locator", .text) 40 | t.column("progression", .double).notNull() 41 | t.column("created", .datetime).notNull() 42 | } 43 | } 44 | } 45 | 46 | func read(_ query: @escaping (GRDB.Database) throws -> T) -> AnyPublisher { 47 | writer.readPublisher(value: query) 48 | .eraseToAnyPublisher() 49 | } 50 | 51 | func write(_ updates: @escaping (GRDB.Database) throws -> T) -> AnyPublisher { 52 | writer.writePublisher(updates: updates) 53 | .eraseToAnyPublisher() 54 | } 55 | 56 | func observe(_ query: @escaping (GRDB.Database) throws -> T) -> AnyPublisher { 57 | ValueObservation.tracking(query) 58 | .publisher(in: writer) 59 | .eraseToAnyPublisher() 60 | } 61 | } 62 | 63 | /// Protocol for a database entity id. 64 | /// 65 | /// Using this instead of regular integers makes the code safer, because we can only give ids of the 66 | /// right model in APIs. It also helps self-document APIs. 67 | protocol EntityId: Codable, Hashable, RawRepresentable, ExpressibleByIntegerLiteral, CustomStringConvertible, DatabaseValueConvertible where RawValue == Int64 {} 68 | 69 | extension EntityId { 70 | 71 | // MARK: - ExpressibleByIntegerLiteral 72 | 73 | init(integerLiteral value: Int64) { 74 | self.init(rawValue: value)! 75 | } 76 | 77 | // MARK: - Codable 78 | 79 | init(from decoder: Decoder) throws { 80 | self.init(rawValue: try decoder.singleValueContainer().decode(Int64.self))! 81 | } 82 | 83 | func encode(to encoder: Encoder) throws { 84 | var container = encoder.singleValueContainer() 85 | try container.encode(rawValue) 86 | } 87 | 88 | // MARK: - CustomStringConvertible 89 | 90 | var description: String { 91 | "\(Self.self)(\(rawValue))" 92 | } 93 | 94 | // MARK: - DatabaseValueConvertible 95 | 96 | var databaseValue: DatabaseValue { rawValue.databaseValue } 97 | 98 | static func fromDatabaseValue(_ dbValue: DatabaseValue) -> Self? { 99 | Int64.fromDatabaseValue(dbValue).map(Self.init) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Sources/Library/Base.lproj/PublicationMenuViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 36 | 46 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /Sources/Library/DRM/DRMLibraryService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DRMLibraryService.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 01.02.19. 6 | // 7 | // Copyright 2019 Readium Foundation. All rights reserved. 8 | // Use of this source code is governed by a BSD-style license which is detailed 9 | // in the LICENSE file present in the project repository where this source code is maintained. 10 | // 11 | 12 | import Combine 13 | import Foundation 14 | import R2Shared 15 | 16 | 17 | struct DRMFulfilledPublication { 18 | let localURL: URL 19 | let suggestedFilename: String 20 | } 21 | 22 | protocol DRMLibraryService { 23 | 24 | /// Returns the `ContentProtection` which will be provided to the `Streamer`, to unlock 25 | /// publications. 26 | var contentProtection: ContentProtection? { get } 27 | 28 | /// Returns whether this DRM can fulfill the given file into a protected publication. 29 | func canFulfill(_ file: URL) -> Bool 30 | 31 | /// Fulfills the given file to the fully protected publication. 32 | func fulfill(_ file: URL) -> AnyPublisher 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Sources/Library/DRM/LCPLibraryService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LCPLibraryService.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 01.02.19. 6 | // 7 | // Copyright 2019 Readium Foundation. All rights reserved. 8 | // Use of this source code is governed by a BSD-style license which is detailed 9 | // in the LICENSE file present in the project repository where this source code is maintained. 10 | // 11 | 12 | #if LCP 13 | 14 | import Combine 15 | import Foundation 16 | import UIKit 17 | import R2Shared 18 | import R2LCPClient 19 | import ReadiumLCP 20 | 21 | 22 | class LCPLibraryService: DRMLibraryService { 23 | 24 | private var lcpService = LCPService(client: LCPClient()) 25 | 26 | lazy var contentProtection: ContentProtection? = lcpService.contentProtection() 27 | 28 | func canFulfill(_ file: URL) -> Bool { 29 | return file.pathExtension.lowercased() == "lcpl" 30 | } 31 | 32 | func fulfill(_ file: URL) -> AnyPublisher { 33 | Future { promise in 34 | self.lcpService.acquirePublication(from: file) { result in 35 | // Removes the license file, but only if it's in the App directory (e.g. Inbox/). 36 | // Otherwise we might delete something from a shared location (e.g. iCloud). 37 | if Paths.isAppFile(at: file) { 38 | try? FileManager.default.removeItem(at: file) 39 | } 40 | 41 | switch result { 42 | case .success(let pub): 43 | promise(.success(DRMFulfilledPublication( 44 | localURL: pub.localURL, 45 | suggestedFilename: pub.suggestedFilename 46 | ))) 47 | case .failure(let error): 48 | promise(.failure(error)) 49 | case .cancelled: 50 | promise(.success(nil)) 51 | } 52 | } 53 | }.eraseToAnyPublisher() 54 | } 55 | } 56 | 57 | /// Facade to the private R2LCPClient.framework. 58 | class LCPClient: ReadiumLCP.LCPClient { 59 | 60 | func createContext(jsonLicense: String, hashedPassphrase: String, pemCrl: String) throws -> LCPClientContext { 61 | return try R2LCPClient.createContext(jsonLicense: jsonLicense, hashedPassphrase: hashedPassphrase, pemCrl: pemCrl) 62 | } 63 | 64 | func decrypt(data: Data, using context: LCPClientContext) -> Data? { 65 | return R2LCPClient.decrypt(data: data, using: context as! DRMContext) 66 | } 67 | 68 | func findOneValidPassphrase(jsonLicense: String, hashedPassphrases: [String]) -> String? { 69 | return R2LCPClient.findOneValidPassphrase(jsonLicense: jsonLicense, hashedPassphrases: hashedPassphrases) 70 | } 71 | 72 | } 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /Sources/Library/DetailsTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailsTableViewController.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Alexandre Camilleri on 11/20/17. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import UIKit 14 | import R2Shared 15 | 16 | protocol DetailsTableViewControllerFactory { 17 | func make(publication: Publication) -> DetailsTableViewController 18 | } 19 | 20 | final class DetailsTableViewController: UITableViewController { 21 | 22 | var publication: Publication! 23 | 24 | // Informations 25 | @IBOutlet weak var titleLabel: UILabel! 26 | @IBOutlet weak var idLabel: UILabel! 27 | 28 | override func viewDidLoad() { 29 | titleLabel.text = publication?.metadata.title 30 | idLabel.text = publication?.metadata.identifier 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Library/LibraryError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LibraryError.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 12.06.19. 6 | // 7 | // Copyright 2019 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Foundation 14 | import R2Shared 15 | 16 | enum LibraryError: LocalizedError { 17 | 18 | case publicationIsNotValid 19 | case bookNotFound 20 | case bookDeletionFailed(Error?) 21 | case importFailed(Error) 22 | case openFailed(Error) 23 | case downloadFailed(Error) 24 | case cancelled 25 | 26 | var errorDescription: String? { 27 | switch self { 28 | case .publicationIsNotValid: 29 | return NSLocalizedString("library_error_publicationIsNotValid", comment: "Error message used when trying to import a publication that is not valid") 30 | case .bookNotFound: 31 | return NSLocalizedString("library_error_bookNotFound", comment: "Error message used when trying to open a book whose file is not found") 32 | case .importFailed(let error): 33 | return String(format: NSLocalizedString("library_error_importFailed", comment: "Error message used when a low-level error occured while importing a publication"), error.localizedDescription) 34 | case .openFailed(let error): 35 | return String(format: NSLocalizedString("library_error_openFailed", comment: "Error message used when a low-level error occured while opening a publication"), error.localizedDescription) 36 | case .downloadFailed(let error): 37 | return String(format: NSLocalizedString("library_error_downloadFailed", comment: "Error message when the download of a publication failed"), error.localizedDescription) 38 | default: 39 | return nil 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Sources/Library/LibraryFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LibraryFactory.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 22.02.19. 6 | // 7 | // Copyright 2019 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Foundation 14 | import UIKit 15 | import R2Shared 16 | 17 | 18 | final class LibraryFactory { 19 | 20 | fileprivate let storyboard = UIStoryboard(name: "Library", bundle: nil) 21 | fileprivate let libraryService: LibraryService 22 | 23 | init(libraryService: LibraryService) { 24 | self.libraryService = libraryService 25 | } 26 | 27 | } 28 | 29 | extension LibraryFactory: LibraryViewControllerFactory { 30 | func make() -> LibraryViewController { 31 | let library = storyboard.instantiateViewController(withIdentifier: "LibraryViewController") as! LibraryViewController 32 | library.factory = self 33 | library.library = libraryService 34 | return library 35 | } 36 | } 37 | 38 | extension LibraryFactory: DetailsTableViewControllerFactory { 39 | func make(publication: Publication) -> DetailsTableViewController { 40 | let controller = storyboard.instantiateViewController(withIdentifier: "DetailsTableViewController") as! DetailsTableViewController 41 | controller.publication = publication 42 | return controller 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /Sources/Library/LibraryModule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LibraryModule.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 22.02.19. 6 | // 7 | // Copyright 2019 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Combine 14 | import Foundation 15 | import R2Shared 16 | import R2Streamer 17 | import UIKit 18 | 19 | 20 | /// The Library module handles the presentation of the bookshelf, and the publications' management. 21 | protocol LibraryModuleAPI { 22 | 23 | var delegate: LibraryModuleDelegate? { get } 24 | 25 | /// Root navigation controller containing the Library. 26 | /// Can be used to present the library to the user. 27 | var rootViewController: UINavigationController { get } 28 | 29 | /// Loads the sample publications if needed. 30 | func preloadSamples() 31 | 32 | /// Imports a new publication to the library, either from: 33 | /// - a local file URL 34 | /// - a remote URL which will be downloaded 35 | func importPublication(from url: URL, sender: UIViewController) -> AnyPublisher 36 | 37 | } 38 | 39 | protocol LibraryModuleDelegate: ModuleDelegate { 40 | 41 | /// Called when the user tap on a publication in the library. 42 | func libraryDidSelectPublication(_ publication: Publication, book: Book, completion: @escaping () -> Void) 43 | 44 | } 45 | 46 | 47 | final class LibraryModule: LibraryModuleAPI { 48 | 49 | weak var delegate: LibraryModuleDelegate? 50 | 51 | private let library: LibraryService 52 | private let factory: LibraryFactory 53 | private var subscriptions = Set() 54 | 55 | init(delegate: LibraryModuleDelegate?, books: BookRepository, server: PublicationServer, httpClient: HTTPClient) { 56 | self.library = LibraryService(books: books, publicationServer: server, httpClient: httpClient) 57 | self.factory = LibraryFactory(libraryService: library) 58 | self.delegate = delegate 59 | } 60 | 61 | private(set) lazy var rootViewController: UINavigationController = { 62 | return UINavigationController(rootViewController: libraryViewController) 63 | }() 64 | 65 | private lazy var libraryViewController: LibraryViewController = { 66 | let library: LibraryViewController = factory.make() 67 | library.libraryDelegate = delegate 68 | return library 69 | }() 70 | 71 | func preloadSamples() { 72 | library.preloadSamples() 73 | .receive(on: DispatchQueue.main) 74 | .sink(receiveCompletion: { completion in 75 | if case let .failure(error) = completion { 76 | self.delegate?.presentError(error, from: self.libraryViewController) 77 | } 78 | }) {} 79 | .store(in: &subscriptions) 80 | } 81 | 82 | func importPublication(from url: URL, sender: UIViewController) -> AnyPublisher { 83 | library.importPublication(from: url, sender: sender) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Sources/Library/PublicationCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PublicationCollectionViewCell.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Geoffrey Bugniot on 26/07/2018. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import UIKit 14 | 15 | protocol PublicationCollectionViewCellDelegate: AnyObject { 16 | var lastFlippedCell: PublicationCollectionViewCell? { get set } 17 | 18 | func displayInformation(forCellAt indexPath: IndexPath) 19 | func removePublicationFromLibrary(forCellAt indexPath: IndexPath) 20 | func cellFlipped(_ cell: PublicationCollectionViewCell) 21 | } 22 | 23 | class PublicationCollectionViewCell: UICollectionViewCell { 24 | 25 | @IBOutlet weak var coverImageView: UIImageView! 26 | @IBOutlet weak var titleLabel: UILabel! 27 | @IBOutlet weak var authorLabel: UILabel! 28 | 29 | weak var delegate: PublicationCollectionViewCellDelegate? 30 | 31 | var publicationMenuViewController = PublicationMenuViewController() 32 | var isMenuDisplayed = false 33 | 34 | var progress: Float = 0.0 { 35 | 36 | didSet { 37 | progressView.progress = progress 38 | let hidden = (progress == 0 || progress == 1) 39 | if hidden != progressView.isHidden { 40 | progressView.isHidden = hidden 41 | if hidden { 42 | self.backgroundColor = UIColor.clear 43 | } else { 44 | self.backgroundColor = UIColor.lightGray 45 | } 46 | } 47 | } 48 | 49 | } 50 | 51 | private lazy var progressView: UIProgressView = { 52 | 53 | let pView = UIProgressView(progressViewStyle: .bar) 54 | pView.translatesAutoresizingMaskIntoConstraints = false 55 | self.addSubview(pView) 56 | 57 | let leftConstraint = NSLayoutConstraint(item: pView, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1.0, constant: 0.0) 58 | let rightConstraint = NSLayoutConstraint(item: pView, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1.0, constant: 0.0) 59 | let verticalConstraint = NSLayoutConstraint(item: pView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: 0.0) 60 | 61 | self.addConstraints([leftConstraint, rightConstraint, verticalConstraint]) 62 | 63 | return pView 64 | 65 | }() 66 | 67 | override func awakeFromNib() { 68 | 69 | super.awakeFromNib() 70 | 71 | publicationMenuViewController.delegate = self 72 | publicationMenuViewController.view.isHidden = !isMenuDisplayed 73 | contentView.addSubview(publicationMenuViewController.view) 74 | 75 | } 76 | 77 | override func layoutSubviews() { 78 | super.layoutSubviews() 79 | publicationMenuViewController.view.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height) 80 | } 81 | 82 | } 83 | 84 | extension PublicationCollectionViewCell { 85 | 86 | /// Flip the PublicationCollectionViewCell and display a user menu. 87 | func flipMenu() { 88 | 89 | var transitionOptions: UIView.AnimationOptions! 90 | 91 | if isMenuDisplayed { 92 | transitionOptions = UIView.AnimationOptions.transitionFlipFromLeft 93 | delegate?.lastFlippedCell = nil 94 | self.isAccessibilityElement = true 95 | } else { 96 | self.isAccessibilityElement = false 97 | transitionOptions = UIView.AnimationOptions.transitionFlipFromRight 98 | if delegate?.lastFlippedCell != self { 99 | delegate?.cellFlipped(self) 100 | } 101 | } 102 | 103 | // Reverse the UI. Display the menu and hide the cover or vice versa 104 | UIView.transition(with: contentView, duration: 0.5, options: transitionOptions, animations: { 105 | // coverImageView.superview is the stack view embedding the cover image, 106 | // the title label and the author label. 107 | self.coverImageView.superview!.isHidden = !self.isMenuDisplayed 108 | self.publicationMenuViewController.view.isHidden = self.isMenuDisplayed 109 | }, completion: { _ in 110 | self.isMenuDisplayed = !self.isMenuDisplayed 111 | }) 112 | 113 | } 114 | 115 | } 116 | 117 | extension PublicationCollectionViewCell: PublicationMenuViewControllerDelegate { 118 | 119 | func infosButtonTapped() { 120 | guard let indexPath = (superview as? UICollectionView)?.indexPath(for: self) else { 121 | return 122 | } 123 | flipMenu() 124 | delegate?.displayInformation(forCellAt: indexPath) 125 | } 126 | 127 | func removeButtonTapped() { 128 | guard let indexPath = (superview as? UICollectionView)?.indexPath(for: self) else { 129 | return 130 | } 131 | flipMenu() 132 | delegate?.removePublicationFromLibrary(forCellAt: indexPath) 133 | } 134 | 135 | func cancelButtonTapped() { 136 | flipMenu() 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /Sources/Library/PublicationMenuViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PublicationMenuViewController.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Geoffrey Bugniot on 30/07/2018. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | // 13 | 14 | import UIKit 15 | 16 | protocol PublicationMenuViewControllerDelegate: AnyObject { 17 | func infosButtonTapped() 18 | func removeButtonTapped() 19 | func cancelButtonTapped() 20 | } 21 | 22 | class PublicationMenuViewController: UIViewController { 23 | 24 | weak var delegate: PublicationMenuViewControllerDelegate? 25 | 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | } 29 | 30 | @IBAction func infosButtonTapped(_ sender: Any) { 31 | delegate?.infosButtonTapped() 32 | } 33 | 34 | @IBAction func removeButtonTapped(_ sender: Any) { 35 | delegate?.removeButtonTapped() 36 | } 37 | 38 | @IBAction func cancelButtonTapped(_ sender: Any) { 39 | delegate?.cancelButtonTapped() 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Library/en.lproj/PublicationMenuViewController.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UIButton"; accessibilityLabel = "Delete Publication"; ObjectID = "XNx-HX-fJ0"; */ 3 | "XNx-HX-fJ0.accessibilityLabel" = "Delete Publication"; 4 | 5 | /* Class = "UIButton"; normalTitle = "Remove"; ObjectID = "XNx-HX-fJ0"; */ 6 | "XNx-HX-fJ0.normalTitle" = "Remove"; 7 | 8 | /* Class = "UIButton"; accessibilityLabel = "Publication Information"; ObjectID = "gmr-BT-CvG"; */ 9 | "gmr-BT-CvG.accessibilityLabel" = "Publication Information"; 10 | 11 | /* Class = "UIButton"; normalTitle = "Infos"; ObjectID = "gmr-BT-CvG"; */ 12 | "gmr-BT-CvG.normalTitle" = "Infos"; 13 | 14 | /* Class = "UIButton"; normalTitle = "Cancel"; ObjectID = "kn0-yM-9Ms"; */ 15 | "kn0-yM-9Ms.normalTitle" = "Cancel"; 16 | -------------------------------------------------------------------------------- /Sources/OPDS/OPDSFacetViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OPDSFacetViewController.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Nikita Aizikovskyi on Mar-05-2018. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import UIKit 14 | import R2Shared 15 | 16 | 17 | protocol OPDSFacetViewControllerFactory { 18 | func make(feed: Feed) -> OPDSFacetViewController 19 | } 20 | 21 | protocol OPDSFacetViewControllerDelegate: AnyObject { 22 | func opdsFacetViewController(_ opdsFacetViewController: OPDSFacetViewController, presentOPDSFeedAt href: String) 23 | } 24 | 25 | final class OPDSFacetViewController : UIViewController { 26 | var feed: Feed! 27 | weak var delegate: OPDSFacetViewControllerDelegate? 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | } 32 | 33 | @IBAction func dismissView(_ sender: Any) { 34 | dismiss(animated: true, completion: nil) 35 | } 36 | } 37 | 38 | // MARK: - Table view datasource 39 | 40 | extension OPDSFacetViewController: UITableViewDataSource { 41 | 42 | func numberOfSections(in tableView: UITableView) -> Int { 43 | return feed.facets.count 44 | } 45 | 46 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 47 | return feed.facets[section].links.count 48 | } 49 | 50 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 51 | let cell = tableView.dequeueReusableCell(withIdentifier: "opdsFacetTableViewCell", for: indexPath) 52 | 53 | cell.textLabel?.text = feed.facets[indexPath.section].links[indexPath.row].title 54 | if let count = feed.facets[indexPath.section].links[indexPath.row].properties.numberOfItems { 55 | cell.detailTextLabel?.text = "\(count)" 56 | } else { 57 | cell.detailTextLabel?.text = "" 58 | } 59 | 60 | cell.textLabel?.font = UIFont.systemFont(ofSize: 14) 61 | cell.textLabel?.lineBreakMode = .byTruncatingTail 62 | cell.detailTextLabel?.font = UIFont.systemFont(ofSize: 12) 63 | 64 | return cell 65 | } 66 | 67 | } 68 | 69 | // MARK: - Table view delegate 70 | 71 | extension OPDSFacetViewController: UITableViewDelegate { 72 | 73 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 74 | let href = feed.facets[indexPath.section].links[indexPath.row].href 75 | delegate?.opdsFacetViewController(self, presentOPDSFeedAt: href) 76 | dismiss(animated: true, completion: nil) 77 | } 78 | 79 | func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 80 | return feed.facets[section].metadata.title 81 | } 82 | 83 | func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) { 84 | let header = view as! UITableViewHeaderFooterView 85 | header.textLabel?.font = UIFont.boldSystemFont(ofSize: 15) 86 | } 87 | 88 | } 89 | 90 | -------------------------------------------------------------------------------- /Sources/OPDS/OPDSFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OPDSFactory.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 22.02.19. 6 | // 7 | // Copyright 2019 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Foundation 14 | import UIKit 15 | import R2Shared 16 | 17 | final class OPDSFactory { 18 | 19 | /// To simplify the refactoring of dependencies, the factory is a singleton for now. 20 | static let shared = OPDSFactory() 21 | 22 | weak var delegate: OPDSModuleDelegate? 23 | fileprivate let storyboard = UIStoryboard(name: "OPDS", bundle: nil) 24 | 25 | } 26 | 27 | 28 | extension OPDSFactory: OPDSCatalogSelectorViewControllerFactory { 29 | func make() -> OPDSCatalogSelectorViewController { 30 | let controller = storyboard.instantiateViewController(withIdentifier: "OPDSCatalogSelectorViewController") as! OPDSCatalogSelectorViewController 31 | return controller 32 | } 33 | } 34 | 35 | 36 | extension OPDSFactory: OPDSRootTableViewControllerFactory { 37 | func make(feedURL: URL, indexPath: IndexPath?) -> OPDSRootTableViewController { 38 | let controller = storyboard.instantiateViewController(withIdentifier: "OPDSRootTableViewController") as! OPDSRootTableViewController 39 | controller.factory = self 40 | controller.originalFeedURL = feedURL 41 | controller.originalFeedIndexPath = nil 42 | return controller 43 | } 44 | } 45 | 46 | 47 | extension OPDSFactory: OPDSPublicationInfoViewControllerFactory { 48 | func make(publication: Publication) -> OPDSPublicationInfoViewController { 49 | let controller = storyboard.instantiateViewController(withIdentifier: "OPDSPublicationInfoViewController") as! OPDSPublicationInfoViewController 50 | controller.publication = publication 51 | controller.moduleDelegate = delegate 52 | return controller 53 | } 54 | } 55 | 56 | 57 | extension OPDSFactory: OPDSFacetViewControllerFactory { 58 | func make(feed: Feed) -> OPDSFacetViewController { 59 | let controller = storyboard.instantiateViewController(withIdentifier: "OPDSFacetViewController") as! OPDSFacetViewController 60 | controller.feed = feed 61 | return controller 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/OPDS/OPDSGroupCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OPDSGroupCollectionViewCell.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Geoffrey Bugniot on 24/04/2018. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import UIKit 14 | 15 | class OPDSGroupCollectionViewCell: UICollectionViewCell { 16 | 17 | @IBOutlet weak var navigationTitleLabel: UILabel! 18 | @IBOutlet weak var navigationCountLabel: UILabel! 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Sources/OPDS/OPDSModule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OPDSModule.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 22.02.19. 6 | // 7 | // Copyright 2019 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Combine 14 | import Foundation 15 | import UIKit 16 | import R2Shared 17 | 18 | 19 | /// The OPDS module handles the presentation of OPDS catalogs. 20 | protocol OPDSModuleAPI { 21 | 22 | var delegate: OPDSModuleDelegate? { get } 23 | 24 | /// Root navigation controller containing the OPDS catalogs. 25 | /// Can be used to present the OPDS catalogs to the user. 26 | var rootViewController: UINavigationController { get } 27 | 28 | } 29 | 30 | protocol OPDSModuleDelegate: ModuleDelegate { 31 | 32 | /// Called when an OPDS publication needs to be downloaded. 33 | func opdsDownloadPublication(_ publication: Publication?, at link: Link, sender: UIViewController) -> AnyPublisher 34 | 35 | } 36 | 37 | 38 | final class OPDSModule: OPDSModuleAPI { 39 | 40 | weak var delegate: OPDSModuleDelegate? 41 | 42 | private let factory = OPDSFactory.shared 43 | 44 | init(delegate: OPDSModuleDelegate?) { 45 | self.delegate = delegate 46 | factory.delegate = delegate 47 | } 48 | 49 | private(set) lazy var rootViewController: UINavigationController = { 50 | let catalogViewController: OPDSCatalogSelectorViewController = factory.make() 51 | return UINavigationController(rootViewController: catalogViewController) 52 | }() 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Sources/OPDS/OPDSNavigationTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OPDSNavigationTableViewCell.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Geoffrey Bugniot on 23/04/2018. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import UIKit 14 | 15 | class OPDSNavigationTableViewCell: UITableViewCell { 16 | 17 | @IBOutlet weak var title: UILabel! 18 | @IBOutlet weak var count: UILabel! 19 | 20 | override func awakeFromNib() { 21 | super.awakeFromNib() 22 | // Initialization code 23 | } 24 | 25 | override func setSelected(_ selected: Bool, animated: Bool) { 26 | super.setSelected(selected, animated: animated) 27 | 28 | // Configure the view for the selected state 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Sources/OPDS/OPDSPlaceholderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OPDSPlaceholderView.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Geoffrey Bugniot on 09/05/2018. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import UIKit 14 | import Kingfisher 15 | 16 | // MARK: Placeholder base class 17 | 18 | class OPDSPlaceholderView: UITextView { 19 | 20 | convenience init(frame: CGRect, title: String?, author: String?) { 21 | self.init(frame: frame) 22 | 23 | if let title = title { 24 | text = title + ("\n_________\n") 25 | } 26 | 27 | if let author = author { 28 | text = text + author 29 | } 30 | 31 | text = text + "" 32 | 33 | layer.borderWidth = 5.0 34 | layer.borderColor = #colorLiteral(red: 0.08269290555, green: 0.2627741129, blue: 0.3623990017, alpha: 1).cgColor 35 | backgroundColor = #colorLiteral(red: 0.05882352963, green: 0.180392161, blue: 0.2470588237, alpha: 1) 36 | textColor = #colorLiteral(red: 0.8639426257, green: 0.8639426257, blue: 0.8639426257, alpha: 1) 37 | font = UIFont.systemFont(ofSize: 9) 38 | } 39 | 40 | } 41 | 42 | // MARK: - Placeholder subclass used in a publication screen 43 | 44 | class OPDSPlaceholderPublicationView : OPDSPlaceholderView, Placeholder { 45 | 46 | } 47 | 48 | // MARK: - Placeholder subclass used in a list of publications screen 49 | 50 | class OPDSPlaceholderListView : OPDSPlaceholderView, Placeholder { 51 | 52 | } 53 | 54 | // MARK: - Placeholder protocol specific to publication screen 55 | 56 | extension OPDSPlaceholderPublicationView { 57 | 58 | public func add(to imageView: KFCrossPlatformImageView) { 59 | imageView.addSubview(self) 60 | 61 | self.translatesAutoresizingMaskIntoConstraints = false 62 | NSLayoutConstraint.activate([ 63 | NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: imageView, attribute: .centerX, multiplier: 1, constant: 0), 64 | NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: imageView, attribute: .centerY, multiplier: 1, constant: 0), 65 | NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: imageView, attribute: .height, multiplier: 0.9, constant: 0), 66 | NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: imageView, attribute: .width, multiplier: 0.5, constant: 0) 67 | ]) 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /Sources/OPDS/OPDSPublicationInfoViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OPDSPublicationInfoViewController.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Nikita Aizikovskyi on Mar-27-2018. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Combine 14 | import UIKit 15 | import R2Shared 16 | import Kingfisher 17 | 18 | protocol OPDSPublicationInfoViewControllerFactory { 19 | func make(publication: Publication) -> OPDSPublicationInfoViewController 20 | } 21 | 22 | class OPDSPublicationInfoViewController: UIViewController, Loggable { 23 | 24 | weak var moduleDelegate: OPDSModuleDelegate? 25 | 26 | var publication: Publication? 27 | 28 | @IBOutlet weak var imageView: UIImageView! 29 | @IBOutlet weak var fxImageView: UIImageView! 30 | @IBOutlet weak var titleLabel: UILabel! 31 | @IBOutlet weak var authorLabel: UILabel! 32 | @IBOutlet weak var descriptionLabel: UILabel! 33 | @IBOutlet weak var downloadButton: UIButton! 34 | @IBOutlet weak var downloadActivityIndicator: UIActivityIndicatorView! 35 | 36 | private lazy var downloadLink: Link? = publication?.downloadLinks.first 37 | private var subscriptions = Set() 38 | 39 | override func viewDidLoad() { 40 | fxImageView.clipsToBounds = true 41 | fxImageView!.contentMode = .scaleAspectFill 42 | imageView!.contentMode = .scaleAspectFit 43 | 44 | let titleTextView = OPDSPlaceholderPublicationView( 45 | frame: imageView.frame, 46 | title: publication?.metadata.title, 47 | author: publication?.metadata.authors 48 | .map { $0.name } 49 | .joined(separator: ", ") 50 | ) 51 | 52 | if let images = publication?.images { 53 | if images.count > 0 { 54 | let coverURL = URL(string: images[0].href) 55 | if (coverURL != nil) { 56 | imageView.kf.setImage( 57 | with: coverURL, 58 | placeholder: titleTextView, 59 | options: [.transition(ImageTransition.fade(0.5))], 60 | progressBlock: nil 61 | ) { result in 62 | switch result { 63 | case .success(let image): 64 | self.fxImageView?.image = image.image 65 | UIView.transition( 66 | with: self.fxImageView, 67 | duration: 0.3, 68 | options: .transitionCrossDissolve, 69 | animations: { self.fxImageView?.image = image.image }, 70 | completion: nil 71 | ) 72 | case .failure(_): 73 | break 74 | } 75 | } 76 | } 77 | } 78 | } 79 | 80 | titleLabel.text = publication?.metadata.title 81 | authorLabel.text = publication?.metadata.authors 82 | .map { $0.name } 83 | .joined(separator: ", ") 84 | descriptionLabel.text = publication?.metadata.description 85 | descriptionLabel.sizeToFit() 86 | 87 | downloadActivityIndicator.stopAnimating() 88 | 89 | // If we are not able to get a free link, we hide the download button 90 | // TODO: handle payment or redirection for others links? 91 | if downloadLink == nil { 92 | downloadButton.isHidden = true 93 | } 94 | } 95 | 96 | @IBAction func downloadBook(_ sender: UIButton) { 97 | guard let delegate = moduleDelegate, let downloadLink = downloadLink else { 98 | return 99 | } 100 | 101 | downloadActivityIndicator.startAnimating() 102 | downloadButton.isEnabled = false 103 | delegate.opdsDownloadPublication(publication, at: downloadLink, sender: self) 104 | .receive(on: DispatchQueue.main) 105 | .sink { completion in 106 | self.downloadActivityIndicator.stopAnimating() 107 | self.downloadButton.isEnabled = true 108 | 109 | if case .failure(let error) = completion { 110 | delegate.presentError(error, from: self) 111 | } 112 | } receiveValue: { book in 113 | delegate.presentAlert( 114 | NSLocalizedString("success_title", comment: "Title of the alert when a publication is successfully downloaded"), 115 | message: String(format: NSLocalizedString("library_download_success_message", comment: "Message of the alert when a publication is successfully downloaded"), book.title), 116 | from: self 117 | ) 118 | } 119 | .store(in: &subscriptions) 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /Sources/OPDS/OPDSPublicationTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OPDSPublicationTableViewCell.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Geoffrey Bugniot on 23/04/2018. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import UIKit 14 | import R2Shared 15 | import Kingfisher 16 | 17 | class OPDSPublicationTableViewCell: UITableViewCell { 18 | 19 | @IBOutlet weak var collectionView: UICollectionView! 20 | 21 | var feed: Feed? 22 | weak var opdsRootTableViewController: OPDSRootTableViewController? 23 | 24 | static let iPadLayoutNumberPerRow:[ScreenOrientation: Int] = [.portrait: 4, .landscape: 5] 25 | static let iPhoneLayoutNumberPerRow:[ScreenOrientation: Int] = [.portrait: 3, .landscape: 4] 26 | 27 | lazy var layoutNumberPerRow:[UIUserInterfaceIdiom:[ScreenOrientation: Int]] = [ 28 | .pad : OPDSPublicationTableViewCell.iPadLayoutNumberPerRow, 29 | .phone : OPDSPublicationTableViewCell.iPhoneLayoutNumberPerRow 30 | ] 31 | 32 | override func awakeFromNib() { 33 | super.awakeFromNib() 34 | collectionView.register(UINib(nibName: "PublicationCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "publicationCollectionViewCell") 35 | 36 | } 37 | 38 | override func setSelected(_ selected: Bool, animated: Bool) { 39 | super.setSelected(selected, animated: animated) 40 | 41 | // Configure the view for the selected state 42 | } 43 | 44 | override func layoutSubviews() { 45 | super.layoutSubviews() 46 | collectionView.collectionViewLayout.invalidateLayout() 47 | } 48 | 49 | } 50 | 51 | extension OPDSPublicationTableViewCell: UICollectionViewDataSource { 52 | 53 | // MARK: - Collection view data source 54 | 55 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 56 | return feed?.publications.count ?? 0 57 | } 58 | 59 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 60 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "publicationCollectionViewCell", 61 | for: indexPath) as! PublicationCollectionViewCell 62 | 63 | cell.isAccessibilityElement = true 64 | cell.accessibilityHint = NSLocalizedString("opds_show_detail_view_a11y_hint", comment: "Accessibility hint for OPDS publication cell") 65 | 66 | if let publications = feed?.publications, let publication = feed?.publications[indexPath.row] { 67 | 68 | cell.accessibilityLabel = publication.metadata.title 69 | 70 | let titleTextView = OPDSPlaceholderListView( 71 | frame: cell.frame, 72 | title: publication.metadata.title, 73 | author: publication.metadata.authors 74 | .map { $0.name } 75 | .joined(separator: ", ") 76 | ) 77 | 78 | let coverURL: URL? = publication.link(withRel: .cover)?.url(relativeTo: publication.baseURL) 79 | ?? publication.images.first.flatMap { URL(string: $0.href) } 80 | 81 | if let coverURL = coverURL { 82 | cell.coverImageView.kf.setImage( 83 | with: coverURL, 84 | placeholder: titleTextView, 85 | options: [.transition(ImageTransition.fade(0.5))], 86 | progressBlock: nil) { _ in } 87 | } else { 88 | cell.coverImageView.addSubview(titleTextView) 89 | } 90 | 91 | cell.titleLabel.text = publication.metadata.title 92 | cell.authorLabel.text = publication.metadata.authors 93 | .map { $0.name } 94 | .joined(separator: ", ") 95 | 96 | if indexPath.row == publications.count - 3 { 97 | opdsRootTableViewController?.loadNextPage(completionHandler: { (feed) in 98 | self.feed = feed 99 | collectionView.reloadData() 100 | }) 101 | } 102 | 103 | } 104 | 105 | return cell 106 | } 107 | 108 | } 109 | 110 | extension OPDSPublicationTableViewCell: UICollectionViewDelegateFlowLayout { 111 | 112 | // MARK: - Collection view delegate 113 | 114 | func collectionView(_ collectionView: UICollectionView, 115 | layout collectionViewLayout: UICollectionViewLayout, 116 | sizeForItemAt indexPath: IndexPath) -> CGSize { 117 | 118 | let idiom = { () -> UIUserInterfaceIdiom in 119 | let tempIdion = UIDevice.current.userInterfaceIdiom 120 | return (tempIdion != .pad) ? .phone:.pad // ignnore carplay and others 121 | } () 122 | 123 | guard let deviceLayoutNumberPerRow = layoutNumberPerRow[idiom] else {return CGSize(width: 0, height: 0)} 124 | guard let numberPerRow = deviceLayoutNumberPerRow[.current] else {return CGSize(width: 0, height: 0)} 125 | 126 | let minimumSpacing: CGFloat = 5.0 127 | let labelHeight: CGFloat = 50.0 128 | let coverRatio: CGFloat = 1.5 129 | 130 | let itemWidth = (collectionView.frame.width / CGFloat(numberPerRow)) - (CGFloat(minimumSpacing) * CGFloat(numberPerRow)) - minimumSpacing 131 | let itemHeight = (itemWidth * coverRatio) + labelHeight 132 | 133 | return CGSize(width: itemWidth, height: itemHeight) 134 | 135 | } 136 | 137 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 138 | if let publication = feed?.publications[indexPath.row] { 139 | let opdsPublicationInfoViewController: OPDSPublicationInfoViewController = OPDSFactory.shared.make(publication: publication) 140 | opdsRootTableViewController?.navigationController?.pushViewController(opdsPublicationInfoViewController, animated: true) 141 | } 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /Sources/Reader/CBZ/CBZModule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CBZModule.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 22.02.19. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Foundation 14 | import UIKit 15 | import R2Shared 16 | 17 | 18 | final class CBZModule: ReaderFormatModule { 19 | 20 | weak var delegate: ReaderFormatModuleDelegate? 21 | 22 | init(delegate: ReaderFormatModuleDelegate?) { 23 | self.delegate = delegate 24 | } 25 | 26 | var publicationFormats: [Publication.Format] { 27 | return [.cbz] 28 | } 29 | 30 | func makeReaderViewController(for publication: Publication, locator: Locator?, bookId: Book.Id, books: BookRepository, bookmarks: BookmarkRepository, resourcesServer: ResourcesServer) throws -> UIViewController { 31 | let cbzVC = CBZViewController(publication: publication, locator: locator, bookId: bookId, books: books, bookmarks: bookmarks) 32 | cbzVC.moduleDelegate = self.delegate 33 | return cbzVC 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Sources/Reader/CBZ/CBZViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CBZViewController.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Alexandre Camilleri on 6/28/17. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import UIKit 14 | import R2Navigator 15 | import R2Shared 16 | import R2Streamer 17 | 18 | 19 | class CBZViewController: ReaderViewController { 20 | 21 | init(publication: Publication, locator: Locator?, bookId: Book.Id, books: BookRepository, bookmarks: BookmarkRepository) { 22 | let navigator = CBZNavigatorViewController(publication: publication, initialLocation: locator) 23 | 24 | super.init(navigator: navigator, publication: publication, bookId: bookId, books: books, bookmarks: bookmarks) 25 | 26 | navigator.delegate = self 27 | } 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | 32 | view.backgroundColor = .black 33 | } 34 | 35 | override var currentBookmark: Bookmark? { 36 | guard let locator = navigator.currentLocation else { 37 | return nil 38 | } 39 | 40 | return Bookmark(bookId: bookId, locator: locator) 41 | } 42 | 43 | } 44 | 45 | extension CBZViewController: CBZNavigatorDelegate { 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Reader/Common/Bookmark/BookmarkCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BookmarkCell.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Senda Li on 2018/7/19. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Foundation 14 | import UIKit 15 | 16 | class BookmarkCell: UITableViewCell { 17 | 18 | lazy var dateFormatter: DateFormatter = { 19 | let formatter = DateFormatter() 20 | formatter.dateStyle = .medium 21 | return formatter 22 | }() 23 | 24 | lazy var timeLabel: UILabel = { 25 | let label = UILabel() 26 | self.contentView.addSubview(label) 27 | label.translatesAutoresizingMaskIntoConstraints = false 28 | 29 | if let detailLabel = self.detailTextLabel { 30 | label.textColor = detailLabel.textColor 31 | label.font = detailLabel.font 32 | 33 | NSLayoutConstraint.activate([ 34 | label.bottomAnchor.constraint(equalTo: detailLabel.bottomAnchor), 35 | label.heightAnchor.constraint(equalTo: detailLabel.heightAnchor), 36 | label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16) 37 | ]) 38 | } 39 | 40 | return label 41 | } () 42 | 43 | var formattedDate: Date? { 44 | didSet { 45 | self.timeLabel.text = { 46 | if let newDate = formattedDate{ 47 | return dateFormatter.string(from: newDate) 48 | } else { 49 | return "" 50 | } 51 | } () 52 | self.timeLabel.sizeToFit() 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/Reader/Common/DRM/DRMManagementTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DRMManagementTableViewController.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Alexandre Camilleri on 11/27/17. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import UIKit 14 | import R2Shared 15 | import R2Navigator 16 | 17 | protocol DRMManagementTableViewControllerFactory { 18 | func make(publication: Publication, delegate: ReaderModuleDelegate?) -> DRMManagementTableViewController 19 | } 20 | 21 | class DRMManagementTableViewController: UITableViewController { 22 | @IBOutlet weak var stateLabel: UILabel! 23 | @IBOutlet weak var typeLabel: UILabel! 24 | @IBOutlet weak var providerLabel: UILabel! 25 | @IBOutlet weak var issuedLabel: UILabel! 26 | @IBOutlet weak var updatedLabel: UILabel! 27 | 28 | @IBOutlet weak var startLabel: UILabel! 29 | @IBOutlet weak var endLabel: UILabel! 30 | @IBOutlet weak var printsLeftLabel: UILabel! 31 | @IBOutlet weak var copiesLeftLabel: UILabel! 32 | 33 | @IBOutlet weak var renewButton: UIButton! 34 | @IBOutlet weak var returnButton: UIButton! 35 | 36 | public var viewModel: DRMViewModel! 37 | public var appearance: UserProperty? 38 | 39 | weak var moduleDelegate: ReaderModuleDelegate? 40 | 41 | override func viewWillAppear(_ animated: Bool) { 42 | title = NSLocalizedString("reader_drm_management_title", comment: "Title of the DRM management view") 43 | reload() 44 | self.navigationController?.navigationBar.barTintColor = UIColor.white 45 | self.navigationController?.navigationBar.tintColor = UIColor.black 46 | self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.black] 47 | } 48 | 49 | override func viewWillDisappear(_ animated: Bool) { 50 | if let appearance = appearance{ 51 | setUIColor(for: appearance) 52 | } 53 | super.viewWillDisappear(animated) 54 | } 55 | 56 | internal func setUIColor(for appearance: UserProperty) { 57 | let colors = AssociatedColors.getColors(for: appearance) 58 | 59 | navigationController?.navigationBar.barTintColor = colors.mainColor 60 | navigationController?.navigationBar.tintColor = colors.textColor 61 | navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: colors.textColor] 62 | } 63 | 64 | @IBAction func renewTapped() { 65 | let alert = UIAlertController( 66 | title: NSLocalizedString("reader_drm_renew_title", comment: "Title of the renew confirmation alert"), 67 | message: NSLocalizedString("reader_drm_renew_message", comment: "Message of the renew confirmation alert"), 68 | preferredStyle: .alert 69 | ) 70 | let confirmButton = UIAlertAction(title: NSLocalizedString("confirm_button", comment: "Confirmation button to renew a publication"), style: .default, handler: { (_) in 71 | self.viewModel.renewLoan { error in 72 | if let error = error { 73 | self.moduleDelegate?.presentError(error, from: self) 74 | } else { 75 | self.reload() 76 | self.moduleDelegate?.presentAlert( 77 | NSLocalizedString("success_title", comment: "Title for the success message after renewing a publication"), 78 | message: NSLocalizedString("reader_drm_renew_success_message", comment: "Success message after renewing a publication"), 79 | from: self 80 | ) 81 | } 82 | } 83 | }) 84 | let dismissButton = UIAlertAction(title: NSLocalizedString("cancel_button", comment: "Cancel renewing the publication"), style: .cancel) 85 | 86 | alert.addAction(dismissButton) 87 | alert.addAction(confirmButton) 88 | // Present alert. 89 | present(alert, animated: true) 90 | } 91 | 92 | @IBAction func returnTapped() { 93 | let alert = UIAlertController( 94 | title: NSLocalizedString("reader_drm_return_title", comment: "Title of the return confirmation alert"), 95 | message: NSLocalizedString("reader_drm_return_message", comment: "Message of the return confirmation alert"), 96 | preferredStyle: .alert 97 | ) 98 | let confirmButton = UIAlertAction(title: NSLocalizedString("confirm_button", comment: "Confirmation button to return a publication"), style: .destructive, handler: { (_) in 99 | self.viewModel.returnPublication() { error in 100 | if let error = error { 101 | self.moduleDelegate?.presentError(error, from: self) 102 | } else { 103 | self.navigationController?.popToRootViewController(animated: true) 104 | self.moduleDelegate?.presentAlert( 105 | NSLocalizedString("success_title", comment: "Title for the success message after returning a publication"), 106 | message: NSLocalizedString("reader_drm_return_success_message", comment: "Success message after returning a publication"), 107 | from: self 108 | ) 109 | } 110 | } 111 | }) 112 | let dismissButton = UIAlertAction(title: NSLocalizedString("cancel_button", comment: "Cancel returning the publication"), style: .cancel) 113 | 114 | alert.addAction(dismissButton) 115 | alert.addAction(confirmButton) 116 | // Present alert. 117 | present(alert, animated: true) 118 | 119 | } 120 | 121 | internal func reload() { 122 | typeLabel.text = viewModel.name 123 | stateLabel.text = viewModel.state 124 | providerLabel.text = viewModel.provider 125 | issuedLabel.text = viewModel.issued?.description 126 | updatedLabel.text = viewModel.updated?.description 127 | startLabel.text = viewModel.start?.description ?? "-" 128 | endLabel.text = viewModel.end?.description ?? "-" 129 | printsLeftLabel.text = viewModel.printsLeft 130 | copiesLeftLabel.text = viewModel.copiesLeft 131 | renewButton.isEnabled = viewModel.canRenewLoan 132 | returnButton.isEnabled = viewModel.canReturnPublication 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /Sources/Reader/Common/DRM/DRMViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DRMViewModel.swift 3 | // r2-testapp-swift (carthage) 4 | // 5 | // Created by Mickaël Menu on 19.02.19. 6 | // 7 | // Copyright 2019 Readium Foundation. All rights reserved. 8 | // Use of this source code is governed by a BSD-style license which is detailed 9 | // in the LICENSE file present in the project repository where this source code is maintained. 10 | // 11 | 12 | import Foundation 13 | import UIKit 14 | import R2Shared 15 | 16 | /// Used to display a DRM license's informations 17 | /// Should be subclassed for specific DRM. 18 | class DRMViewModel: NSObject { 19 | 20 | /// Class cluster factory. 21 | /// Use this instead of regular constructors to create the right DRM view model. 22 | static func make(publication: Publication, presentingViewController: UIViewController) -> DRMViewModel { 23 | #if LCP 24 | if let license = publication.lcpLicense { 25 | return LCPViewModel(publication: publication, license: license, presentingViewController: presentingViewController) 26 | } 27 | #endif 28 | 29 | return DRMViewModel(publication: publication, presentingViewController: presentingViewController) 30 | } 31 | 32 | let publication: Publication 33 | 34 | /// Host view controller to be used to present any dialog. 35 | weak var presentingViewController: UIViewController? 36 | 37 | init(publication: Publication, presentingViewController: UIViewController) { 38 | assert(publication.isProtected) 39 | 40 | self.publication = publication 41 | self.presentingViewController = presentingViewController 42 | } 43 | 44 | var name: String? { 45 | return publication.protectionName 46 | } 47 | 48 | var state: String? { 49 | return nil 50 | } 51 | 52 | var provider: String? { 53 | return nil 54 | } 55 | 56 | var issued: Date? { 57 | return nil 58 | } 59 | 60 | var updated: Date? { 61 | return nil 62 | } 63 | 64 | var start: Date? { 65 | return nil 66 | } 67 | 68 | var end: Date? { 69 | return nil 70 | } 71 | 72 | var copiesLeft: String { 73 | return NSLocalizedString("reader_drm_unlimited_label", comment: "Unlimited quantity for a given DRM consumable right") 74 | } 75 | 76 | var printsLeft: String { 77 | return NSLocalizedString("reader_drm_unlimited_label", comment: "Unlimited quantity for a given DRM consumable right") 78 | } 79 | 80 | var canRenewLoan: Bool { 81 | return false 82 | } 83 | 84 | func renewLoan(completion: @escaping (Error?) -> Void) { 85 | completion(nil) 86 | } 87 | 88 | var canReturnPublication: Bool { 89 | return false 90 | } 91 | 92 | func returnPublication(completion: @escaping (Error?) -> Void) { 93 | completion(nil) 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /Sources/Reader/Common/DRM/LCPViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2020 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 | #if LCP 8 | 9 | import Foundation 10 | import UIKit 11 | import R2Shared 12 | import ReadiumLCP 13 | 14 | final class LCPViewModel: DRMViewModel { 15 | 16 | private let license: LCPLicense 17 | 18 | init(publication: Publication, license: LCPLicense, presentingViewController: UIViewController) { 19 | self.license = license 20 | super.init(publication: publication, presentingViewController: presentingViewController) 21 | } 22 | 23 | override var state: String? { 24 | return license.status?.status.rawValue 25 | } 26 | 27 | override var provider: String? { 28 | return license.license.provider 29 | } 30 | 31 | override var issued: Date? { 32 | return license.license.issued 33 | } 34 | 35 | override var updated: Date? { 36 | return license.license.updated 37 | } 38 | 39 | override var start: Date? { 40 | return license.license.rights.start 41 | } 42 | 43 | override var end: Date? { 44 | return license.license.rights.end 45 | } 46 | 47 | override var copiesLeft: String { 48 | guard let quantity = license.charactersToCopyLeft else { 49 | return super.copiesLeft 50 | } 51 | return String(format: NSLocalizedString("lcp_characters_label", comment: "Quantity of characters left to be copied"), quantity) 52 | } 53 | 54 | override var printsLeft: String { 55 | guard let quantity = license.pagesToPrintLeft else { 56 | return super.printsLeft 57 | } 58 | return String(format: NSLocalizedString("lcp_pages_label", comment: "Quantity of pages left to be printed"), quantity) 59 | } 60 | 61 | override var canRenewLoan: Bool { 62 | return license.canRenewLoan 63 | } 64 | 65 | override func renewLoan(completion: @escaping (Error?) -> Void) { 66 | guard let presentingViewController = self.presentingViewController else { 67 | completion(nil) 68 | return 69 | } 70 | 71 | license.renewLoan(with: LCPDefaultRenewDelegate(presentingViewController: presentingViewController)) { result in 72 | switch result { 73 | case .success, .cancelled: 74 | completion(nil) 75 | case .failure(let error): 76 | completion(error) 77 | } 78 | } 79 | } 80 | 81 | override var canReturnPublication: Bool { 82 | return license.canReturnPublication 83 | } 84 | 85 | override func returnPublication(completion: @escaping (Error?) -> Void) { 86 | license.returnPublication(completion: completion) 87 | } 88 | 89 | } 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /Sources/Reader/EPUB/AssociatedColors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AssociatedColors.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Geoffrey Bugniot on 03/07/2018. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import UIKit 14 | import R2Shared 15 | 16 | class AssociatedColors { 17 | 18 | /// Get associated colors for a specific appearance setting 19 | /// - parameter appearance: The selected appearance 20 | /// - Returns: A tuple with a main color and a text color 21 | static func getColors(for appearance: UserProperty) -> (mainColor: UIColor, textColor: UIColor) { 22 | var mainColor, textColor: UIColor 23 | 24 | switch appearance.toString() { 25 | case "readium-sepia-on": 26 | mainColor = UIColor.init(red: 250/255, green: 244/255, blue: 232/255, alpha: 1) 27 | textColor = UIColor.init(red: 18/255, green: 18/255, blue: 18/255, alpha: 1) 28 | case "readium-night-on": 29 | mainColor = UIColor.black 30 | textColor = UIColor.init(red: 254/255, green: 254/255, blue: 254/255, alpha: 1) 31 | default: 32 | mainColor = UIColor.white 33 | textColor = UIColor.black 34 | } 35 | 36 | return (mainColor, textColor) 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Reader/EPUB/EPUBModule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EPUB.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 22.02.19. 6 | // 7 | // Copyright 2019 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Foundation 14 | import UIKit 15 | import R2Shared 16 | 17 | 18 | final class EPUBModule: ReaderFormatModule { 19 | 20 | weak var delegate: ReaderFormatModuleDelegate? 21 | 22 | init(delegate: ReaderFormatModuleDelegate?) { 23 | self.delegate = delegate 24 | } 25 | 26 | var publicationFormats: [Publication.Format] { 27 | return [.epub, .webpub] 28 | } 29 | 30 | func makeReaderViewController(for publication: Publication, locator: Locator?, bookId: Book.Id, books: BookRepository, bookmarks: BookmarkRepository, resourcesServer: ResourcesServer) throws -> UIViewController { 31 | guard publication.metadata.identifier != nil else { 32 | throw ReaderError.epubNotValid 33 | } 34 | 35 | let epubViewController = EPUBViewController(publication: publication, locator: locator, bookId: bookId, books: books, bookmarks: bookmarks, resourcesServer: resourcesServer) 36 | epubViewController.moduleDelegate = delegate 37 | return epubViewController 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Sources/Reader/EPUB/EPUBViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EPUBViewController.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Alexandre Camilleri on 7/3/17. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import UIKit 14 | import R2Shared 15 | import R2Navigator 16 | 17 | class EPUBViewController: ReaderViewController { 18 | 19 | var popoverUserconfigurationAnchor: UIBarButtonItem? 20 | var userSettingNavigationController: UserSettingsNavigationController 21 | 22 | init(publication: Publication, locator: Locator?, bookId: Book.Id, books: BookRepository, bookmarks: BookmarkRepository, resourcesServer: ResourcesServer) { 23 | let navigator = EPUBNavigatorViewController(publication: publication, initialLocation: locator, resourcesServer: resourcesServer) 24 | 25 | let settingsStoryboard = UIStoryboard(name: "UserSettings", bundle: nil) 26 | userSettingNavigationController = settingsStoryboard.instantiateViewController(withIdentifier: "UserSettingsNavigationController") as! UserSettingsNavigationController 27 | userSettingNavigationController.fontSelectionViewController = 28 | (settingsStoryboard.instantiateViewController(withIdentifier: "FontSelectionViewController") as! FontSelectionViewController) 29 | userSettingNavigationController.advancedSettingsViewController = 30 | (settingsStoryboard.instantiateViewController(withIdentifier: "AdvancedSettingsViewController") as! AdvancedSettingsViewController) 31 | 32 | super.init(navigator: navigator, publication: publication, bookId: bookId, books: books, bookmarks: bookmarks) 33 | 34 | navigator.delegate = self 35 | } 36 | 37 | var epubNavigator: EPUBNavigatorViewController { 38 | return navigator as! EPUBNavigatorViewController 39 | } 40 | 41 | override func viewDidLoad() { 42 | super.viewDidLoad() 43 | 44 | /// Set initial UI appearance. 45 | if let appearance = publication.userProperties.getProperty(reference: ReadiumCSSReference.appearance.rawValue) { 46 | setUIColor(for: appearance) 47 | } 48 | 49 | let userSettings = epubNavigator.userSettings 50 | userSettingNavigationController.userSettings = userSettings 51 | userSettingNavigationController.modalPresentationStyle = .popover 52 | userSettingNavigationController.usdelegate = self 53 | userSettingNavigationController.userSettingsTableViewController.publication = publication 54 | 55 | 56 | publication.userSettingsUIPresetUpdated = { [weak self] preset in 57 | guard let `self` = self, let presetScrollValue:Bool = preset?[.scroll] else { 58 | return 59 | } 60 | 61 | if let scroll = self.userSettingNavigationController.userSettings.userProperties.getProperty(reference: ReadiumCSSReference.scroll.rawValue) as? Switchable { 62 | if scroll.on != presetScrollValue { 63 | self.userSettingNavigationController.scrollModeDidChange() 64 | } 65 | } 66 | } 67 | } 68 | 69 | override func viewDidAppear(_ animated: Bool) { 70 | super.viewDidAppear(animated) 71 | } 72 | 73 | override open func viewWillDisappear(_ animated: Bool) { 74 | super.viewWillDisappear(animated) 75 | 76 | epubNavigator.userSettings.save() 77 | } 78 | 79 | override func makeNavigationBarButtons() -> [UIBarButtonItem] { 80 | var buttons = super.makeNavigationBarButtons() 81 | 82 | // User configuration button 83 | let userSettingsButton = UIBarButtonItem(image: #imageLiteral(resourceName: "settingsIcon"), style: .plain, target: self, action: #selector(presentUserSettings)) 84 | buttons.insert(userSettingsButton, at: 1) 85 | popoverUserconfigurationAnchor = userSettingsButton 86 | 87 | return buttons 88 | } 89 | 90 | override var currentBookmark: Bookmark? { 91 | guard let locator = navigator.currentLocation else { 92 | return nil 93 | } 94 | 95 | return Bookmark(bookId: bookId, locator: locator) 96 | } 97 | 98 | @objc func presentUserSettings() { 99 | let popoverPresentationController = userSettingNavigationController.popoverPresentationController! 100 | 101 | popoverPresentationController.delegate = self 102 | popoverPresentationController.barButtonItem = popoverUserconfigurationAnchor 103 | 104 | userSettingNavigationController.publication = publication 105 | present(userSettingNavigationController, animated: true) { 106 | // Makes sure that the popover is dismissed also when tapping on one of the other UIBarButtonItems. 107 | // ie. http://karmeye.com/2014/11/20/ios8-popovers-and-passthroughviews/ 108 | popoverPresentationController.passthroughViews = nil 109 | } 110 | } 111 | 112 | } 113 | 114 | extension EPUBViewController: EPUBNavigatorDelegate { 115 | 116 | } 117 | 118 | extension EPUBViewController: UIGestureRecognizerDelegate { 119 | 120 | func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { 121 | return true 122 | } 123 | 124 | } 125 | 126 | extension EPUBViewController: UserSettingsNavigationControllerDelegate { 127 | 128 | internal func getUserSettings() -> UserSettings { 129 | return epubNavigator.userSettings 130 | } 131 | 132 | internal func updateUserSettingsStyle() { 133 | DispatchQueue.main.async { 134 | self.epubNavigator.updateUserSettingStyle() 135 | } 136 | } 137 | 138 | /// Synchronyze the UI appearance to the UserSettings.Appearance. 139 | /// 140 | /// - Parameter appearance: The appearance. 141 | internal func setUIColor(for appearance: UserProperty) { 142 | let colors = AssociatedColors.getColors(for: appearance) 143 | 144 | navigator.view.backgroundColor = colors.mainColor 145 | view.backgroundColor = colors.mainColor 146 | // 147 | navigationController?.navigationBar.barTintColor = colors.mainColor 148 | navigationController?.navigationBar.tintColor = colors.textColor 149 | 150 | navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: colors.textColor] 151 | } 152 | 153 | } 154 | 155 | extension EPUBViewController: UIPopoverPresentationControllerDelegate { 156 | // Prevent the popOver to be presented fullscreen on iPhones. 157 | func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle 158 | { 159 | return .none 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /Sources/Reader/EPUB/User Settings/FontSelectionViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FontSelectionTableViewController.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Alexandre Camilleri on 9/20/17. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import UIKit 14 | import R2Navigator 15 | import R2Shared 16 | 17 | protocol FontSelectionDelegate: AnyObject { 18 | func currentFontIndex() -> Int 19 | func fontDidChange(to fontIndex: Int) 20 | } 21 | 22 | class FontSelectionViewController: UIViewController { 23 | @IBOutlet weak var fontTableView: UITableView! 24 | weak var delegate: FontSelectionDelegate? 25 | var userSettings: UserSettings? 26 | var initialTableViewIndex: IndexPath? 27 | 28 | override func viewDidLoad() { 29 | super.viewDidLoad() 30 | fontTableView.delegate = self 31 | fontTableView.dataSource = self 32 | self.navigationController?.isNavigationBarHidden = true 33 | fontTableView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil) 34 | } 35 | override func viewWillAppear(_ animated: Bool) { 36 | super.viewWillAppear(animated) 37 | navigationController?.setNavigationBarHidden(false, animated: animated) 38 | } 39 | 40 | override func viewDidAppear(_ animated: Bool) { 41 | super.viewDidAppear(animated) 42 | if let initialFontIndex = delegate?.currentFontIndex() { 43 | initialTableViewIndex = IndexPath.init(row: initialFontIndex, section: 0) 44 | } 45 | } 46 | 47 | override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 48 | // Update preferredContentSize only when fontTableView is populated 49 | self.preferredContentSize = CGSize(width: super.preferredContentSize.width, height: fontTableView.contentSize.height) 50 | } 51 | } 52 | 53 | extension FontSelectionViewController: UITableViewDataSource { 54 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 55 | if let fontFamily = userSettings?.userProperties.getProperty(reference: ReadiumCSSReference.fontFamily.rawValue) as? Enumerable { 56 | return fontFamily.values.count 57 | } else { 58 | return 0 59 | } 60 | } 61 | 62 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 63 | let cell = tableView.dequeueReusableCell(withIdentifier: "fontTableViewCell")! 64 | 65 | let fontFamily = userSettings?.userProperties.getProperty(reference: ReadiumCSSReference.fontFamily.rawValue) as? Enumerable 66 | cell.textLabel?.text = fontFamily?.values[indexPath.row] 67 | 68 | return cell 69 | } 70 | } 71 | 72 | extension FontSelectionViewController: UITableViewDelegate { 73 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 74 | delegate?.fontDidChange(to: indexPath.row) 75 | } 76 | 77 | func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? { 78 | if let oldIndex = tableView.indexPathForSelectedRow { 79 | tableView.cellForRow(at: oldIndex)?.accessoryType = .none 80 | } else { 81 | if let initialTableViewIndex = initialTableViewIndex { 82 | tableView.cellForRow(at: initialTableViewIndex)?.accessoryType = .none 83 | } 84 | } 85 | 86 | tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark 87 | 88 | return indexPath 89 | } 90 | 91 | override func viewDidLayoutSubviews() { 92 | // Add initial checkmark on row only when view is resized according to its preferredContentSize 93 | if fontTableView.indexPathForSelectedRow == nil && initialTableViewIndex != nil { 94 | fontTableView.cellForRow(at: initialTableViewIndex!)?.accessoryType = .checkmark 95 | } 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /Sources/Reader/EPUB/User Settings/en.lproj/UserSettings.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "UILabel"; text = "Page Margins"; ObjectID = "05J-dh-XXO"; */ 3 | "05J-dh-XXO.text" = "Page Margins"; 4 | 5 | /* Class = "UIButton"; accessibilityLabel = "Increase Page Margins"; ObjectID = "2Pi-2L-tEv"; */ 6 | "2Pi-2L-tEv.accessibilityLabel" = "Increase Page Margins"; 7 | 8 | /* Class = "UIButton"; accessibilityLabel = "Decrease Word Spacing"; ObjectID = "4C4-XJ-WHZ"; */ 9 | "4C4-XJ-WHZ.accessibilityLabel" = "Decrease Word Spacing"; 10 | 11 | /* Class = "UIButton"; accessibilityLabel = "Increase Letter Spacing"; ObjectID = "6Kd-tC-BhP"; */ 12 | "6Kd-tC-BhP.accessibilityLabel" = "Increase Letter Spacing"; 13 | 14 | /* Class = "UIViewController"; title = "Advanced settings"; ObjectID = "77D-Mk-Z01"; */ 15 | "77D-Mk-Z01.title" = "Advanced settings"; 16 | 17 | /* Class = "UILabel"; text = "-"; ObjectID = "7D5-LE-bsa"; */ 18 | "7D5-LE-bsa.text" = "-"; 19 | 20 | /* Class = "UILabel"; text = "fname"; ObjectID = "8XZ-xf-2Ua"; */ 21 | "8XZ-xf-2Ua.text" = "fname"; 22 | 23 | /* Class = "UISlider"; accessibilityLabel = "Brightness"; ObjectID = "98A-jm-Ha8"; */ 24 | "98A-jm-Ha8.accessibilityLabel" = "Brightness"; 25 | 26 | /* Class = "UIButton"; accessibilityLabel = "Decrease Page Margins"; ObjectID = "Bsg-3a-693"; */ 27 | "Bsg-3a-693.accessibilityLabel" = "Decrease Page Margins"; 28 | 29 | /* Class = "UISegmentedControl"; El2-dy-YXY.segmentTitles[0] = "auto"; ObjectID = "El2-dy-YXY"; */ 30 | "El2-dy-YXY.segmentTitles[0]" = "auto"; 31 | 32 | /* Class = "UISegmentedControl"; El2-dy-YXY.segmentTitles[1] = "1"; ObjectID = "El2-dy-YXY"; */ 33 | "El2-dy-YXY.segmentTitles[1]" = "1"; 34 | 35 | /* Class = "UISegmentedControl"; El2-dy-YXY.segmentTitles[2] = "2"; ObjectID = "El2-dy-YXY"; */ 36 | "El2-dy-YXY.segmentTitles[2]" = "2"; 37 | 38 | /* Class = "UITableViewCell"; accessibilityHint = "Double tap to select"; ObjectID = "J1O-1U-t1t"; */ 39 | "J1O-1U-t1t.accessibilityHint" = "Double tap to select"; 40 | 41 | /* Class = "UITableViewCell"; accessibilityHint = "Double tap to select"; ObjectID = "KgI-Ea-6ce"; */ 42 | "KgI-Ea-6ce.accessibilityHint" = "Double tap to select"; 43 | 44 | /* Class = "UILabel"; text = "Word Spacing"; ObjectID = "M0q-Tu-XNc"; */ 45 | "M0q-Tu-XNc.text" = "Word Spacing"; 46 | 47 | /* Class = "UISegmentedControl"; TZb-nI-KDr.segmentTitles[0] = "Default"; ObjectID = "TZb-nI-KDr"; */ 48 | "TZb-nI-KDr.segmentTitles[0]" = "Default"; 49 | 50 | /* Class = "UISegmentedControl"; TZb-nI-KDr.segmentTitles[1] = "Sepia"; ObjectID = "TZb-nI-KDr"; */ 51 | "TZb-nI-KDr.segmentTitles[1]" = "Sepia"; 52 | 53 | /* Class = "UISegmentedControl"; TZb-nI-KDr.segmentTitles[2] = "Night"; ObjectID = "TZb-nI-KDr"; */ 54 | "TZb-nI-KDr.segmentTitles[2]" = "Night"; 55 | 56 | /* Class = "UIViewController"; title = "Font selection"; ObjectID = "UDV-9G-lDt"; */ 57 | "UDV-9G-lDt.title" = "Font selection"; 58 | 59 | /* Class = "UIButton"; accessibilityLabel = "Decrease Letter Spacing"; ObjectID = "V5p-j8-qFi"; */ 60 | "V5p-j8-qFi.accessibilityLabel" = "Decrease Letter Spacing"; 61 | 62 | /* Class = "UILabel"; text = "Columns"; ObjectID = "Zyr-jk-03f"; */ 63 | "Zyr-jk-03f.text" = "Columns"; 64 | 65 | /* Class = "UISwitch"; accessibilityLabel = "Publisher's defaults"; ObjectID = "a7V-SY-NbP"; */ 66 | "a7V-SY-NbP.accessibilityLabel" = "Publisher's defaults"; 67 | 68 | /* Class = "UILabel"; text = "Line Height"; ObjectID = "bdf-ya-NK8"; */ 69 | "bdf-ya-NK8.text" = "Line Height"; 70 | 71 | /* Class = "UILabel"; text = "Advanced settings"; ObjectID = "cet-ah-qtg"; */ 72 | "cet-ah-qtg.text" = "Advanced settings"; 73 | 74 | /* Class = "UIButton"; accessibilityLabel = "Font Increase"; ObjectID = "g9Q-YR-gsD"; */ 75 | "g9Q-YR-gsD.accessibilityLabel" = "Font Increase"; 76 | 77 | /* Class = "UIButton"; normalTitle = "A"; ObjectID = "g9Q-YR-gsD"; */ 78 | "g9Q-YR-gsD.normalTitle" = "A"; 79 | 80 | /* Class = "UIButton"; selectedTitle = "A"; ObjectID = "g9Q-YR-gsD"; */ 81 | "g9Q-YR-gsD.selectedTitle" = "A"; 82 | 83 | /* Class = "UILabel"; text = "Publisher's defaults"; ObjectID = "gBh-xX-VRL"; */ 84 | "gBh-xX-VRL.text" = "Publisher's defaults"; 85 | 86 | /* Class = "UISwitch"; accessibilityLabel = "Scroll mode"; ObjectID = "kDI-GV-cGx"; */ 87 | "kDI-GV-cGx.accessibilityLabel" = "Scroll mode"; 88 | 89 | /* Class = "UILabel"; text = "-"; ObjectID = "n8X-0W-lHD"; */ 90 | "n8X-0W-lHD.text" = "-"; 91 | 92 | /* Class = "UILabel"; text = "-"; ObjectID = "oHs-C3-4vp"; */ 93 | "oHs-C3-4vp.text" = "-"; 94 | 95 | /* Class = "UILabel"; text = "Title"; ObjectID = "old-oT-1OP"; */ 96 | "old-oT-1OP.text" = "Title"; 97 | 98 | /* Class = "UILabel"; text = "Font"; ObjectID = "pS9-ec-4XU"; */ 99 | "pS9-ec-4XU.text" = "Font"; 100 | 101 | /* Class = "UILabel"; text = "-"; ObjectID = "pfB-vG-vRl"; */ 102 | "pfB-vG-vRl.text" = "-"; 103 | 104 | /* Class = "UILabel"; text = "Letter Spacing"; ObjectID = "rgR-fx-ups"; */ 105 | "rgR-fx-ups.text" = "Letter Spacing"; 106 | 107 | /* Class = "UIButton"; accessibilityLabel = "Decrease Line Height"; ObjectID = "vPS-fv-5nP"; */ 108 | "vPS-fv-5nP.accessibilityLabel" = "Decrease Line Height"; 109 | 110 | /* Class = "UILabel"; text = "Scroll mode"; ObjectID = "vim-oD-Nr3"; */ 111 | "vim-oD-Nr3.text" = "Scroll mode"; 112 | 113 | /* Class = "UIButton"; accessibilityLabel = "Increase Word Spacing"; ObjectID = "xPG-Eb-jSz"; */ 114 | "xPG-Eb-jSz.accessibilityLabel" = "Increase Word Spacing"; 115 | 116 | /* Class = "UIButton"; accessibilityLabel = "Font Decrease"; ObjectID = "ycF-0D-Zfs"; */ 117 | "ycF-0D-Zfs.accessibilityLabel" = "Font Decrease"; 118 | 119 | /* Class = "UIButton"; normalTitle = "A"; ObjectID = "ycF-0D-Zfs"; */ 120 | "ycF-0D-Zfs.normalTitle" = "A"; 121 | 122 | /* Class = "UIButton"; accessibilityLabel = "Increase Line Height"; ObjectID = "zc8-4S-b2w"; */ 123 | "zc8-4S-b2w.accessibilityLabel" = "Increase Line Height"; 124 | -------------------------------------------------------------------------------- /Sources/Reader/PDF/PDFModule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFModule.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 05.03.19. 6 | // 7 | // Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Foundation 14 | import UIKit 15 | import R2Navigator 16 | import R2Shared 17 | 18 | 19 | /// The PDF module is only available on iOS 11 and more, since it relies on PDFKit. 20 | @available(iOS 11.0, *) 21 | final class PDFModule: ReaderFormatModule { 22 | 23 | weak var delegate: ReaderFormatModuleDelegate? 24 | 25 | init(delegate: ReaderFormatModuleDelegate?) { 26 | self.delegate = delegate 27 | } 28 | 29 | var publicationFormats: [Publication.Format] { 30 | return [.pdf] 31 | } 32 | 33 | func makeReaderViewController(for publication: Publication, locator: Locator?, bookId: Book.Id, books: BookRepository, bookmarks: BookmarkRepository, resourcesServer: ResourcesServer) throws -> UIViewController { 34 | let viewController = PDFViewController(publication: publication, locator: locator, bookId: bookId, books: books, bookmarks: bookmarks) 35 | viewController.moduleDelegate = delegate 36 | return viewController 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Reader/PDF/PDFViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PDFViewController.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 07.03.19. 6 | // 7 | // Copyright 2019 Readium Foundation. All rights reserved. 8 | // Use of this source code is governed by a BSD-style license which is detailed 9 | // in the LICENSE file present in the project repository where this source code is maintained. 10 | // 11 | 12 | import Foundation 13 | import UIKit 14 | import R2Navigator 15 | import R2Shared 16 | 17 | 18 | @available(iOS 11.0, *) 19 | final class PDFViewController: ReaderViewController { 20 | 21 | init(publication: Publication, locator: Locator?, bookId: Book.Id, books: BookRepository, bookmarks: BookmarkRepository) { 22 | let navigator = PDFNavigatorViewController(publication: publication, initialLocation: locator) 23 | 24 | super.init(navigator: navigator, publication: publication, bookId: bookId, books: books, bookmarks: bookmarks) 25 | 26 | navigator.delegate = self 27 | } 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | } 32 | 33 | override var currentBookmark: Bookmark? { 34 | guard let locator = navigator.currentLocation else { 35 | return nil 36 | } 37 | 38 | return Bookmark(bookId: bookId, locator: locator) 39 | } 40 | 41 | } 42 | 43 | @available(iOS 11.0, *) 44 | extension PDFViewController: PDFNavigatorDelegate { 45 | } 46 | -------------------------------------------------------------------------------- /Sources/Reader/ReaderError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReaderError.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 12.06.19. 6 | // 7 | // Copyright 2019 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Foundation 14 | 15 | enum ReaderError: LocalizedError { 16 | case formatNotSupported 17 | case epubNotValid 18 | 19 | var errorDescription: String? { 20 | switch self { 21 | case .formatNotSupported: 22 | return NSLocalizedString("reader_error_formatNotSupported", comment: "Error message when trying to read a publication with a unsupported format") 23 | case .epubNotValid: 24 | return NSLocalizedString("reader_error_epubNotValid", comment: "Error message when trying to read an EPUB that is invalid") 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Reader/ReaderFactory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReaderFactory.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 22.02.19. 6 | // 7 | // Copyright 2019 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Foundation 14 | import UIKit 15 | import R2Shared 16 | 17 | 18 | final class ReaderFactory { 19 | 20 | final class Storyboards { 21 | let outline = UIStoryboard(name: "Outline", bundle: nil) 22 | let drm = UIStoryboard(name: "DRM", bundle: nil) 23 | } 24 | 25 | let storyboards = Storyboards() 26 | } 27 | 28 | extension ReaderFactory: OutlineTableViewControllerFactory { 29 | func make(publication: Publication, bookId: Book.Id, bookmarks: BookmarkRepository) -> OutlineTableViewController { 30 | let controller = storyboards.outline.instantiateViewController(withIdentifier: "OutlineTableViewController") as! OutlineTableViewController 31 | controller.publication = publication 32 | controller.bookId = bookId 33 | controller.bookmarkRepository = bookmarks 34 | return controller 35 | } 36 | } 37 | 38 | extension ReaderFactory: DRMManagementTableViewControllerFactory { 39 | func make(publication: Publication, delegate: ReaderModuleDelegate?) -> DRMManagementTableViewController { 40 | let controller = 41 | storyboards.drm.instantiateViewController(withIdentifier: "DRMManagementTableViewController") as! DRMManagementTableViewController 42 | controller.moduleDelegate = delegate 43 | controller.viewModel = DRMViewModel.make(publication: publication, presentingViewController: controller) 44 | return controller 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Reader/ReaderFormatModule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReaderFormatModule.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 22.02.19. 6 | // 7 | // Copyright 2019 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Foundation 14 | import UIKit 15 | import R2Shared 16 | 17 | 18 | /// A ReaderFormatModule is a sub-module of ReaderModule that handles publication of a given format (eg. EPUB, CBZ). 19 | protocol ReaderFormatModule { 20 | 21 | var delegate: ReaderFormatModuleDelegate? { get } 22 | 23 | /// Publication types handled by this sub-module. 24 | var publicationFormats: [Publication.Format] { get } 25 | 26 | /// Creates the view controller to present the publication. 27 | func makeReaderViewController(for publication: Publication, locator: Locator?, bookId: Book.Id, books: BookRepository, bookmarks: BookmarkRepository, resourcesServer: ResourcesServer) throws -> UIViewController 28 | 29 | } 30 | 31 | protocol ReaderFormatModuleDelegate: AnyObject { 32 | 33 | /// Shows the reader's outline from the given links. 34 | func presentOutline(of publication: Publication, bookId: Book.Id, delegate: OutlineTableViewControllerDelegate?, from viewController: UIViewController) 35 | 36 | /// Shows the DRM management screen for the given DRM. 37 | func presentDRM(for publication: Publication, from viewController: UIViewController) 38 | 39 | func presentAlert(_ title: String, message: String, from viewController: UIViewController) 40 | func presentError(_ error: Error?, from viewController: UIViewController) 41 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Sources/Reader/ReaderModule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReaderModule.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Mickaël Menu on 22.02.19. 6 | // 7 | // Copyright 2019 European Digital Reading Lab. All rights reserved. 8 | // Licensed to the Readium Foundation under one or more contributor license agreements. 9 | // Use of this source code is governed by a BSD-style license which is detailed in the 10 | // LICENSE file present in the project repository where this source code is maintained. 11 | // 12 | 13 | import Foundation 14 | import UIKit 15 | import R2Shared 16 | 17 | 18 | /// The ReaderModule handles the presentation of publications to be read by the user. 19 | /// It contains sub-modules implementing ReaderFormatModule to handle each format of publication (eg. CBZ, EPUB). 20 | protocol ReaderModuleAPI { 21 | 22 | var delegate: ReaderModuleDelegate? { get } 23 | 24 | /// Presents the given publication to the user, inside the given navigation controller. 25 | /// - Parameter completion: Called once the publication is presented, or if an error occured. 26 | func presentPublication(publication: Publication, book: Book, in navigationController: UINavigationController, completion: @escaping () -> Void) 27 | 28 | } 29 | 30 | protocol ReaderModuleDelegate: ModuleDelegate { 31 | } 32 | 33 | 34 | final class ReaderModule: ReaderModuleAPI { 35 | 36 | weak var delegate: ReaderModuleDelegate? 37 | private let books: BookRepository 38 | private let bookmarks: BookmarkRepository 39 | private let resourcesServer: ResourcesServer 40 | 41 | /// Sub-modules to handle different publication formats (eg. EPUB, CBZ) 42 | var formatModules: [ReaderFormatModule] = [] 43 | 44 | private let factory = ReaderFactory() 45 | 46 | init(delegate: ReaderModuleDelegate?, books: BookRepository, bookmarks: BookmarkRepository, resourcesServer: ResourcesServer) { 47 | self.delegate = delegate 48 | self.books = books 49 | self.bookmarks = bookmarks 50 | self.resourcesServer = resourcesServer 51 | 52 | formatModules = [ 53 | CBZModule(delegate: self), 54 | EPUBModule(delegate: self), 55 | ] 56 | 57 | if #available(iOS 11.0, *) { 58 | formatModules.append(PDFModule(delegate: self)) 59 | } 60 | } 61 | 62 | func presentPublication(publication: Publication, book: Book, in navigationController: UINavigationController, completion: @escaping () -> Void) { 63 | guard let delegate = delegate, let bookId = book.id else { 64 | fatalError("Reader delegate not set") 65 | } 66 | 67 | func present(_ viewController: UIViewController) { 68 | let backItem = UIBarButtonItem() 69 | backItem.title = "" 70 | viewController.navigationItem.backBarButtonItem = backItem 71 | viewController.hidesBottomBarWhenPushed = true 72 | navigationController.pushViewController(viewController, animated: true) 73 | } 74 | 75 | guard let module = self.formatModules.first(where:{ $0.publicationFormats.contains(publication.format) }) else { 76 | delegate.presentError(ReaderError.formatNotSupported, from: navigationController) 77 | completion() 78 | return 79 | } 80 | 81 | do { 82 | let readerViewController = try module.makeReaderViewController(for: publication, locator: book.locator, bookId: bookId, books: books, bookmarks: bookmarks, resourcesServer: resourcesServer) 83 | present(readerViewController) 84 | } catch { 85 | delegate.presentError(error, from: navigationController) 86 | } 87 | 88 | completion() 89 | } 90 | 91 | } 92 | 93 | 94 | extension ReaderModule: ReaderFormatModuleDelegate { 95 | 96 | func presentDRM(for publication: Publication, from viewController: UIViewController) { 97 | let drmViewController: DRMManagementTableViewController = factory.make(publication: publication, delegate: delegate) 98 | let backItem = UIBarButtonItem() 99 | backItem.title = "" 100 | drmViewController.navigationItem.backBarButtonItem = backItem 101 | viewController.navigationController?.pushViewController(drmViewController, animated: true) 102 | } 103 | 104 | func presentOutline(of publication: Publication, bookId: Book.Id, delegate: OutlineTableViewControllerDelegate?, from viewController: UIViewController) { 105 | let outlineTableVC: OutlineTableViewController = factory.make(publication: publication, bookId: bookId, bookmarks: bookmarks) 106 | outlineTableVC.delegate = delegate 107 | viewController.present(UINavigationController(rootViewController: outlineTableVC), animated: true) 108 | } 109 | 110 | func presentAlert(_ title: String, message: String, from viewController: UIViewController) { 111 | delegate?.presentAlert(title, message: message, from: viewController) 112 | } 113 | 114 | func presentError(_ error: Error?, from viewController: UIViewController) { 115 | delegate?.presentError(error, from: viewController) 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /Sources/Reader/Toast.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Toast.swift 3 | // r2-testapp-swift 4 | // 5 | // Created by Aferdita Muriqi on 8/4/18. 6 | // Copyright © 2018 Readium. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MBProgressHUD 11 | 12 | /// Displays the given text in the view for the given duration. 13 | func toast(_ text: String, on view: UIView, duration: TimeInterval) { 14 | let hud = MBProgressHUD.showAdded(to: view, animated: true) 15 | hud.mode = .text 16 | hud.label.text = text 17 | hud.hide(animated: true, afterDelay: duration) 18 | } 19 | 20 | /// Displays an activity indicator in the view. 21 | /// 22 | /// - Returns: A closure to be called when the toast needs to be hidden. 23 | func toastActivity(on view: UIView) -> () -> () { 24 | let hud = MBProgressHUD.showAdded(to: view, animated: true) 25 | hud.mode = .indeterminate 26 | return { 27 | hud.hide(animated: true) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon29x29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon29x29.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@2x-1.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon29x29@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon40x40.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@2x-1.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon40x40@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon60x60@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon60x60@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon76x76.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon76x76@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "size" : "29x29", 15 | "idiom" : "iphone", 16 | "filename" : "AppIcon29x29@2x-1.png", 17 | "scale" : "2x" 18 | }, 19 | { 20 | "size" : "29x29", 21 | "idiom" : "iphone", 22 | "filename" : "AppIcon29x29@3x.png", 23 | "scale" : "3x" 24 | }, 25 | { 26 | "size" : "40x40", 27 | "idiom" : "iphone", 28 | "filename" : "AppIcon40x40@2x-1.png", 29 | "scale" : "2x" 30 | }, 31 | { 32 | "size" : "40x40", 33 | "idiom" : "iphone", 34 | "filename" : "AppIcon40x40@3x.png", 35 | "scale" : "3x" 36 | }, 37 | { 38 | "size" : "60x60", 39 | "idiom" : "iphone", 40 | "filename" : "AppIcon60x60@2x.png", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "size" : "60x60", 45 | "idiom" : "iphone", 46 | "filename" : "AppIcon60x60@3x.png", 47 | "scale" : "3x" 48 | }, 49 | { 50 | "idiom" : "ipad", 51 | "size" : "20x20", 52 | "scale" : "1x" 53 | }, 54 | { 55 | "idiom" : "ipad", 56 | "size" : "20x20", 57 | "scale" : "2x" 58 | }, 59 | { 60 | "size" : "29x29", 61 | "idiom" : "ipad", 62 | "filename" : "AppIcon29x29.png", 63 | "scale" : "1x" 64 | }, 65 | { 66 | "size" : "29x29", 67 | "idiom" : "ipad", 68 | "filename" : "AppIcon29x29@2x.png", 69 | "scale" : "2x" 70 | }, 71 | { 72 | "size" : "40x40", 73 | "idiom" : "ipad", 74 | "filename" : "AppIcon40x40.png", 75 | "scale" : "1x" 76 | }, 77 | { 78 | "size" : "40x40", 79 | "idiom" : "ipad", 80 | "filename" : "AppIcon40x40@2x.png", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "size" : "76x76", 85 | "idiom" : "ipad", 86 | "filename" : "AppIcon76x76.png", 87 | "scale" : "1x" 88 | }, 89 | { 90 | "size" : "76x76", 91 | "idiom" : "ipad", 92 | "filename" : "AppIcon76x76@2x.png", 93 | "scale" : "2x" 94 | }, 95 | { 96 | "size" : "83.5x83.5", 97 | "idiom" : "ipad", 98 | "filename" : "readiumlogo_2048-83.5@2x.png", 99 | "scale" : "2x" 100 | }, 101 | { 102 | "size" : "1024x1024", 103 | "idiom" : "ios-marketing", 104 | "filename" : "icon_1024x1024.png", 105 | "scale" : "1x" 106 | } 107 | ], 108 | "info" : { 109 | "version" : 1, 110 | "author" : "xcode" 111 | } 112 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/AppIcon.appiconset/icon_1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/AppIcon.appiconset/icon_1024x1024.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/AppIcon.appiconset/readiumlogo_2048-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/AppIcon.appiconset/readiumlogo_2048-83.5@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/about.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "glyphicons-196-info-sign.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "glyphicons-196-info-sign@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "glyphicons-196-info-sign@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/about.imageset/glyphicons-196-info-sign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/about.imageset/glyphicons-196-info-sign.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/about.imageset/glyphicons-196-info-sign@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/about.imageset/glyphicons-196-info-sign@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/about.imageset/glyphicons-196-info-sign@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/about.imageset/glyphicons-196-info-sign@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/alignLeft.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "glyphicons-111-align-left.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "glyphicons-111-align-left@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "glyphicons-111-align-left@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/alignLeft.imageset/glyphicons-111-align-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/alignLeft.imageset/glyphicons-111-align-left.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/alignLeft.imageset/glyphicons-111-align-left@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/alignLeft.imageset/glyphicons-111-align-left@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/alignLeft.imageset/glyphicons-111-align-left@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/alignLeft.imageset/glyphicons-111-align-left@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/alignRight.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "glyphicons-113-align-right.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "glyphicons-113-align-right@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "glyphicons-113-align-right@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/alignRight.imageset/glyphicons-113-align-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/alignRight.imageset/glyphicons-113-align-right.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/alignRight.imageset/glyphicons-113-align-right@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/alignRight.imageset/glyphicons-113-align-right@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/alignRight.imageset/glyphicons-113-align-right@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/alignRight.imageset/glyphicons-113-align-right@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/bookIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ic_book_48pt.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "ic_book_48pt_2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "ic_book_48pt_3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/bookIcon.imageset/ic_book_48pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/bookIcon.imageset/ic_book_48pt.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/bookIcon.imageset/ic_book_48pt_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/bookIcon.imageset/ic_book_48pt_2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/bookIcon.imageset/ic_book_48pt_3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/bookIcon.imageset/ic_book_48pt_3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/bookmark.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bookmark-white-20.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "bookmark-white-20@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "bookmark-white-20@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/bookmark.imageset/bookmark-white-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/bookmark.imageset/bookmark-white-20.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/bookmark.imageset/bookmark-white-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/bookmark.imageset/bookmark-white-20@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/bookmark.imageset/bookmark-white-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/bookmark.imageset/bookmark-white-20@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/bookshelf.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bookshelf_1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "bookshelf_2.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "bookshelf_3.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/bookshelf.imageset/bookshelf_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/bookshelf.imageset/bookshelf_1.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/bookshelf.imageset/bookshelf_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/bookshelf.imageset/bookshelf_2.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/bookshelf.imageset/bookshelf_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/bookshelf.imageset/bookshelf_3.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/catalogs.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ic_view_list_1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "ic_view_list_2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "ic_view_list_3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/catalogs.imageset/ic_view_list_1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/catalogs.imageset/ic_view_list_1x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/catalogs.imageset/ic_view_list_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/catalogs.imageset/ic_view_list_2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/catalogs.imageset/ic_view_list_3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/catalogs.imageset/ic_view_list_3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/center.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "glyphicons-112-align-center.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "glyphicons-112-align-center@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "glyphicons-112-align-center@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/center.imageset/glyphicons-112-align-center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/center.imageset/glyphicons-112-align-center.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/center.imageset/glyphicons-112-align-center@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/center.imageset/glyphicons-112-align-center@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/center.imageset/glyphicons-112-align-center@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/center.imageset/glyphicons-112-align-center@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/drm.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "glyphicons-241-rotation-lock.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "glyphicons-241-rotation-lock@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "glyphicons-241-rotation-lock@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/drm.imageset/glyphicons-241-rotation-lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/drm.imageset/glyphicons-241-rotation-lock.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/drm.imageset/glyphicons-241-rotation-lock@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/drm.imageset/glyphicons-241-rotation-lock@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/drm.imageset/glyphicons-241-rotation-lock@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/drm.imageset/glyphicons-241-rotation-lock@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/edrlab-logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "edrlab-logo.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/edrlab-logo.imageset/edrlab-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/edrlab-logo.imageset/edrlab-logo.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/gradient.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "gradient.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/gradient.imageset/gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/gradient.imageset/gradient.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/higherBrightness.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "glyphicons-190-brightness-increase.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "glyphicons-190-brightness-increase@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "glyphicons-190-brightness-increase@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/higherBrightness.imageset/glyphicons-190-brightness-increase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/higherBrightness.imageset/glyphicons-190-brightness-increase.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/higherBrightness.imageset/glyphicons-190-brightness-increase@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/higherBrightness.imageset/glyphicons-190-brightness-increase@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/higherBrightness.imageset/glyphicons-190-brightness-increase@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/higherBrightness.imageset/glyphicons-190-brightness-increase@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/justify.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "glyphicons-114-justify.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "glyphicons-114-justify@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "glyphicons-114-justify@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/justify.imageset/glyphicons-114-justify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/justify.imageset/glyphicons-114-justify.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/justify.imageset/glyphicons-114-justify@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/justify.imageset/glyphicons-114-justify@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/justify.imageset/glyphicons-114-justify@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/justify.imageset/glyphicons-114-justify@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/lowerBrightness.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "glyphicons-189-brightness-reduce.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "glyphicons-189-brightness-reduce@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "glyphicons-189-brightness-reduce@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/lowerBrightness.imageset/glyphicons-189-brightness-reduce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/lowerBrightness.imageset/glyphicons-189-brightness-reduce.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/lowerBrightness.imageset/glyphicons-189-brightness-reduce@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/lowerBrightness.imageset/glyphicons-189-brightness-reduce@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/lowerBrightness.imageset/glyphicons-189-brightness-reduce@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/lowerBrightness.imageset/glyphicons-189-brightness-reduce@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/menuIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "glyphicons-517-menu-hamburger.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "glyphicons-517-menu-hamburger@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "glyphicons-517-menu-hamburger@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/menuIcon.imageset/glyphicons-517-menu-hamburger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/menuIcon.imageset/glyphicons-517-menu-hamburger.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/menuIcon.imageset/glyphicons-517-menu-hamburger@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/menuIcon.imageset/glyphicons-517-menu-hamburger@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/menuIcon.imageset/glyphicons-517-menu-hamburger@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/menuIcon.imageset/glyphicons-517-menu-hamburger@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/minus.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "glyphicons-434-minus.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "glyphicons-434-minus@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "glyphicons-434-minus@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/minus.imageset/glyphicons-434-minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/minus.imageset/glyphicons-434-minus.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/minus.imageset/glyphicons-434-minus@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/minus.imageset/glyphicons-434-minus@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/minus.imageset/glyphicons-434-minus@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/minus.imageset/glyphicons-434-minus@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/plus.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "glyphicons-433-plus.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "glyphicons-433-plus@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "glyphicons-433-plus@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/plus.imageset/glyphicons-433-plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/plus.imageset/glyphicons-433-plus.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/plus.imageset/glyphicons-433-plus@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/plus.imageset/glyphicons-433-plus@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/plus.imageset/glyphicons-433-plus@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/plus.imageset/glyphicons-433-plus@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/r2-logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "r2-logo.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/r2-logo.imageset/r2-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/r2-logo.imageset/r2-logo.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/readium-icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "readium-icon.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/readium-icon.imageset/readium-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/readium-icon.imageset/readium-icon.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/rf.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "rf.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "rf@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "rf@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/rf.imageset/rf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/rf.imageset/rf.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/rf.imageset/rf@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/rf.imageset/rf@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/rf.imageset/rf@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/rf.imageset/rf@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/settingsIcon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "glyphicons-101-font.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "glyphicons-101-font@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "glyphicons-101-font@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/settingsIcon.imageset/glyphicons-101-font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/settingsIcon.imageset/glyphicons-101-font.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/settingsIcon.imageset/glyphicons-101-font@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/settingsIcon.imageset/glyphicons-101-font@2x.png -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/settingsIcon.imageset/glyphicons-101-font@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Assets.xcassets/settingsIcon.imageset/glyphicons-101-font@3x.png -------------------------------------------------------------------------------- /Sources/Resources/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /Sources/Resources/Samples/1.cbz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Samples/1.cbz -------------------------------------------------------------------------------- /Sources/Resources/Samples/1.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Samples/1.epub -------------------------------------------------------------------------------- /Sources/Resources/Samples/2.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Samples/2.epub -------------------------------------------------------------------------------- /Sources/Resources/Samples/3.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Samples/3.epub -------------------------------------------------------------------------------- /Sources/Resources/Samples/4.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Samples/4.epub -------------------------------------------------------------------------------- /Sources/Resources/Samples/5.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Samples/5.epub -------------------------------------------------------------------------------- /Sources/Resources/Samples/6.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/readium/r2-testapp-swift/ddacada9162fd85ce0e071629fe2d8ad79b0645b/Sources/Resources/Samples/6.epub -------------------------------------------------------------------------------- /Sources/Resources/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | r2-testapp-swift 4 | 5 | Created by Mickaël Menu on 12.06.19. 6 | 7 | Copyright 2018 European Digital Reading Lab. All rights reserved. 8 | Licensed to the Readium Foundation under one or more contributor license agreements. 9 | Use of this source code is governed by a BSD-style license which is detailed in the 10 | LICENSE file present in the project repository where this source code is maintained. 11 | */ 12 | 13 | 14 | /* MARK: App */ 15 | 16 | /* Library tab title */ 17 | "bookshelf_tab" = "Bookshelf"; 18 | "catalogs_tab" = "OPDS Feeds"; 19 | "about_tab" = "About"; 20 | 21 | /* Buttons */ 22 | "ok_button" = "OK"; 23 | "cancel_button" = "Cancel"; 24 | "confirm_button" = "Confirm"; 25 | "add_button" = "Add"; 26 | "edit_button" = "Edit"; 27 | "remove_button" = "Remove"; 28 | "filter_button" = "Filter"; 29 | 30 | /* Alerts */ 31 | "error_title" = "Error"; 32 | "success_title" = "Success"; 33 | 34 | /* Caption for the app version in About screen */ 35 | "app_version_caption" = "App Version:"; 36 | /* Caption for the build version in About screen */ 37 | "build_version_caption" = "Build Version:"; 38 | 39 | 40 | /* MARK: Library */ 41 | 42 | /* Hint message when the library is empty */ 43 | "library_empty_message" = "📖 Open EPUB/CBZ file to import"; 44 | 45 | /* Add book alert message */ 46 | "library_add_book_title" = "Add a book to your library"; 47 | "library_add_book_from_device_button" = "From your device"; 48 | "library_add_book_from_url_button" = "From a URL"; 49 | "library_add_book_from_url_title" = "Add a book from a URL"; 50 | "library_add_book_from_url_failure_message" = "URL is not valid, please try again"; 51 | "library_add_book_from_url_placeholder" = "URL"; 52 | 53 | /* Alert when a publication is successfully imported */ 54 | "library_import_success_message" = "Publication added to library"; 55 | /* Alert when a publication is successfully downloaded */ 56 | "library_download_success_message" = "[%@] added to library."; 57 | 58 | /* Import confirmation alert when the publication already exists in the library */ 59 | "library_duplicate_alert_title" = "The publication already exists"; 60 | "library_duplicate_alert_message" = "Would you like to add it anyways?"; 61 | 62 | /* Remove confirmation alert when the user deletes a publication */ 63 | "library_delete_alert_title" = "Are you sure?"; 64 | "library_delete_alert_message" = "This will remove the Publication from your library."; 65 | 66 | /* Error message used when trying to import a publication that is not valid */ 67 | "library_error_publicationIsNotValid" = "The publication isn't valid"; 68 | /* Error message used when trying to open a book whose file is not found */ 69 | "library_error_bookNotFound" = "The book file was not found"; 70 | /* Error message used when a low-level error occured while importing a publication */ 71 | "library_error_importFailed" = "Error while importing this publication: %@"; 72 | /* Error message used when a low-level error occured while opening a publication */ 73 | "library_error_openFailed" = "Error while opening this publication: %@"; 74 | /* Error message when the download of a publication failed */ 75 | "library_error_downloadFailed" = "Download failed: %@"; 76 | 77 | /* Accessibility label for the library collection view */ 78 | "library_a11y_label" = "Library"; 79 | 80 | /* Accessibility hint for the publication collection cell */ 81 | "library_publication_a11y_hint" = "Hold to access options."; 82 | 83 | 84 | /* MARK: Reader */ 85 | 86 | /* Error message when adding a new bookmark failed */ 87 | "reader_bookmark_failure_message" = "Could not add bookmark"; 88 | /* Success message when adding a bookmark */ 89 | "reader_bookmark_success_message" = "Bookmark added"; 90 | 91 | /* Error message when trying to read a publication with a unsupported format */ 92 | "reader_error_formatNotSupported" = "Unsupported format"; 93 | /* Error message when trying to read an EPUB that is invalid */ 94 | "reader_error_epubNotValid" = "Invalid EPUB file"; 95 | 96 | /* Accessibility label to go backward in the publication */ 97 | "reader_backward_a11y_label" = "Previous Chapter"; 98 | /* Accessibility label to go forward in the publication */ 99 | "reader_forward_a11y_label" = "Next Chapter"; 100 | 101 | /* Outline bookmark label when the position is available */ 102 | "reader_outline_position_label" = "position %d"; 103 | /* Outline bookmark label when the progression is available */ 104 | "reader_outline_progression_label" = "%.2f%% through the chapter"; 105 | 106 | /* Title of the DRM management view */ 107 | "reader_drm_management_title" = "DRM Management"; 108 | /* Unlimited quantity for a given DRM consumable right */ 109 | "reader_drm_unlimited_label" = "unlimited"; 110 | /* Title of the renew confirmation alert */ 111 | "reader_drm_renew_title" = "Renew License"; 112 | /* Message of the renew confirmation alert */ 113 | "reader_drm_renew_message" = "The provider will receive your request and process it."; 114 | /* Success message after renewing a publication */ 115 | "reader_drm_renew_success_message" = "Publication renewed successfully."; 116 | /* Title of the return confirmation alert */ 117 | "reader_drm_return_title" = "Return License"; 118 | /* Message of the return confirmation alert */ 119 | "reader_drm_return_message" = "Returning the loan will prevent you from accessing the publication."; 120 | /* Success message after returning a publication */ 121 | "reader_drm_return_success_message" = "Publication returned successfully."; 122 | 123 | 124 | /* MARK: LCP */ 125 | 126 | /* Quantity of characters left to be copied */ 127 | "lcp_characters_label" = "%d characters"; 128 | /* Quantity of pages left to be printed */ 129 | "lcp_pages_label" = "%d pages"; 130 | 131 | 132 | /* MARK: OPDS */ 133 | 134 | /* Button to edit the OPDS catalog */ 135 | "opds_edit_button" = "Edit catalog"; 136 | /* Button to expand a feed gallery */ 137 | "opds_more_button" = "more >"; 138 | "opds_more_button_a11y_label" = "expand feed"; 139 | /* Title of the section displaying the feeds */ 140 | "opds_browse_title" = "Browse"; 141 | /* Title of the add feed alert */ 142 | "opds_add_title" = "Enter feed title and URL"; 143 | /* Message when adding an invalid OPDS feed */ 144 | "opds_add_failure_message" = "Feed is not valid, please try again."; 145 | /* Label for the OPDS feed title field */ 146 | "opds_feed_title_caption" = "Feed Title"; 147 | /* Label for the OPDS feed URL field */ 148 | "opds_feed_url_caption" = "Feed URL"; 149 | /* Error message when the feed couldn't be loaded */ 150 | "opds_failure_message" = "Something went wrong."; 151 | /* Add OPDS Feed button label */ 152 | "opds_add_button_a11y_label" = "Add OPDS Feed"; 153 | "opds_show_detail_view_a11y_hint" = "tap to view details"; 154 | "opds_feed_header_a11y_hint" = "Feed Group Header"; 155 | 156 | 157 | /* MARK: User Settings */ 158 | 159 | "user_settings_appearance_default_a11y_label" = "Appearance default"; 160 | "user_settings_appearance_sepia_a11y_label" = "Appearance sepia"; 161 | "user_settings_appearance_night_a11y_label" = "Appearance night"; 162 | 163 | "user_settings_alignment_justify_a11y_label" = "Alignment justify"; 164 | "user_settings_alignment_left_a11y_label" = "Alignment left"; 165 | 166 | "user_settings_column_auto_a11y_label" = "Column auto"; 167 | "user_settings_column_1_a11y_label" = "Column 1"; 168 | "user_settings_column_2_a11y_label" = "Column 2"; 169 | -------------------------------------------------------------------------------- /Sources/r2-testapp-swift.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.icloud-container-identifiers 6 | 7 | com.apple.developer.icloud-services 8 | 9 | CloudDocuments 10 | 11 | com.apple.developer.ubiquity-container-identifiers 12 | 13 | 14 | 15 | --------------------------------------------------------------------------------