Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ jobs:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.41.2'
channel: stable
cache: true
- run: flutter pub get
- run: flutter pub get --enforce-lockfile
- run: bash tool/build_epub_worker.sh --check
- run: dart format --output=none --set-exit-if-changed lib test example/lib example/test tool
- run: flutter analyze
- run: flutter test
Expand All @@ -23,6 +25,15 @@ jobs:
working-directory: example
- run: flutter build web
working-directory: example
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm ci
working-directory: tool/browser
- run: npx playwright install --with-deps chromium
working-directory: tool/browser
- run: npm test
working-directory: tool/browser

desktop-build:
strategy:
Expand All @@ -39,11 +50,24 @@ jobs:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.41.2'
channel: stable
cache: true
- run: sudo apt-get update && sudo apt-get install -y libgtk-3-dev
if: matrix.target == 'linux'
- run: flutter pub get
working-directory: example
- run: flutter build ${{ matrix.target }}
if: matrix.target != 'macos'
working-directory: example
- run: flutter build macos --config-only --release
if: matrix.target == 'macos'
working-directory: example
# Flutter 3.41.2's universal build has an Xcode 27 architecture-check issue.
# Compile the runner architecture until flutter/flutter#188346 is fixed.
- run: >-
xcodebuild -workspace macos/Runner.xcworkspace -scheme Runner
-configuration Release -derivedDataPath build/macos -sdk macosx
ARCHS=$(uname -m) ONLY_ACTIVE_ARCH=YES CODE_SIGNING_ALLOWED=NO build
if: matrix.target == 'macos'
working-directory: example
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ migrate_working_dir/
#.vscode/

# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
# This private package commits its lockfile to reproduce the packaged worker.
**/doc/api/
.dart_tool/
.flutter-plugins-dependencies
/build/
/artifacts/
node_modules/
/coverage/
33 changes: 33 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Papyrus reader

This is an independent Flutter package and Git repository. The owning workspace
is one level up; use its `tools/flutter` and `tools/dart` SDK wrappers when present.
Read `docs/reader-plan.md` before changing architecture or expanding formats.

- Domain types are host-facing and serializable. Keep version-1 locators readable.
EPUB content offsets are chapter-local normalized UTF-16 offsets, never page
numbers. Legacy CFI strings are compatibility data, not conformant anchors.
- The host owns bytes, persistence, routing and account/profile isolation.
Package engines own transient resources and must release them on cancellation.
- EPUB document processing is pure Dart in `lib/src/engine/epub/worker`. Native
isolates and browser workers use the same request/response protocol. Never
move ZIP, XML or chapter HTML parsing back onto the Flutter UI thread.
- Rebuild `assets/epub_worker.js` with `tool/build_epub_worker.sh` after modifying
worker source. The packaged JS lets dependent applications build without a
manual worker generation step. CI checks the generated asset for drift.
This private repository commits `pubspec.lock` and pins its CI SDK so the
browser worker and third-party notices are reproducible.
- UI text measurement belongs in Flutter. Preserve semantic runs, Unicode,
illustrations and content offsets when reflowing. Layout must use the space
left after panels, system insets and text scaling, not device width alone.
- Keep custom engines/renderers/builders usable. Don't silently expose settings
that the active engine cannot apply.
- Use in-process transport in widget tests with fake clocks; exercise native and
browser transports independently. Run `flutter analyze`, `flutter test`, and
example checks. Build web for conditional-import and worker changes.
- Client's Git-pinned reader is independent. Use an ignored local path override
for integration checks; update its release pin only to a committed revision.

Native example Podfiles are intentional platform setup. Generated build/cache
folders are not source. Keep performance claims limited to measurements actually
made; synthetic tests do not certify arbitrary publisher content or every device.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Changelog

## Unreleased

- Rebuild reader Material colors consistently, including popup backgrounds and
host-defined field labels, while preserving typography, geometry and motion.
- Process EPUB archives and chapters in cancellable native/browser workers.
- Follow OPF spine order independently of TOC hierarchy and restore TOC anchors.
- Add rich lazy pagination, adaptive spreads and page-first navigation.
- Preserve EPUB content offsets through typography and viewport changes.
- Keep reading viewports mounted during progress updates; add typeface controls,
consistent appearance and escapable loading/error screens.
- Share one pdfrx document between metadata and rendering; report PDF page offsets.
- Fit and turn bounded PDF pages/spreads in paginated mode; preserve scale on
appearance changes and refit after column, mode and viewport changes.
- Retain keyboard navigation across EPUB chapters and correct reader settings
colors under opposite host themes and in open mobile sheets.
- Add a long-chapter reading lab, architecture plan and platform integration guide.

## 0.0.1

- Add reflowable EPUB 2/3 and PDF engines.
Expand Down
49 changes: 37 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
`papyrus_reader` is a self-contained Flutter library for reading reflowable
EPUB 2/3 and PDF documents on Android, iOS, Web, Linux, macOS, and Windows.

It provides EPUB CFI locations, PDF page locations, TOC/outline navigation,
It provides versioned EPUB content locations, PDF page locations, TOC/outline navigation,
scroll and paginated layouts, configurable appearance, and a responsive
Material 3 reader shell. UI builders and theme data allow Papyrus to replace
the default chrome without forking the engines.
the default chrome without forking the engines. EPUB processing runs in a native
isolate or browser Worker; rich pagination preserves content across typography
changes and turns within chapters before crossing the document spine.

The package deliberately has no Papyrus API, PowerSync, filesystem, routing,
or state-management dependency. The host owns document bytes and persistence.
Expand All @@ -20,14 +22,17 @@ dependencies:
```

```dart
// Create once per book session and retain across host rebuilds.
final document = ReaderDocument(
id: book.id,
title: book.title,
author: book.author,
format: ReaderFormat.epub,
loadBytes: () => file.readAsBytes(),
);

PapyrusReader(
document: ReaderDocument(
id: book.id,
title: book.title,
author: book.author,
format: ReaderFormat.epub,
loadBytes: () => file.readAsBytes(),
),
document: document,
initialLocator: savedLocator,
initialPreferences: savedPreferences,
onLocatorChanged: saveLocator,
Expand All @@ -41,15 +46,24 @@ provided `ReaderController` remains host-owned. With an external controller,
omitted initial preferences remain controller-owned; an explicit value
overrides them for the document load.

Use **Hide controls** in the toolbar for distraction-free reading. Both bars
collapse, expanding the page area without reopening the document. Page-turn
keys, EPUB swipes and PDF scrolling remain available. The small **Show controls**
button in the top corner or **Escape** restores the bars. This is temporary UI
state; opening another document restores the controls. Custom toolbar builders
can expose `ReaderToolbarContext.toggleControls`.

## Example

```bash
cd example
flutter run
```

The six-platform example includes deterministic EPUB/PDF assets, light and
dark themes, and in-memory position/preference restoration.
The six-platform example includes deterministic long EPUB and three-page PDF
assets, a local EPUB/PDF file picker, light/dark themes, and in-memory
position/preference restoration. Example positions reset when the app restarts;
production persistence belongs to the host application.

## Current scope

Expand All @@ -58,6 +72,7 @@ return `ReaderErrorCode.unsupportedFixedLayout`. MOBI/AZW3, TXT, comic
archives, search, bookmarks, highlights, and notes are planned extensions.

- [Architecture](docs/architecture.md)
- [Core release plan](docs/reader-plan.md)
- [Client integration](docs/integration.md)
- [Supported formats](docs/supported-formats.md)

Expand All @@ -66,7 +81,17 @@ archives, search, bookmarks, highlights, and notes are planned extensions.
```bash
flutter analyze
flutter test
cd example && flutter analyze && flutter test && flutter build web
./tool/build_epub_worker.sh
(cd example && flutter analyze && flutter test && flutter build web)
# From the reader repository, after building the example:
(cd tool/browser && npm ci && npx playwright install chromium && npm test)
```

Use the parent workspace's pinned SDK wrappers when available. The generated
browser worker is packaged so host applications need no extra build step; rebuild
it after changing worker source. Browser CSP must allow `worker-src blob:`.
Use `tool/build_epub_worker.sh --check` to check asset drift. Browser checks can
use an installed Chrome via `PAPYRUS_CHROME_EXECUTABLE` and save screenshots via
`PAPYRUS_SCREENSHOT_DIR`. See [Validation](docs/validation.md) for platform coverage.

Licensed under AGPL-3.0.
Loading
Loading