diff --git a/README.md b/README.md
index 94c5655..4904199 100644
--- a/README.md
+++ b/README.md
@@ -8,14 +8,16 @@ which implements both TTS and Text+Audio sync on top of the native toolkits via
This plugin is supposed to support both EPUB and WebPubs, with or without pre-recorded audio.
## Plans
+
We will work on the main branch on a modernized and simple API using newest toolkits and utilize much more of the toolkit functionality.
See repo Issues for up-to-date progress.
General TODO:
-- [X] Use Preferences API on both platforms.
-- [X] Use Decorator API for highlighting.
-- [X] Test TTS and Audio navigators for maturity, possibly replacing our own audio handlers.
+
+- [x] Use Preferences API on both platforms.
+- [x] Use Decorator API for highlighting.
+- [x] Test TTS and Audio navigators for maturity, possibly replacing our own audio handlers.
- [ ] Finding: Readium native toolkits do not yet support MediaOverlays, making support for "karaoke books" difficult.
We either wait for this to be implemented, or do something similar to the `nota-lyt4` branch and use `audio_service` and `just_audio` plugins for MediaOverlay playback.
@@ -40,7 +42,7 @@ Also, update your Android and iOS projects as follows:
your `android/app/src/main/AndroidManifest.xml` file:
```html
-
+
```
### iOS
@@ -71,6 +73,34 @@ end
NSAppTransportSecurity
NSAllowsArbitraryLoads
-
+
```
+
+### Web
+
+To use this plugin for web, follow these steps to ensure everything works correctly:
+
+#### 1. Copy the JavaScript File
+
+To use the JavaScript file from the plugin in your Flutter web app, run the following command in your terminal from the root directory of your app:
+
+```bash
+dart run flutter_readium:copy_js_file
+```
+
+It is recommended to place the destination directory within the `web` directory or a subdirectory of it. Avoid saving it outside the `web` directory.
+
+#### 2. Add Scripts to `index.html`
+
+After copying the JavaScript file to your app, add Flutter's initialization JS code and the plugin JS to the `head` section of your `index.html` file:
+
+```html
+
+
+
+
+
+```
+
+If the plugin's JS file is not saved in the same directory as `index.html`, update the source path accordingly.
diff --git a/bin/build_js b/bin/build_js
new file mode 100755
index 0000000..930feac
--- /dev/null
+++ b/bin/build_js
@@ -0,0 +1,5 @@
+# Build for flutter_readium web
+echo "Building flutter_readium web"
+cd ./flutter_readium/
+npm i
+npm run build
\ No newline at end of file
diff --git a/bin/code_gen b/bin/code_gen
new file mode 100755
index 0000000..0ebd6bc
--- /dev/null
+++ b/bin/code_gen
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+set -e
+
+cd ./flutter_readium_platform_interface; dart run build_runner build --delete-conflicting-outputs
\ No newline at end of file
diff --git a/bin/forAll b/bin/forAll
new file mode 100755
index 0000000..fd46d25
--- /dev/null
+++ b/bin/forAll
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+if [ "x$1" == "x" ]
+then
+ echo "Possible usage:"
+ echo " $0 dart pub outdated"
+ echo " $0 dart pub upgrade"
+ exit 1
+fi
+
+dir=(
+ "./flutter_readium"
+ "./flutter_readium_platform_interface"
+ "./flutter_readium_web"
+)
+for i in "${dir[@]}"; do
+ echo -e "\033[35;1m=== $@ $i ===\033[0m"
+ (cd "$i" || exit; $@)
+done
diff --git a/bin/install b/bin/install
new file mode 100755
index 0000000..c70ef0c
--- /dev/null
+++ b/bin/install
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+set -e
+
+./bin/forAll flutter pub get
+
+# this is not working as expected
+if [ "$(uname)" == "Darwin" ]; then
+ (cd ./flutter_readium/example/ios; pod install --repo-update)
+fi
+
+
+./bin/build_js
\ No newline at end of file
diff --git a/flutter_readium/README.md b/flutter_readium/README.md
index a3b2bb8..f4d427c 100644
--- a/flutter_readium/README.md
+++ b/flutter_readium/README.md
@@ -12,3 +12,19 @@ Android and/or iOS.
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
+
+## Editing on web
+
+When making changes to the TypeScript files, convert the main TypeScript file (`ReadiumReader.ts`) to JavaScript using:
+
+```bash
+npm run build
+```
+
+Run this command from the root of the plugin.
+
+To test the changes, follow the installation instructions in the example app directory.
+
+```bash
+dart run flutter_readium:copy_js_file
+```
diff --git a/flutter_readium/android/src/main/kotlin/dk/nota/flutter_readium/ReadiumExtensions.kt b/flutter_readium/android/src/main/kotlin/dk/nota/flutter_readium/ReadiumExtensions.kt
index 843218f..565f947 100644
--- a/flutter_readium/android/src/main/kotlin/dk/nota/flutter_readium/ReadiumExtensions.kt
+++ b/flutter_readium/android/src/main/kotlin/dk/nota/flutter_readium/ReadiumExtensions.kt
@@ -75,7 +75,8 @@ fun epubPreferencesFromMap(
fontWeight = prefMap["fontWeight"]?.toDoubleOrNull() ?: defaults?.fontWeight,
scroll = prefMap["verticalScroll"]?.toBoolean() ?: defaults?.scroll,
backgroundColor = prefMap["backgroundColor"]?.let { readiumColorFromCSS(it) } ?: defaults?.backgroundColor,
- textColor = prefMap["textColor"]?.let { readiumColorFromCSS(it) } ?: defaults?.textColor
+ textColor = prefMap["textColor"]?.let { readiumColorFromCSS(it) } ?: defaults?.textColor,
+ pageMargins = prefMap["pageMargins"]?.toDoubleOrNull() ?: defaults?.pageMargins,
)
return newPreferences
} catch (ex: Exception) {
diff --git a/flutter_readium/bin/copy_js_file.dart b/flutter_readium/bin/copy_js_file.dart
new file mode 100644
index 0000000..7aff0e2
--- /dev/null
+++ b/flutter_readium/bin/copy_js_file.dart
@@ -0,0 +1,42 @@
+// ignore_for_file: avoid_print
+
+import 'dart:io';
+import 'dart:isolate';
+
+void main(List args) async {
+ if (args.isEmpty) {
+ print('Usage: dart run flutter_readium:copy_js_file ');
+ return;
+ }
+
+ final destinationDir = args[0];
+ final packageUri = Uri.parse('package:flutter_readium/helpers/readiumReader.js');
+ final resolvedUri = await Isolate.resolvePackageUri(packageUri);
+
+ if (resolvedUri == null) {
+ print('Error: Could not resolve package URI');
+ return;
+ }
+
+ final sourcePath = resolvedUri.toFilePath();
+ final sourceFile = File(sourcePath);
+
+ if (!sourceFile.existsSync()) {
+ print('Error: Source file not found: $sourcePath');
+ return;
+ }
+
+ final destinationPath = '$destinationDir/readiumReader.js';
+ // final destinationFile = File(destinationPath);
+
+ try {
+ // Ensure the destination directory exists
+ Directory(destinationDir).createSync(recursive: true);
+
+ // Copy the file
+ sourceFile.copySync(destinationPath);
+ print('File copied to $destinationPath');
+ } catch (e) {
+ print('Error copying file: $e');
+ }
+}
diff --git a/flutter_readium/example/assets/webManifestList.json b/flutter_readium/example/assets/webManifestList.json
new file mode 100644
index 0000000..0b3940a
--- /dev/null
+++ b/flutter_readium/example/assets/webManifestList.json
@@ -0,0 +1,4 @@
+[
+ "https://merkur.live.dbb.dk/opds2/publication/free/merkur:libraryid:37881/manifest.json",
+ "https://publication-server.readium.org/bW9ieS1kaWNrLmVwdWI/manifest.json"
+]
diff --git a/flutter_readium/example/lib/main.dart b/flutter_readium/example/lib/main.dart
index 364210b..1571648 100644
--- a/flutter_readium/example/lib/main.dart
+++ b/flutter_readium/example/lib/main.dart
@@ -4,7 +4,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:path_provider/path_provider.dart';
-import 'extensions/text_settings_theme.dart' show themes;
import 'pages/index.dart';
import 'state/index.dart';
@@ -29,7 +28,11 @@ Future main() async {
lazy: false,
),
BlocProvider(
- create: (final _) => TextSettingsBloc()..add(ChangeTheme(themes[0])),
+ create: (final _) {
+ final bloc = TextSettingsBloc();
+ bloc.setDefaultPreferences();
+ return bloc;
+ },
),
// BlocProvider(
// create: (final _) => TtsSettingsBloc(),
diff --git a/flutter_readium/example/lib/pages/bookshelf.page.dart b/flutter_readium/example/lib/pages/bookshelf.page.dart
index f80e15e..1717105 100644
--- a/flutter_readium/example/lib/pages/bookshelf.page.dart
+++ b/flutter_readium/example/lib/pages/bookshelf.page.dart
@@ -1,7 +1,9 @@
+import 'dart:convert';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -19,25 +21,13 @@ class BookshelfPage extends StatefulWidget {
class BookshelfPageState extends State {
final _flutterReadiumPlugin = FlutterReadium();
+ final ScrollController _scrollController = ScrollController();
List _testPublications = [];
bool _isLoading = true;
// Pubs loaded from assets folder should not be delete-able as they would just be re-added on restart
// we should probably make it so they will only be loaded once
final List _identifiersFromAsset = ['dk-nota-714304'];
- // TODO: find a better solution for initialLocator.
- final initialLocator = Locator(
- href: '/OPS/main2.xml',
- title: 'Test',
- // locations: Locations(cssSelector: '#chapter_245810 > div > p:nth-child(19)'),
- locations: Locations(
- progression: 0.38461538461538464,
- position: 26,
- totalProgression: 0.176056338028169,
- ),
- type: 'text/html',
- );
-
@override
void initState() {
super.initState();
@@ -45,14 +35,33 @@ class BookshelfPageState extends State {
}
Future _initialize() async {
- // should only be done first time app is started. how to do that?
- final localPublications = await PublicationUtils.moveAssetPublicationsToReadiumStorage();
final loadedPublications = [];
- for (String localPubPath in localPublications) {
- Publication? publication = await openPublicationFromUrl(localPubPath);
- if (publication != null) {
- loadedPublications.add(publication);
+ if (kIsWeb) {
+ // Web: Load publications from JSON asset
+ final String response = await rootBundle.loadString('assets/webManifestList.json');
+ final List manifestHrefs = json.decode(response);
+ for (final href in manifestHrefs) {
+ try {
+ Publication? pub;
+ pub = await openPublicationFromUrl(href.toString());
+ if (pub != null) {
+ loadedPublications.add(pub);
+ await _flutterReadiumPlugin.closePublication(pub.identifier);
+ }
+ } on Exception catch (e) {
+ debugPrint('Error opening publication: $e');
+ }
+ }
+ } else {
+ // should only be done first time app is started. how to do that?
+ final localPublications = await PublicationUtils.moveAssetPublicationsToReadiumStorage();
+
+ for (String localPubPath in localPublications) {
+ Publication? publication = await openPublicationFromUrl(localPubPath);
+ if (publication != null) {
+ loadedPublications.add(publication);
+ }
}
}
@@ -64,7 +73,9 @@ class BookshelfPageState extends State {
Future openPublicationFromUrl(String pubUrl) async {
try {
- Publication pub = await _flutterReadiumPlugin.openPublication(pubUrl);
+ Publication pub = kIsWeb
+ ? await _flutterReadiumPlugin.getPublication(pubUrl)
+ : await _flutterReadiumPlugin.openPublication(pubUrl);
debugPrint('openPublication success: ${pub.metadata.title}');
return pub;
} on PlatformException catch (e) {
@@ -73,6 +84,12 @@ class BookshelfPageState extends State {
}
}
+ @override
+ void dispose() {
+ _scrollController.dispose();
+ super.dispose();
+ }
+
@override
Widget build(final BuildContext context) => Scaffold(
restorationId: 'bookshelf_page',
@@ -87,9 +104,11 @@ class BookshelfPageState extends State {
children: [
Expanded(
child: CupertinoScrollbar(
+ controller: _scrollController,
thickness: 5.0,
thumbVisibility: true,
child: ListView.builder(
+ controller: _scrollController,
itemCount: _testPublications.length,
itemBuilder: (final context, final index) {
final publication = _testPublications[index];
diff --git a/flutter_readium/example/lib/pages/player.page.dart b/flutter_readium/example/lib/pages/player.page.dart
index c5a9022..1f8356f 100644
--- a/flutter_readium/example/lib/pages/player.page.dart
+++ b/flutter_readium/example/lib/pages/player.page.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_readium/flutter_readium.dart';
+import 'package:pointer_interceptor/pointer_interceptor.dart';
import '../state/index.dart';
import '../widgets/index.dart';
@@ -66,7 +67,7 @@ class _PlayerPageState extends State with RestorationMixin {
showModalBottomSheet(
context: context,
isScrollControlled: true,
- builder: (final context) => const TextSettingsWidget(),
+ builder: (final context) => PointerInterceptor(child: const TextSettingsWidget()),
);
},
tooltip: 'Open text style settings',
diff --git a/flutter_readium/example/lib/state/text_settings_bloc.dart b/flutter_readium/example/lib/state/text_settings_bloc.dart
index 65ea679..3b3161e 100644
--- a/flutter_readium/example/lib/state/text_settings_bloc.dart
+++ b/flutter_readium/example/lib/state/text_settings_bloc.dart
@@ -1,3 +1,4 @@
+import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_readium/flutter_readium.dart';
@@ -30,12 +31,14 @@ class TextSettingsState {
required this.fontSize,
required this.theme,
required this.highlight,
+ this.pageMargins,
});
bool verticalScroll;
int fontSize;
TextSettingsTheme theme;
TextSettingsTheme highlight;
+ double? pageMargins;
@override
String toString() =>
@@ -46,12 +49,14 @@ class TextSettingsState {
final int? fontSize,
final TextSettingsTheme? theme,
final TextSettingsTheme? highlight,
+ final double? pageMargins,
}) {
final newState = TextSettingsState(
verticalScroll: verticalScroll ?? this.verticalScroll,
fontSize: fontSize ?? this.fontSize,
theme: theme ?? this.theme,
highlight: highlight ?? this.highlight,
+ pageMargins: pageMargins ?? this.pageMargins,
);
return newState;
@@ -69,10 +74,24 @@ class TextSettingsBloc extends Bloc {
verticalScroll: state.verticalScroll,
backgroundColor: state.theme.backgroundColor,
textColor: state.theme.textColor,
+ pageMargins: state.pageMargins,
);
instance.setEPUBPreferences(epubPreferences);
}
+ void setDefaultPreferences() {
+ final defaultPreferences = EPUBPreferences(
+ fontFamily: 'Original',
+ fontSize: state.fontSize,
+ fontWeight: 1.0,
+ verticalScroll: state.verticalScroll,
+ backgroundColor: state.theme.backgroundColor,
+ textColor: state.theme.textColor,
+ pageMargins: state.pageMargins,
+ );
+ instance.setDefaultPreferences(defaultPreferences);
+ }
+
TextSettingsBloc()
: super(
TextSettingsState(
@@ -86,6 +105,7 @@ class TextSettingsBloc extends Bloc {
textColor: highlights[0].textColor,
backgroundColor: highlights[0].backgroundColor,
),
+ pageMargins: kIsWeb ? 35 : null,
),
) {
on((final event, final emit) {
diff --git a/flutter_readium/example/lib/widgets/reader.widget.dart b/flutter_readium/example/lib/widgets/reader.widget.dart
index 902e5db..284fc27 100644
--- a/flutter_readium/example/lib/widgets/reader.widget.dart
+++ b/flutter_readium/example/lib/widgets/reader.widget.dart
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_readium/flutter_readium.dart';
-import 'package:flutter_readium/reader_widget.dart';
+import 'package:flutter_readium/reader_widget_switch.dart';
import 'package:native_device_orientation/native_device_orientation.dart';
import '../state/index.dart';
@@ -63,7 +63,6 @@ class ReaderWidget extends StatelessWidget {
),
);
} else if (state.publication != null) {
- context.read().add(OpenPubSuccess());
return Semantics(
container: true,
explicitChildNodes: true,
diff --git a/flutter_readium/example/pubspec.lock b/flutter_readium/example/pubspec.lock
index 9c92c21..a47738e 100644
--- a/flutter_readium/example/pubspec.lock
+++ b/flutter_readium/example/pubspec.lock
@@ -445,6 +445,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.8"
+ pointer_interceptor:
+ dependency: "direct main"
+ description:
+ name: pointer_interceptor
+ sha256: "57210410680379aea8b1b7ed6ae0c3ad349bfd56fe845b8ea934a53344b9d523"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.10.1+2"
+ pointer_interceptor_ios:
+ dependency: transitive
+ description:
+ name: pointer_interceptor_ios
+ sha256: a6906772b3205b42c44614fcea28f818b1e5fdad73a4ca742a7bd49818d9c917
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.10.1"
+ pointer_interceptor_platform_interface:
+ dependency: transitive
+ description:
+ name: pointer_interceptor_platform_interface
+ sha256: "0597b0560e14354baeb23f8375cd612e8bd4841bf8306ecb71fcd0bb78552506"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.10.0+1"
+ pointer_interceptor_web:
+ dependency: transitive
+ description:
+ name: pointer_interceptor_web
+ sha256: "460b600e71de6fcea2b3d5f662c92293c049c4319e27f0829310e5a953b3ee2a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.10.3"
process:
dependency: transitive
description:
diff --git a/flutter_readium/example/pubspec.yaml b/flutter_readium/example/pubspec.yaml
index 23dd42e..9699b0e 100644
--- a/flutter_readium/example/pubspec.yaml
+++ b/flutter_readium/example/pubspec.yaml
@@ -1,5 +1,5 @@
name: flutter_readium_example
-description: "Demonstrates how to use the flutter_readium plugin."
+description: 'Demonstrates how to use the flutter_readium plugin.'
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
environment:
@@ -9,6 +9,7 @@ dependencies:
flutter:
sdk: flutter
+ cupertino_icons: ^1.0.8
file_picker:
flutter_bloc:
hydrated_bloc:
@@ -23,7 +24,7 @@ dependencies:
native_device_orientation:
path:
path_provider:
- cupertino_icons: ^1.0.8
+ pointer_interceptor:
dev_dependencies:
integration_test:
@@ -37,3 +38,4 @@ flutter:
disable-swift-package-manager: true
assets:
- assets/pubs/
+ - assets/webManifestList.json
diff --git a/flutter_readium/example/web/index.html b/flutter_readium/example/web/index.html
index 8f8e058..4aec8fc 100644
--- a/flutter_readium/example/web/index.html
+++ b/flutter_readium/example/web/index.html
@@ -1,7 +1,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- flutter_readium_example
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ flutter_readium_example
+
+
+
+
+
+
+
+
+
+
+