Skip to content

Commit

Permalink
refine Book members to:
Browse files Browse the repository at this point in the history
- metadata
- spines
- manifest
- navigation document
  • Loading branch information
pedia committed Nov 6, 2024
1 parent c164d80 commit a75cfd0
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 115 deletions.
80 changes: 46 additions & 34 deletions epub3/lib/src/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -179,25 +179,22 @@ class ItemRef {
}
}

class Manifest {
class Package {
final Version version;
final Metadata metadata;
final List<Item> items;
final Manifest manifest;
final Spine spine;
// TODO: guide
Manifest({
required this.version,
required this.metadata,
required this.items,
required this.spine,
});
Package(this.version, this.metadata, this.manifest, this.spine);
}

String get identifier => metadata.identifier.first.identifier;
class Manifest {
final List<Item> items;
Manifest(this.items);

String tocFile() {
String tocFile(Version version) {
Item? entry;
if (version == Version.epub2) {
entry = items.where((element) => element.id == spine.toc).firstOrNull;
entry = items.where((element) => element.id == 'ncx').firstOrNull;
} else if (version == Version.epub3) {
// TODO: this is better?
// id="nav" first
Expand All @@ -215,14 +212,19 @@ class Manifest {
}
return p.join('', entry?.href ?? '');
}

Item? find(String id) {
return items.firstWhere((i) => i.id == id);
}
}

class Chapter {
final String title;
final String? href;
final List<Chapter> children;

/// raw content, always content of html file
final String? content;
String? content;

/// TODO: extract text from html document
String get text => '';
Expand All @@ -232,7 +234,7 @@ class Chapter {
(prev, c) => prev + c.chapterCount,
);

const Chapter({
Chapter({
required this.title,
this.href,
this.children = const [],
Expand All @@ -242,7 +244,10 @@ class Chapter {
factory Chapter.textContent(String title, String text) {
final href = title; // TODO:
return Chapter(
title: title, href: href, content: Chapter.toHtml(title, text));
title: title,
href: href,
content: Chapter.toHtml(title, text),
children: []);
}

// transform text to HTML
Expand Down Expand Up @@ -326,20 +331,24 @@ abstract class ContentReader {
}

class Book {
final Version version;
final Manifest manifest;
final Metadata metadata;
final Spine spine;
final Navigation navigation;

/// Read content later
final ContentReader? reader;
Book(this.manifest, this.navigation, this.reader);
Book(this.version, this.manifest, this.metadata, this.spine, this.navigation,
this.reader);

factory Book.create(
{required String title, required String author, ContentReader? reader}) {
return Book(
Manifest(
version: Version.epub3,
metadata: Metadata.create(title, author),
items: [],
spine: Spine.empty(),
),
Version.epub3,
Manifest([]),
Metadata.create(title, author),
Spine.empty(),
Navigation(title: title, author: author, chapters: []),
reader,
);
Expand All @@ -350,21 +359,20 @@ class Book {
manifest.items.addAll(chapter.items);
}

Version get version => manifest.version;
String get identifier => metadata.identifier.first.identifier;

String get title {
if (navigation.title != null) {
return navigation.title!;
}
return manifest.metadata.title.isNotEmpty ? manifest.metadata.title[0] : '';
return metadata.title.isNotEmpty ? metadata.title[0] : '';
}

String get author {
if (navigation.author != null) {
return navigation.author!;
}
return manifest.metadata.creator.isNotEmpty
? manifest.metadata.creator[0].creator
: '';
return metadata.creator.isNotEmpty ? metadata.creator[0].creator : '';
}

String? get cover =>
Expand All @@ -380,7 +388,7 @@ class Book {
Item? get nav {
if (version == Version.epub2) {
return manifest.items
.where((element) => element.id == manifest.spine.toc)
.where((element) => element.id == spine.toc)
.firstOrNull;
} else if (version == Version.epub3) {
return manifest.items
Expand All @@ -390,14 +398,18 @@ class Book {
return null;
}

String? toHref(String id) {
return manifest.items.where((i) => i.id == id).firstOrNull?.href;
}

String pathOf(String href) {
final pos = href.indexOf('#');
final path = pos == -1 ? href : href.substring(0, pos);
return p.join(p.dirname(manifest.tocFile()), path);
String path = pos == -1 ? href : href.substring(0, pos);

// id, not file
if (p.extension(path).isEmpty) {
final item = manifest.find(path);
if (item != null) {
path = item.href!;
}
}
return p.join(p.dirname(manifest.tocFile(version)), path);
}

String? readString(String href) {
Expand Down
56 changes: 34 additions & 22 deletions epub3/lib/src/reader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import 'package:xml/xpath.dart' as xml;

import 'model.dart';

/// [Reader] read scheme and content from epub file.
/// [Reader] read `package document` and content from zip file.
class Reader extends ContentReader {
factory Reader.open(Archive archive) {
final rootFile = extractRootFileName(archive);
Expand All @@ -23,21 +23,31 @@ class Reader extends ContentReader {

static const opfNS = 'http://www.idpf.org/2007/opf';

Book? read() {
final scheme = readSchema(rootFile);
if (scheme == null) {
Book? read({bool extractContent = false}) {
final package = parsePackage(rootFile);
if (package == null) {
return null;
}

final navfile = pathOf(scheme.tocFile());
final nav = scheme.version == Version.epub2
final navfile = package.manifest.tocFile(package.version);
final nav = package.version == Version.epub2
? readNavigation2(navfile)
: readNavigation3(navfile);
return Book(scheme, nav ?? Navigation(chapters: []), this);
final book = Book(package.version, package.manifest, package.metadata,
package.spine, nav ?? Navigation(chapters: []), this);

if (extractContent) {
book.chapters.forEach((ch) => _loadContent(book, ch));
}
return book;
}

/// Relative path by rootfile
String pathOf(String fp) => p.join(p.dirname(rootFile), fp);
void _loadContent(Book book, Chapter ch) {
ch.content = book.readString(ch.href!);
for (Chapter sub in ch.children) {
_loadContent(book, sub);
}
}

static const containerFN = 'META-INF/container.xml';

Expand All @@ -59,8 +69,8 @@ class Reader extends ContentReader {
return null;
}

/// Read [Manifest] from OEBPS/content.opf
Manifest? readSchema(String path) {
/// Parse Package document normal as OEBPS/content.opf
Package? parsePackage(String path) {
// root element, <package>
var doc = _readAsXml(path);
if (doc == null) return null;
Expand All @@ -76,13 +86,13 @@ class Reader extends ContentReader {
throw Exception('Unsupported EPUB version: $vs.');
}

return Manifest(
version: version,
metadata: _extractMetadata(
return Package(
version,
_extractMetadata(
doc.findElements('metadata', namespace: opfNS).first, version),
items: _extractManifest(
doc.findElements('manifest', namespace: opfNS).first),
spine: _extractSpine(doc.findElements('spine', namespace: opfNS).first),
Manifest(_extractManifest(
doc.findElements('manifest', namespace: opfNS).first)),
_extractSpine(doc.findElements('spine', namespace: opfNS).first),
);
}

Expand Down Expand Up @@ -212,7 +222,7 @@ class Reader extends ContentReader {
.toList();

Navigation? readNavigation2(String path) {
final doc = _readAsXml(path);
final doc = _readAsXml(pathOf(path));
if (doc == null) return null;

return _readNav2(doc);
Expand Down Expand Up @@ -242,7 +252,7 @@ class Reader extends ContentReader {
/// path maybe "EPUB/xhtml/epub30-nav.xhtml"
/// nav / ol / li / span|a
Navigation? readNavigation3(String path) {
final doc = _readAsXml(path);
final doc = _readAsXml(pathOf(path));
if (doc == null) return null;

if (doc.localName == 'ncx') {
Expand All @@ -269,23 +279,25 @@ class Reader extends ContentReader {
final a = li.findElements('a').firstOrNull;

final id = li.getAttribute('id');
final href = id == null ? id : a?.getAttribute('href');
final href = id != null ? id : a?.getAttribute('href');

return Chapter(
title: clean(span != null ? span.innerText : a?.innerText ?? ''),
href: href,
children: _readOl(li),
// TODO: content #2
);
}

/// Relative path by rootfile
String pathOf(String fp) => p.join(p.dirname(rootFile), fp);

/// Read an [ArchiveFile]
ArchiveFile? readFile(String path) {
assert(path.indexOf('#') == -1);

// pathOf for relative path
// normalize for avoid ./
path = p.normalize(pathOf(path));
path = pathOf(p.normalize(path));

return archive.files
.firstWhereOrNull((ArchiveFile file) => file.name == path);
Expand Down
6 changes: 3 additions & 3 deletions epub3/lib/src/writer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class Writer {
final out = xml.XmlBuilder();
out.processing('xml', 'version="1.0"');

final uid = manifest.metadata.identifier.first.id;
final uid = book.metadata.identifier.first.id;
out.element(
'package',
namespaces: {Reader.opfNS: null},
Expand All @@ -86,7 +86,7 @@ class Writer {
'identifier',
namespace: dcuri,
attributes: {'id': uid}, // same id as [1]
nest: manifest.metadata.identifier.first.identifier,
nest: book.metadata.identifier.first.identifier,
);
out.element('title', namespace: dcuri, nest: book.title);
out.element('language', namespace: dcuri, nest: 'en');
Expand Down Expand Up @@ -116,7 +116,7 @@ class Writer {

out.element('spine', // attributes: {'toc': manifest.spine.toc},
nest: () {
for (var i in manifest.spine.refs) {
for (var i in book.spine.refs) {
out.element('itemref', attributes: i.attributes);
}
out.element('itemref', attributes: {'idref': 'nav'});
Expand Down
2 changes: 1 addition & 1 deletion epub3/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: epub3
description: Read and Write Epub3 files.
homepage: https://github.com/pedia/epub3
version: 0.2.2
version: 0.3.0

environment:
sdk: '>=2.14.0 <4.0.0'
Expand Down
Loading

0 comments on commit a75cfd0

Please sign in to comment.