mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 07:47:24 +00:00
export comic as pdf
This commit is contained in:
@@ -244,7 +244,8 @@
|
|||||||
"Deleted @a favorite items.": "已删除 @a 条无效收藏",
|
"Deleted @a favorite items.": "已删除 @a 条无效收藏",
|
||||||
"New version available": "有新版本可用",
|
"New version available": "有新版本可用",
|
||||||
"A new version is available. Do you want to update now?" : "有新版本可用。您要现在更新吗?",
|
"A new version is available. Do you want to update now?" : "有新版本可用。您要现在更新吗?",
|
||||||
"No new version available": "没有新版本可用"
|
"No new version available": "没有新版本可用",
|
||||||
|
"Export as pdf": "导出为pdf"
|
||||||
},
|
},
|
||||||
"zh_TW": {
|
"zh_TW": {
|
||||||
"Home": "首頁",
|
"Home": "首頁",
|
||||||
@@ -491,6 +492,7 @@
|
|||||||
"Deleted @a favorite items.": "已刪除 @a 條無效收藏",
|
"Deleted @a favorite items.": "已刪除 @a 條無效收藏",
|
||||||
"New version available": "有新版本可用",
|
"New version available": "有新版本可用",
|
||||||
"A new version is available. Do you want to update now?" : "有新版本可用。您要現在更新嗎?",
|
"A new version is available. Do you want to update now?" : "有新版本可用。您要現在更新嗎?",
|
||||||
"No new version available": "沒有新版本可用"
|
"No new version available": "沒有新版本可用",
|
||||||
|
"Export as pdf": "匯出為pdf"
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -76,7 +76,7 @@ class LocalComic with HistoryMixin implements Comic {
|
|||||||
cover,
|
cover,
|
||||||
));
|
));
|
||||||
|
|
||||||
String get baseDir => directory.contains("/") ? directory : FilePath.join(LocalManager().path, directory);
|
String get baseDir => (directory.contains('/') || directory.contains('\\')) ? directory : FilePath.join(LocalManager().path, directory);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get description => "";
|
String get description => "";
|
||||||
|
@@ -4,9 +4,11 @@ import 'package:venera/foundation/app.dart';
|
|||||||
import 'package:venera/foundation/appdata.dart';
|
import 'package:venera/foundation/appdata.dart';
|
||||||
import 'package:venera/foundation/comic_source/comic_source.dart';
|
import 'package:venera/foundation/comic_source/comic_source.dart';
|
||||||
import 'package:venera/foundation/local.dart';
|
import 'package:venera/foundation/local.dart';
|
||||||
|
import 'package:venera/foundation/log.dart';
|
||||||
import 'package:venera/pages/downloading_page.dart';
|
import 'package:venera/pages/downloading_page.dart';
|
||||||
import 'package:venera/utils/cbz.dart';
|
import 'package:venera/utils/cbz.dart';
|
||||||
import 'package:venera/utils/io.dart';
|
import 'package:venera/utils/io.dart';
|
||||||
|
import 'package:venera/utils/pdf.dart';
|
||||||
import 'package:venera/utils/translations.dart';
|
import 'package:venera/utils/translations.dart';
|
||||||
|
|
||||||
class LocalComicsPage extends StatefulWidget {
|
class LocalComicsPage extends StatefulWidget {
|
||||||
@@ -299,8 +301,7 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
|
|||||||
return ContentDialog(
|
return ContentDialog(
|
||||||
title: "Delete".tl,
|
title: "Delete".tl,
|
||||||
content: CheckboxListTile(
|
content: CheckboxListTile(
|
||||||
title:
|
title: Text("Also remove files on disk".tl),
|
||||||
Text("Also remove files on disk".tl),
|
|
||||||
value: removeComicFile,
|
value: removeComicFile,
|
||||||
onChanged: (v) {
|
onChanged: (v) {
|
||||||
state(() {
|
state(() {
|
||||||
@@ -361,6 +362,34 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
|
|||||||
}
|
}
|
||||||
controller.close();
|
controller.close();
|
||||||
}),
|
}),
|
||||||
|
if (!multiSelectMode)
|
||||||
|
MenuEntry(
|
||||||
|
icon: Icons.picture_as_pdf_outlined,
|
||||||
|
text: "Export as pdf".tl,
|
||||||
|
onClick: () async {
|
||||||
|
var cache = FilePath.join(App.cachePath, 'temp.pdf');
|
||||||
|
var controller = showLoadingDialog(
|
||||||
|
context,
|
||||||
|
allowCancel: false,
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
await createPdfFromComicIsolate(
|
||||||
|
comic: c as LocalComic,
|
||||||
|
savePath: cache,
|
||||||
|
);
|
||||||
|
await saveFile(
|
||||||
|
file: File(cache),
|
||||||
|
filename: "${c.title}.pdf",
|
||||||
|
);
|
||||||
|
} catch (e, s) {
|
||||||
|
Log.error("PDF Export", e, s);
|
||||||
|
context.showMessage(message: e.toString());
|
||||||
|
} finally {
|
||||||
|
controller.close();
|
||||||
|
File(cache).deleteIgnoreError();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@@ -360,8 +360,8 @@ class _IOOverrides extends IOOverrides {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void overrideIO(void Function() f) {
|
T overrideIO<T>(T Function() f) {
|
||||||
IOOverrides.runWithIOOverrides(
|
return IOOverrides.runWithIOOverrides<T>(
|
||||||
f,
|
f,
|
||||||
_IOOverrides(),
|
_IOOverrides(),
|
||||||
);
|
);
|
||||||
|
92
lib/utils/pdf.dart
Normal file
92
lib/utils/pdf.dart
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import 'dart:isolate';
|
||||||
|
|
||||||
|
import 'package:pdf/widgets.dart';
|
||||||
|
import 'package:venera/foundation/local.dart';
|
||||||
|
import 'package:venera/utils/io.dart';
|
||||||
|
|
||||||
|
Future<void> _createPdfFromComic({
|
||||||
|
required LocalComic comic,
|
||||||
|
required String savePath,
|
||||||
|
required String localPath,
|
||||||
|
}) async {
|
||||||
|
final pdf = Document(
|
||||||
|
title: comic.title,
|
||||||
|
author: comic.subTitle ?? "",
|
||||||
|
producer: "Venera",
|
||||||
|
);
|
||||||
|
|
||||||
|
pdf.document.outline;
|
||||||
|
|
||||||
|
var baseDir = comic.directory.contains('/') || comic.directory.contains('\\')
|
||||||
|
? comic.directory
|
||||||
|
: FilePath.join(localPath, comic.directory);
|
||||||
|
|
||||||
|
// add cover
|
||||||
|
var imageData = File(FilePath.join(baseDir, comic.cover)).readAsBytesSync();
|
||||||
|
pdf.addPage(Page(
|
||||||
|
build: (Context context) {
|
||||||
|
return Image(MemoryImage(imageData), fit: BoxFit.contain);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
bool multiChapters = comic.chapters != null;
|
||||||
|
|
||||||
|
void reorderFiles(List<FileSystemEntity> files) {
|
||||||
|
files.removeWhere(
|
||||||
|
(element) => element is! File || element.path.startsWith('cover'));
|
||||||
|
files.sort((a, b) {
|
||||||
|
var aName = (a as File).name;
|
||||||
|
var bName = (b as File).name;
|
||||||
|
var aNumber = int.tryParse(aName);
|
||||||
|
var bNumber = int.tryParse(bName);
|
||||||
|
if (aNumber != null && bNumber != null) {
|
||||||
|
return aNumber.compareTo(bNumber);
|
||||||
|
}
|
||||||
|
return aName.compareTo(bName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!multiChapters) {
|
||||||
|
var files = Directory(baseDir).listSync();
|
||||||
|
reorderFiles(files);
|
||||||
|
|
||||||
|
for (var file in files) {
|
||||||
|
var imageData = (file as File).readAsBytesSync();
|
||||||
|
pdf.addPage(Page(
|
||||||
|
build: (Context context) {
|
||||||
|
return Image(MemoryImage(imageData), fit: BoxFit.contain);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (var chapter in comic.chapters!.keys) {
|
||||||
|
var files = Directory(FilePath.join(baseDir, chapter)).listSync();
|
||||||
|
reorderFiles(files);
|
||||||
|
for (var file in files) {
|
||||||
|
var imageData = (file as File).readAsBytesSync();
|
||||||
|
pdf.addPage(Page(
|
||||||
|
build: (Context context) {
|
||||||
|
return Image(MemoryImage(imageData), fit: BoxFit.contain);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final file = File(savePath);
|
||||||
|
file.writeAsBytesSync(await pdf.save());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> createPdfFromComicIsolate({
|
||||||
|
required LocalComic comic,
|
||||||
|
required String savePath,
|
||||||
|
}) async {
|
||||||
|
var localPath = LocalManager().path;
|
||||||
|
return Isolate.run(() => overrideIO(() async {
|
||||||
|
return await _createPdfFromComic(
|
||||||
|
comic: comic,
|
||||||
|
savePath: savePath,
|
||||||
|
localPath: localPath,
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
}
|
56
pubspec.lock
56
pubspec.lock
@@ -33,6 +33,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.4"
|
||||||
|
archive:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: archive
|
||||||
|
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.6.1"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -49,6 +57,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.11.0"
|
version: "2.11.0"
|
||||||
|
barcode:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: barcode
|
||||||
|
sha256: ab180ce22c6555d77d45f0178a523669db67f95856e3378259ef2ffeb43e6003
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.8"
|
||||||
battery_plus:
|
battery_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -65,6 +81,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
bidi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: bidi
|
||||||
|
sha256: "9a712c7ddf708f7c41b1923aa83648a3ed44cfd75b04f72d598c45e5be287f9d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.12"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -465,6 +489,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.2"
|
||||||
|
image:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image
|
||||||
|
sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.3.0"
|
||||||
intl:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -626,6 +658,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.0"
|
version: "1.9.0"
|
||||||
|
path_parsing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_parsing
|
||||||
|
sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -674,6 +714,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
|
pdf:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: pdf
|
||||||
|
sha256: "05df53f8791587402493ac97b9869d3824eccbc77d97855f4545cf72df3cae07"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.11.1"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -715,6 +763,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.9.1"
|
version: "3.9.1"
|
||||||
|
qr:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: qr
|
||||||
|
sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
rhttp:
|
rhttp:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@@ -69,6 +69,7 @@ dependencies:
|
|||||||
git:
|
git:
|
||||||
url: https://github.com/pkuislm/flutter_saf.git
|
url: https://github.com/pkuislm/flutter_saf.git
|
||||||
ref: dd5242918da0ea9a0a50b0f87ade7a2def65453d
|
ref: dd5242918da0ea9a0a50b0f87ade7a2def65453d
|
||||||
|
pdf: ^3.11.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user