Compare commits

...

5 Commits

Author SHA1 Message Date
nyne
2d27f7d650 Merge pull request #541 from venera-app/v1.5.2-dev
V1.5.2
2025-10-08 20:06:56 +08:00
e1fbdfbd50 Update version code 2025-10-07 16:10:14 +08:00
0a5b70b161 Add font patching script for linux arm64. Close #468 2025-10-07 16:09:30 +08:00
nyne
5a76a10fb2 Merge pull request #537 from lings03/master
Fix some issue when save or share image in reader.
2025-10-07 15:21:50 +08:00
角砂糖
f09e766a8a Fix some issue when save or share image in reader.
1. Change the image name with comic name and real index
2. Fix wrong equal check
3. Fix wrong selection when image per page > 1 and show single image in first page
2025-10-07 01:19:59 +08:00
8 changed files with 132 additions and 37 deletions

View File

@@ -170,6 +170,9 @@ jobs:
sudo apt-get update -y sudo apt-get update -y
sudo apt-get install -y ninja-build libgtk-3-dev webkit2gtk-4.1 sudo apt-get install -y ninja-build libgtk-3-dev webkit2gtk-4.1
dart pub global activate flutter_to_debian dart pub global activate flutter_to_debian
- name: "Patch font"
run: |
dart run patch/font.dart
- run: python3 debian/build.py arm64 - run: python3 debian/build.py arm64
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:

View File

@@ -13,7 +13,7 @@ export "widget_utils.dart";
export "context.dart"; export "context.dart";
class _App { class _App {
final version = "1.5.1"; final version = "1.5.2";
bool get isAndroid => Platform.isAndroid; bool get isAndroid => Platform.isAndroid;

View File

@@ -140,12 +140,12 @@ class _GalleryModeState extends State<_GalleryMode>
int get totalPages { int get totalPages {
if (!reader.showSingleImageOnFirstPage()) { if (!reader.showSingleImageOnFirstPage()) {
return (reader.images!.length / return (reader.images!.length /
reader.imagesPerPage()) reader.imagesPerPage)
.ceil(); .ceil();
} else { } else {
return 1 + return 1 +
((reader.images!.length - 1) / ((reader.images!.length - 1) /
reader.imagesPerPage()) reader.imagesPerPage)
.ceil(); .ceil();
} }
} }
@@ -169,7 +169,7 @@ class _GalleryModeState extends State<_GalleryMode>
/// Get the range of images for the given page. [page] is 1-based. /// Get the range of images for the given page. [page] is 1-based.
(int start, int end) getPageImagesRange(int page) { (int start, int end) getPageImagesRange(int page) {
var imagesPerPage = reader.imagesPerPage(); var imagesPerPage = reader.imagesPerPage;
if (reader.showSingleImageOnFirstPage()) { if (reader.showSingleImageOnFirstPage()) {
if (page == 1) { if (page == 1) {
return (0, 1); return (0, 1);
@@ -191,6 +191,16 @@ class _GalleryModeState extends State<_GalleryMode>
} }
} }
/// Get the image indices for current page. Returns null if no images.
/// Returns a single index if only one image, or a range if multiple images.
(int, int)? getCurrentPageImageRange() {
if (reader.images == null || reader.images!.isEmpty) {
return null;
}
var (startIndex, endIndex) = getPageImagesRange(reader.page);
return (startIndex, endIndex);
}
/// [cache] is used to cache the images. /// [cache] is used to cache the images.
/// The count of images to cache is determined by the [preCacheCount] setting. /// The count of images to cache is determined by the [preCacheCount] setting.
/// For previous page and next page, it will do a memory cache. /// For previous page and next page, it will do a memory cache.
@@ -259,7 +269,7 @@ class _GalleryModeState extends State<_GalleryMode>
photoViewControllers[index] ??= PhotoViewController(); photoViewControllers[index] ??= PhotoViewController();
if (reader.imagesPerPage() == 1 || if (reader.imagesPerPage == 1 ||
pageImages.length == 1) { pageImages.length == 1) {
return PhotoViewGalleryPageOptions( return PhotoViewGalleryPageOptions(
filterQuality: FilterQuality.medium, filterQuality: FilterQuality.medium,
@@ -533,17 +543,27 @@ class _GalleryModeState extends State<_GalleryMode>
@override @override
String? getImageKeyByOffset(Offset offset) { String? getImageKeyByOffset(Offset offset) {
String? imageKey; var range = getCurrentPageImageRange();
if (reader.imagesPerPage() == 1) { if (range == null) return null;
imageKey = reader.images![reader.page - 1];
} else { var (startIndex, endIndex) = range;
for (var imageState in imageStates) { int actualImageCount = endIndex - startIndex;
if ((imageState as _ComicImageState).containsPoint(offset)) {
imageKey = (imageState.widget.image as ReaderImageProvider).imageKey; if (actualImageCount == 1) {
return reader.images![startIndex];
}
for (var imageState in imageStates) {
if ((imageState as _ComicImageState).containsPoint(offset)) {
var imageKey = (imageState.widget.image as ReaderImageProvider).imageKey;
int index = reader.images!.indexOf(imageKey);
if (index >= startIndex && index < endIndex) {
return imageKey;
} }
} }
} }
return imageKey;
return reader.images![startIndex];
} }
} }

View File

@@ -116,9 +116,9 @@ class _ReaderState extends State<Reader>
return 1; return 1;
} }
if (!showSingleImageOnFirstPage()) { if (!showSingleImageOnFirstPage()) {
return (images!.length / imagesPerPage()).ceil(); return (images!.length / imagesPerPage).ceil();
} else { } else {
return 1 + ((images!.length - 1) / imagesPerPage()).ceil(); return 1 + ((images!.length - 1) / imagesPerPage).ceil();
} }
} }
@@ -277,13 +277,13 @@ class _ReaderState extends State<Reader>
history!.page = images?.length ?? 1; history!.page = images?.length ?? 1;
} else { } else {
/// Record the first image of the page /// Record the first image of the page
if (!showSingleImageOnFirstPage() || imagesPerPage() == 1) { if (!showSingleImageOnFirstPage() || imagesPerPage == 1) {
history!.page = (page - 1) * imagesPerPage() + 1; history!.page = (page - 1) * imagesPerPage + 1;
} else { } else {
if (page == 1) { if (page == 1) {
history!.page = 1; history!.page = 1;
} else { } else {
history!.page = (page - 2) * imagesPerPage() + 2; history!.page = (page - 2) * imagesPerPage + 2;
} }
} }
} }
@@ -371,13 +371,13 @@ abstract mixin class _ImagePerPageHandler {
ComicType get type; ComicType get type;
void initImagesPerPage(int initialPage) { void initImagesPerPage(int initialPage) {
_lastImagesPerPage = imagesPerPage(); _lastImagesPerPage = imagesPerPage;
_lastOrientation = isPortrait; _lastOrientation = isPortrait;
if (imagesPerPage() != 1) { if (imagesPerPage != 1) {
if (showSingleImageOnFirstPage()) { if (showSingleImageOnFirstPage()) {
page = ((initialPage - 1) / imagesPerPage()).ceil() + 1; page = ((initialPage - 1) / imagesPerPage).ceil() + 1;
} else { } else {
page = (initialPage / imagesPerPage()).ceil(); page = (initialPage / imagesPerPage).ceil();
} }
} }
} }
@@ -386,7 +386,7 @@ abstract mixin class _ImagePerPageHandler {
appdata.settings.getReaderSetting(cid, type.sourceKey, 'showSingleImageOnFirstPage'); appdata.settings.getReaderSetting(cid, type.sourceKey, 'showSingleImageOnFirstPage');
/// The number of images displayed on one screen /// The number of images displayed on one screen
int imagesPerPage() { int get imagesPerPage {
if (mode.isContinuous) return 1; if (mode.isContinuous) return 1;
if (isPortrait) { if (isPortrait) {
return appdata.settings.getReaderSetting(cid, type.sourceKey, 'readerScreenPicNumberForPortrait') ?? 1; return appdata.settings.getReaderSetting(cid, type.sourceKey, 'readerScreenPicNumberForPortrait') ?? 1;
@@ -397,7 +397,7 @@ abstract mixin class _ImagePerPageHandler {
/// Check if the number of images per page has changed /// Check if the number of images per page has changed
void _checkImagesPerPageChange() { void _checkImagesPerPageChange() {
int currentImagesPerPage = imagesPerPage(); int currentImagesPerPage = imagesPerPage;
bool currentOrientation = isPortrait; bool currentOrientation = isPortrait;
if (_lastImagesPerPage != currentImagesPerPage || _lastOrientation != currentOrientation) { if (_lastImagesPerPage != currentImagesPerPage || _lastOrientation != currentOrientation) {

View File

@@ -599,22 +599,24 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
} }
void saveCurrentImage() async { void saveCurrentImage() async {
var data = await selectImageToData(); var result = await selectImageToData();
if (data == null) { if (result == null) {
return; return;
} }
var (imageIndex, data) = result;
var fileType = detectFileType(data); var fileType = detectFileType(data);
var filename = "${context.reader.page}${fileType.ext}"; var filename = "${context.reader.widget.name}_${imageIndex + 1}${fileType.ext}";
saveFile(data: data, filename: filename); saveFile(data: data, filename: filename);
} }
void share() async { void share() async {
var data = await selectImageToData(); var result = await selectImageToData();
if (data == null) { if (result == null) {
return; return;
} }
var (imageIndex, data) = result;
var fileType = detectFileType(data); var fileType = detectFileType(data);
var filename = "${context.reader.page}${fileType.ext}"; var filename = "${context.reader.widget.name}_${imageIndex + 1}${fileType.ext}";
Share.shareFile(data: data, filename: filename, mime: fileType.mime); Share.shareFile(data: data, filename: filename, mime: fileType.mime);
} }
@@ -719,8 +721,29 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
Future<int?> selectImage() async { Future<int?> selectImage() async {
var reader = context.reader; var reader = context.reader;
var imageViewController = context.reader._imageViewController; var imageViewController = context.reader._imageViewController;
if (imageViewController is _GalleryModeState && reader.imagesPerPage == 1) {
return reader.page - 1; bool needsSelection = false;
int? singleImageIndex;
if (imageViewController is _GalleryModeState) {
var range = imageViewController.getCurrentPageImageRange();
if (range != null) {
var (startIndex, endIndex) = range;
int actualImageCount = endIndex - startIndex;
if (actualImageCount == 1) {
needsSelection = false;
singleImageIndex = startIndex;
} else {
needsSelection = true;
}
}
} else if (imageViewController is _ContinuousModeState) {
needsSelection = false;
singleImageIndex = reader.page - 1;
}
if (!needsSelection && singleImageIndex != null) {
return singleImageIndex;
} else { } else {
var location = await _showSelectImageOverlay(); var location = await _showSelectImageOverlay();
if (location == null) { if (location == null) {
@@ -734,20 +757,23 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
} }
} }
/// Same as [selectImage], but return the image data. /// Same as [selectImage], but return the image data with its index.
Future<Uint8List?> selectImageToData() async { /// Returns (imageIndex, imageData) or null if cancelled.
Future<(int, Uint8List)?> selectImageToData() async {
var i = await selectImage(); var i = await selectImage();
if (i == null) { if (i == null) {
return null; return null;
} }
var imageKey = context.reader.images![i]; var imageKey = context.reader.images![i];
Uint8List data;
if (imageKey.startsWith("file://")) { if (imageKey.startsWith("file://")) {
return await File(imageKey.substring(7)).readAsBytes(); data = await File(imageKey.substring(7)).readAsBytes();
} else { } else {
return (await CacheManager().findCache( data = await (await CacheManager().findCache(
"$imageKey@${context.reader.type.sourceKey}@${context.reader.cid}@${context.reader.eid}", "$imageKey@${context.reader.type.sourceKey}@${context.reader.cid}@${context.reader.eid}",
))!.readAsBytes(); ))!.readAsBytes();
} }
return (i, data);
} }
Future<Offset?> _showSelectImageOverlay() { Future<Offset?> _showSelectImageOverlay() {

28
patch/font.dart Normal file
View File

@@ -0,0 +1,28 @@
import 'dart:io';
import 'package:archive/archive_io.dart';
import 'package:dio/dio.dart';
void main() async {
const harmonySansLink = "https://developer.huawei.com/images/download/general/HarmonyOS-Sans.zip";
var dio = Dio();
await dio.download(harmonySansLink, "HarmonyOS-Sans.zip");
await extractFileToDisk("HarmonyOS-Sans.zip", "./assets/");
File("HarmonyOS-Sans.zip").deleteSync();
var pubspec = await File("pubspec.yaml").readAsString();
pubspec = pubspec.replaceFirst("# fonts:",
""" fonts:
- family: HarmonyOS Sans
fonts:
- asset: assets/HarmonyOS Sans/HarmonyOS_Sans_SC/HarmonyOS_Sans_SC_Regular.ttf
""");
await File("pubspec.yaml").writeAsString(pubspec);
var mainDart = await File("lib/main.dart").readAsString();
mainDart = mainDart.replaceFirst("Noto Sans CJK", "HarmonyOS Sans");
await File("lib/main.dart").writeAsString(mainDart);
print("Successfully patched font.");
}

View File

@@ -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: "direct dev"
description:
name: archive
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
url: "https://pub.dev"
source: hosted
version: "4.0.7"
args: args:
dependency: transitive dependency: transitive
description: description:
@@ -770,6 +778,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "4.0.0"
posix:
dependency: transitive
description:
name: posix
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
url: "https://pub.dev"
source: hosted
version: "6.0.3"
rhttp: rhttp:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@@ -2,7 +2,7 @@ name: venera
description: "A comic app." description: "A comic app."
publish_to: 'none' publish_to: 'none'
version: 1.5.1+151 version: 1.5.2+152
environment: environment:
sdk: '>=3.8.0 <4.0.0' sdk: '>=3.8.0 <4.0.0'
@@ -94,6 +94,7 @@ dev_dependencies:
flutter_lints: ^5.0.0 flutter_lints: ^5.0.0
flutter_to_arch: ^1.0.1 flutter_to_arch: ^1.0.1
flutter_to_debian: ^2.0.2 flutter_to_debian: ^2.0.2
archive: any
flutter: flutter:
uses-material-design: true uses-material-design: true
@@ -104,6 +105,7 @@ flutter:
- assets/tags.json - assets/tags.json
- assets/tags_tw.json - assets/tags_tw.json
- assets/opencc.txt - assets/opencc.txt
# fonts:
flutter_to_arch: flutter_to_arch:
name: Venera name: Venera