127 lines
4.5 KiB
Dart
127 lines
4.5 KiB
Dart
import 'dart:async';
|
|
import 'dart:ffi';
|
|
import 'dart:isolate';
|
|
|
|
import 'package:ffi/ffi.dart';
|
|
|
|
import '../../core/hwp_source.dart';
|
|
|
|
/// 관리자 지시 (10/20): Android 의 HWP · HWPX → PDF - APK 에 든 libjj_rhwp.so (rhwp v0.8.7, MIT - tool/build_rhwp_android.sh ·
|
|
/// tool/rhwp_ffi) 를 dart:ffi 로 부른다. 여는 것 · 바꾸는 것은 다른 isolate 에서 (화면을 막지 않게), 한 문서는 한 번에 하나씩.
|
|
class RhwpEngine implements HwpPdfEngine {
|
|
static const libName = 'libjj_rhwp.so';
|
|
|
|
/// 글꼴을 찾을 폴더 (Android 는 rhwp 가 시스템 글꼴을 스스로 찾지 않는다 - 한글은 Noto Sans/Serif CJK KR 로 대신 그림).
|
|
/// 관리자 판단 (10/20): 한글 글꼴 컴포넌트를 받으면 그 폴더를 앞에 더한다 (아직 없음)
|
|
static const fontDirs = ['/system/fonts'];
|
|
|
|
/// 라이브러리를 읽을 수 있는지 (없으면 다른 앱으로 연다)
|
|
static bool get available {
|
|
try {
|
|
DynamicLibrary.open(libName);
|
|
return true;
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<HwpPdfDoc> open(String path, {String? password}) async {
|
|
final (handle, err) = await Isolate.run(() => _open(path, password ?? ''));
|
|
if (handle == 0) throw HwpPdfException(err ?? 'rhwp');
|
|
final pages = await Isolate.run(() => _pageCount(handle));
|
|
return _RhwpDoc(handle, pages);
|
|
}
|
|
}
|
|
|
|
class _RhwpDoc implements HwpPdfDoc {
|
|
final int _handle;
|
|
@override
|
|
final int pageCount;
|
|
var _closed = false;
|
|
|
|
/// 한 번에 하나씩 (rhwp 문서는 여러 스레드에서 함께 쓰지 않는다)
|
|
Future<void> _queue = Future.value();
|
|
|
|
_RhwpDoc(this._handle, this.pageCount);
|
|
|
|
Future<T> _serial<T>(Future<T> Function() f) {
|
|
final run = _queue.then((_) => f());
|
|
_queue = run.then((_) {}, onError: (Object _) {});
|
|
return run;
|
|
}
|
|
|
|
@override
|
|
Future<void> exportPdf(int from, int to, String out, {List<String> fontDirs = const []}) => _serial(() async {
|
|
if (_closed) throw const HwpPdfException('closed');
|
|
final h = _handle, dirs = fontDirs.join('\n');
|
|
final err = await Isolate.run(() => _export(h, from, to, dirs, out));
|
|
if (err != null) throw HwpPdfException(err);
|
|
});
|
|
|
|
@override
|
|
Future<void> close() => _serial(() async {
|
|
if (_closed) return;
|
|
_closed = true;
|
|
_close(_handle);
|
|
});
|
|
}
|
|
|
|
// ───────── C 함수 (isolate 안에서 부른다 - 핸들은 주소 숫자로 넘긴다) ─────────
|
|
|
|
typedef _OpenC = Pointer<Void> Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Pointer<Utf8>>);
|
|
typedef _CountC = Int32 Function(Pointer<Void>);
|
|
typedef _CountD = int Function(Pointer<Void>);
|
|
typedef _ExportC = Int32 Function(Pointer<Void>, Int32, Int32, Pointer<Utf8>, Pointer<Utf8>, Pointer<Pointer<Utf8>>);
|
|
typedef _ExportD = int Function(Pointer<Void>, int, int, Pointer<Utf8>, Pointer<Utf8>, Pointer<Pointer<Utf8>>);
|
|
typedef _CloseC = Void Function(Pointer<Void>);
|
|
typedef _CloseD = void Function(Pointer<Void>);
|
|
typedef _FreeC = Void Function(Pointer<Utf8>);
|
|
typedef _FreeD = void Function(Pointer<Utf8>);
|
|
|
|
DynamicLibrary _lib() => DynamicLibrary.open(RhwpEngine.libName);
|
|
|
|
/// 돌려받은 오류 글을 읽고 푼다
|
|
String? _takeError(DynamicLibrary lib, Pointer<Pointer<Utf8>> err) {
|
|
final p = err.value;
|
|
if (p == nullptr) return null;
|
|
final s = p.toDartString();
|
|
lib.lookupFunction<_FreeC, _FreeD>('jj_rhwp_free')(p);
|
|
return s;
|
|
}
|
|
|
|
(int, String?) _open(String path, String password) {
|
|
final lib = _lib();
|
|
final open = lib.lookupFunction<_OpenC, _OpenC>('jj_rhwp_open');
|
|
final a = path.toNativeUtf8(), b = password.toNativeUtf8();
|
|
final err = calloc<Pointer<Utf8>>();
|
|
try {
|
|
final doc = open(a, b, err);
|
|
return (doc.address, _takeError(lib, err));
|
|
} finally {
|
|
calloc.free(a);
|
|
calloc.free(b);
|
|
calloc.free(err);
|
|
}
|
|
}
|
|
|
|
int _pageCount(int handle) => _lib().lookupFunction<_CountC, _CountD>('jj_rhwp_page_count')(Pointer<Void>.fromAddress(handle));
|
|
|
|
String? _export(int handle, int from, int to, String fontDirs, String out) {
|
|
final lib = _lib();
|
|
final export = lib.lookupFunction<_ExportC, _ExportD>('jj_rhwp_export_pdf');
|
|
final d = fontDirs.toNativeUtf8(), o = out.toNativeUtf8();
|
|
final err = calloc<Pointer<Utf8>>();
|
|
try {
|
|
final r = export(Pointer<Void>.fromAddress(handle), from, to, d, o, err);
|
|
final e = _takeError(lib, err);
|
|
return r == 0 ? null : (e ?? 'rhwp');
|
|
} finally {
|
|
calloc.free(d);
|
|
calloc.free(o);
|
|
calloc.free(err);
|
|
}
|
|
}
|
|
|
|
void _close(int handle) => _lib().lookupFunction<_CloseC, _CloseD>('jj_rhwp_close')(Pointer<Void>.fromAddress(handle));
|