@build-qube/papyra-tauri 0.0.0-stage → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -2
- package/dist/engine.d.ts +73 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +152 -0
- package/dist/engine.js.map +1 -0
- package/dist/host.d.ts +22 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +48 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/office.d.ts +54 -0
- package/dist/office.d.ts.map +1 -0
- package/dist/office.js +85 -0
- package/dist/office.js.map +1 -0
- package/dist/tiff.d.ts +14 -0
- package/dist/tiff.d.ts.map +1 -0
- package/dist/tiff.js +43 -0
- package/dist/tiff.js.map +1 -0
- package/dist/workbook.d.ts +19 -0
- package/dist/workbook.d.ts.map +1 -0
- package/dist/workbook.js +139 -0
- package/dist/workbook.js.map +1 -0
- package/package.json +47 -4
package/README.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @build-qube/papyra-tauri
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Tauri engines for papyra's viewer and file preview: PDFs, TIFFs, spreadsheets, and
|
|
4
|
+
Word and PowerPoint are parsed and rendered by papyra in the app's host process,
|
|
5
|
+
through the `tauri-plugin-papyra` crate. `TauriEngine` is the PDF engine;
|
|
6
|
+
`openTiff` and `openWorkbook` mirror papyra's own and resolve to its classes; and
|
|
7
|
+
`TauriOfficeEngine` stands in for papyra's `officeEngine`.
|
|
8
|
+
|
|
9
|
+
A Tauri app's WebView is never cross-origin isolated on Android or iOS, so papyra's
|
|
10
|
+
wasm build cannot run there. This engine does not need it: the Rust side renders on
|
|
11
|
+
the device's real cores, and the WebView only paints.
|
|
12
|
+
|
|
13
|
+
Installing it, wiring `tauri-plugin-papyra` into the app, and keeping papyra's
|
|
14
|
+
wasm out of the bundle are in the [Tauri guide](https://buildqube.github.io/Papyra/docs/guides/tauri).
|
|
15
|
+
|
|
16
|
+
## Pages and lifetimes
|
|
17
|
+
|
|
18
|
+
Pages travel over a `papyra://` protocol, as PNG by default. `new TauriEngine({
|
|
19
|
+
format: 'rgba' })` sends raw pixels instead: no encode or decode, four times the
|
|
20
|
+
bytes. Which is faster depends on the device.
|
|
21
|
+
|
|
22
|
+
Documents stay in the host until closed — TIFFs and workbooks too, through their
|
|
23
|
+
`close()`. The viewer's store and file preview close what they open; if you call the engine yourself, `await engine.close(doc)` — it
|
|
24
|
+
rejects if the host could not release the document. When a WebView loads a page —
|
|
25
|
+
a reload, HMR, the app restarting its UI — the host drops every document that
|
|
26
|
+
WebView had open, since nothing on the new page could close them.
|
package/dist/engine.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { type Attachment, type Availability, type BackendRenderOptions, type Capabilities, type DocumentMetadata, type OpenOptions, type OutlineNode, type PageBitmap, type PageLink, type PageSize, type PageText, type PdfBackend, PdfEngine, type PdfSource, type StructNode } from '@build-qube/papyra/core';
|
|
2
|
+
import { type TauriPageFormat } from './host.js';
|
|
3
|
+
type NativeInfo = {
|
|
4
|
+
[K in keyof DocumentMetadata]?: string | null;
|
|
5
|
+
};
|
|
6
|
+
export type { TauriPageFormat } from './host.js';
|
|
7
|
+
/** Options for {@link TauriEngine}. */
|
|
8
|
+
export interface TauriEngineOptions {
|
|
9
|
+
/**
|
|
10
|
+
* How pages come back from the host. `'png'` (the default) costs an encode in Rust
|
|
11
|
+
* and a decode in the WebView, off its main thread; `'rgba'` costs neither and is
|
|
12
|
+
* four times the bytes over an in-process channel, painted with `putImageData`.
|
|
13
|
+
* Which is faster depends on the device — measure on yours.
|
|
14
|
+
*/
|
|
15
|
+
format?: TauriPageFormat;
|
|
16
|
+
}
|
|
17
|
+
/** The plugin's answer to `open`. */
|
|
18
|
+
interface Opened {
|
|
19
|
+
id: number;
|
|
20
|
+
sizes: [number, number][];
|
|
21
|
+
info: NativeInfo;
|
|
22
|
+
pdfVersion: string | null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* PDFs parsed and rendered by papyra in the Tauri app's host process, through
|
|
26
|
+
* `tauri-plugin-papyra`.
|
|
27
|
+
*
|
|
28
|
+
* For a Tauri app's WebView, which is never cross-origin isolated on Android or iOS
|
|
29
|
+
* and so cannot run papyra's wasm. Here the work runs on the host's real cores with
|
|
30
|
+
* no wasm at all, and the WebView only paints.
|
|
31
|
+
*
|
|
32
|
+
* The app registers the plugin (`.plugin(tauri_plugin_papyra::init())`) and allows
|
|
33
|
+
* `papyra:default` in its capabilities. Everything papyra's own engine reads, this one
|
|
34
|
+
* does: same parser, same text, same structure tree.
|
|
35
|
+
*
|
|
36
|
+
* Documents live in the host until closed, so close what you open — the viewer's
|
|
37
|
+
* store and file preview do.
|
|
38
|
+
*/
|
|
39
|
+
export declare class TauriEngine extends PdfEngine<TauriBackend> {
|
|
40
|
+
#private;
|
|
41
|
+
readonly id = "tauri";
|
|
42
|
+
constructor(options?: TauriEngineOptions);
|
|
43
|
+
availability(): Availability;
|
|
44
|
+
protected load(source: PdfSource, options: OpenOptions): Promise<TauriBackend>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* One document held by the host, as a papyra backend.
|
|
48
|
+
*
|
|
49
|
+
* @internal Exported for tests.
|
|
50
|
+
*/
|
|
51
|
+
export declare class TauriBackend implements PdfBackend {
|
|
52
|
+
#private;
|
|
53
|
+
readonly fingerprint: string;
|
|
54
|
+
readonly format: TauriPageFormat;
|
|
55
|
+
readonly capabilities: Capabilities;
|
|
56
|
+
readonly abortable = false;
|
|
57
|
+
readonly concurrency = 4;
|
|
58
|
+
constructor(opened: Opened, fingerprint: string, format: TauriPageFormat);
|
|
59
|
+
get pageCount(): number;
|
|
60
|
+
pageSize(index: number): PageSize;
|
|
61
|
+
renderPage(index: number, options: BackendRenderOptions, signal?: AbortSignal): Promise<PageBitmap>;
|
|
62
|
+
metadata(): DocumentMetadata;
|
|
63
|
+
pdfVersion(): string | null;
|
|
64
|
+
pageText(index: number): Promise<PageText>;
|
|
65
|
+
outline(): Promise<OutlineNode[]>;
|
|
66
|
+
links(index: number): Promise<readonly PageLink[]>;
|
|
67
|
+
pageLabels(): Promise<readonly string[]>;
|
|
68
|
+
attachments(): Promise<readonly Attachment[]>;
|
|
69
|
+
attachmentData(index: number): Promise<Uint8Array>;
|
|
70
|
+
structTree(): Promise<StructNode[]>;
|
|
71
|
+
close(): Promise<void>;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,oBAAoB,EAGzB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EAErB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,SAAS,EACT,KAAK,SAAS,EAEd,KAAK,UAAU,EAKhB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,WAAW,CAAC;AAQnB,KAAK,UAAU,GAAG;KAAG,CAAC,IAAI,MAAM,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;CAAE,CAAC;AAEpE,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAED,qCAAqC;AACrC,UAAU,MAAM;IACd,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC1B,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAaD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,WAAY,SAAQ,SAAS,CAAC,YAAY,CAAC;;IACtD,QAAQ,CAAC,EAAE,WAAW;IAGtB,YAAY,OAAO,GAAE,kBAAuB,EAG3C;IAED,YAAY,IAAI,YAAY,CAE3B;IAED,UAAgB,IAAI,CAClB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,CAAC,CAevB;CACF;AAED;;;;GAIG;AACH,qBAAa,YAAa,YAAW,UAAU;;IAc3C,QAAQ,CAAC,WAAW,EAAE,MAAM;IAC5B,QAAQ,CAAC,MAAM,EAAE,eAAe;IAdlC,QAAQ,CAAC,YAAY,eAAc;IAGnC,QAAQ,CAAC,SAAS,SAAS;IAC3B,QAAQ,CAAC,WAAW,KAAK;IAOzB,YACE,MAAM,EAAE,MAAM,EACL,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,eAAe,EAiBjC;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAIhC;IAED,UAAU,CACR,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,oBAAoB,EAC7B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,CAOrB;IAED,QAAQ,IAAI,gBAAgB,CAE3B;IAED,UAAU,IAAI,MAAM,GAAG,IAAI,CAE1B;IAMK,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAU/C;IAEK,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAEtC;IAEK,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,QAAQ,EAAE,CAAC,CAKvD;IAED,UAAU,IAAI,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAEvC;IAEK,WAAW,IAAI,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAGlD;IAEK,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAIvD;IAEK,UAAU,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAIxC;IAED,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAErB;CACF"}
|
package/dist/engine.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { buildOutlineTree, buildStructTree, fingerprint, PdfEngine, rethrowLoadError, toAttachment, toBytes, toPageLink, toPageText, } from '@build-qube/papyra/core';
|
|
2
|
+
import { invoke } from '@tauri-apps/api/core';
|
|
3
|
+
import { closeDocument, command, fetchPage, pagesOf, tauriAvailability, } from './host.js';
|
|
4
|
+
/** papyra's engine reads everything, and so does the host running it. */
|
|
5
|
+
const EVERYTHING = {
|
|
6
|
+
text: true,
|
|
7
|
+
outline: true,
|
|
8
|
+
links: true,
|
|
9
|
+
attachments: true,
|
|
10
|
+
structure: true,
|
|
11
|
+
pageLabels: true,
|
|
12
|
+
annotationsToggle: true,
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* PDFs parsed and rendered by papyra in the Tauri app's host process, through
|
|
16
|
+
* `tauri-plugin-papyra`.
|
|
17
|
+
*
|
|
18
|
+
* For a Tauri app's WebView, which is never cross-origin isolated on Android or iOS
|
|
19
|
+
* and so cannot run papyra's wasm. Here the work runs on the host's real cores with
|
|
20
|
+
* no wasm at all, and the WebView only paints.
|
|
21
|
+
*
|
|
22
|
+
* The app registers the plugin (`.plugin(tauri_plugin_papyra::init())`) and allows
|
|
23
|
+
* `papyra:default` in its capabilities. Everything papyra's own engine reads, this one
|
|
24
|
+
* does: same parser, same text, same structure tree.
|
|
25
|
+
*
|
|
26
|
+
* Documents live in the host until closed, so close what you open — the viewer's
|
|
27
|
+
* store and file preview do.
|
|
28
|
+
*/
|
|
29
|
+
export class TauriEngine extends PdfEngine {
|
|
30
|
+
id = 'tauri';
|
|
31
|
+
#format;
|
|
32
|
+
constructor(options = {}) {
|
|
33
|
+
super();
|
|
34
|
+
this.#format = options.format ?? 'png';
|
|
35
|
+
}
|
|
36
|
+
availability() {
|
|
37
|
+
return tauriAvailability('tauri-plugin-papyra');
|
|
38
|
+
}
|
|
39
|
+
async load(source, options) {
|
|
40
|
+
const bytes = await toBytes(source);
|
|
41
|
+
let opened;
|
|
42
|
+
try {
|
|
43
|
+
opened = await invoke(command('open'), bytes, {
|
|
44
|
+
headers: options.password === undefined
|
|
45
|
+
? {}
|
|
46
|
+
: // Headers are ASCII; a password need not be.
|
|
47
|
+
{ 'x-papyra-password': encodeURIComponent(options.password) },
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
rethrowLoadError(e);
|
|
52
|
+
}
|
|
53
|
+
return new TauriBackend(opened, fingerprint(bytes), this.#format);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* One document held by the host, as a papyra backend.
|
|
58
|
+
*
|
|
59
|
+
* @internal Exported for tests.
|
|
60
|
+
*/
|
|
61
|
+
export class TauriBackend {
|
|
62
|
+
fingerprint;
|
|
63
|
+
format;
|
|
64
|
+
capabilities = EVERYTHING;
|
|
65
|
+
// A fetch can be aborted, but the render in Rust runs on regardless; abandoning it
|
|
66
|
+
// would only make a page scrolled back to render twice.
|
|
67
|
+
abortable = false;
|
|
68
|
+
concurrency = 4;
|
|
69
|
+
#id;
|
|
70
|
+
#sizes;
|
|
71
|
+
#info;
|
|
72
|
+
#version;
|
|
73
|
+
#pages;
|
|
74
|
+
constructor(opened, fingerprint, format) {
|
|
75
|
+
this.fingerprint = fingerprint;
|
|
76
|
+
this.format = format;
|
|
77
|
+
this.#id = opened.id;
|
|
78
|
+
this.#sizes = opened.sizes.map(([width, height]) => ({ width, height }));
|
|
79
|
+
const i = opened.info;
|
|
80
|
+
this.#info = {
|
|
81
|
+
title: i.title ?? null,
|
|
82
|
+
author: i.author ?? null,
|
|
83
|
+
subject: i.subject ?? null,
|
|
84
|
+
keywords: i.keywords ?? null,
|
|
85
|
+
creator: i.creator ?? null,
|
|
86
|
+
producer: i.producer ?? null,
|
|
87
|
+
created: i.created ?? null,
|
|
88
|
+
modified: i.modified ?? null,
|
|
89
|
+
};
|
|
90
|
+
this.#version = opened.pdfVersion ?? null;
|
|
91
|
+
this.#pages = pagesOf(this.#id);
|
|
92
|
+
}
|
|
93
|
+
get pageCount() {
|
|
94
|
+
return this.#sizes.length;
|
|
95
|
+
}
|
|
96
|
+
pageSize(index) {
|
|
97
|
+
const size = this.#sizes[index];
|
|
98
|
+
if (!size)
|
|
99
|
+
throw new RangeError(`page ${index} is out of range`);
|
|
100
|
+
return size;
|
|
101
|
+
}
|
|
102
|
+
renderPage(index, options, signal) {
|
|
103
|
+
const annots = options.annotations === false ? 0 : 1;
|
|
104
|
+
return fetchPage(`${this.#pages}${index}?dpi=${options.dpi}&annots=${annots}`, this.format, signal);
|
|
105
|
+
}
|
|
106
|
+
metadata() {
|
|
107
|
+
return this.#info;
|
|
108
|
+
}
|
|
109
|
+
pdfVersion() {
|
|
110
|
+
return this.#version;
|
|
111
|
+
}
|
|
112
|
+
#call(name, args = {}) {
|
|
113
|
+
return invoke(command(name), { id: this.#id, ...args });
|
|
114
|
+
}
|
|
115
|
+
async pageText(index) {
|
|
116
|
+
const text = await this.#call('page_text', { index });
|
|
117
|
+
// JSON carries offsets as numbers; papyra's lines hold a Float32Array.
|
|
118
|
+
return toPageText(index, {
|
|
119
|
+
...text,
|
|
120
|
+
lines: text.lines.map((line) => ({
|
|
121
|
+
...line,
|
|
122
|
+
offsets: Float32Array.from(line.offsets),
|
|
123
|
+
})),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
async outline() {
|
|
127
|
+
return buildOutlineTree(await this.#call('outline'));
|
|
128
|
+
}
|
|
129
|
+
async links(index) {
|
|
130
|
+
const links = await this.#call('links', { index });
|
|
131
|
+
return links
|
|
132
|
+
.map(toPageLink)
|
|
133
|
+
.filter((link) => link !== null);
|
|
134
|
+
}
|
|
135
|
+
pageLabels() {
|
|
136
|
+
return this.#call('page_labels');
|
|
137
|
+
}
|
|
138
|
+
async attachments() {
|
|
139
|
+
const files = await this.#call('attachments');
|
|
140
|
+
return files.map(toAttachment);
|
|
141
|
+
}
|
|
142
|
+
async attachmentData(index) {
|
|
143
|
+
return new Uint8Array(await this.#call('attachment_data', { index }));
|
|
144
|
+
}
|
|
145
|
+
async structTree() {
|
|
146
|
+
return buildStructTree(await this.#call('struct_tree'));
|
|
147
|
+
}
|
|
148
|
+
close() {
|
|
149
|
+
return closeDocument(this.#id);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/host.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Availability, PageBitmap } from '@build-qube/papyra/core';
|
|
2
|
+
/** How pages travel from the host. */
|
|
3
|
+
export type TauriPageFormat = 'png' | 'rgba';
|
|
4
|
+
/** A plugin command, by the name Tauri routes it under. */
|
|
5
|
+
export declare const command: (name: string, plugin?: string) => string;
|
|
6
|
+
/** Ready inside a Tauri WebView; elsewhere, says which plugin it is waiting for. */
|
|
7
|
+
export declare function tauriAvailability(plugin: string): Availability;
|
|
8
|
+
/**
|
|
9
|
+
* Where one document's pages are served: `papyra://localhost/<id>/` on macOS, iOS
|
|
10
|
+
* and Linux, `http://papyra.localhost/<id>/` on Android and Windows — Tauri knows
|
|
11
|
+
* which, so ask it rather than sniff.
|
|
12
|
+
*/
|
|
13
|
+
export declare function pagesOf(id: number): string;
|
|
14
|
+
/**
|
|
15
|
+
* One page from the `papyra://` protocol, whichever format the document is: PNG
|
|
16
|
+
* decoded off the main thread to an `ImageBitmap`, or raw RGBA as papyra's own pages
|
|
17
|
+
* are.
|
|
18
|
+
*/
|
|
19
|
+
export declare function fetchPage(url: string, format: TauriPageFormat, signal?: AbortSignal): Promise<PageBitmap>;
|
|
20
|
+
/** Free a document of any format; closing one already gone is not an error. */
|
|
21
|
+
export declare function closeDocument(id: number): Promise<void>;
|
|
22
|
+
//# sourceMappingURL=host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAGxE,sCAAsC;AACtC,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,MAAM,CAAC;AAE7C,2DAA2D;AAC3D,eAAO,MAAM,OAAO,SAAU,MAAM,4BACR,CAAC;AAE7B,oFAAoF;AACpF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAS9D;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE1C;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,CAarB;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvD"}
|
package/dist/host.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { convertFileSrc, invoke } from '@tauri-apps/api/core';
|
|
2
|
+
/** A plugin command, by the name Tauri routes it under. */
|
|
3
|
+
export const command = (name, plugin = 'papyra') => `plugin:${plugin}|${name}`;
|
|
4
|
+
/** Ready inside a Tauri WebView; elsewhere, says which plugin it is waiting for. */
|
|
5
|
+
export function tauriAvailability(plugin) {
|
|
6
|
+
if ('__TAURI_INTERNALS__' in globalThis)
|
|
7
|
+
return { ok: true };
|
|
8
|
+
return {
|
|
9
|
+
ok: false,
|
|
10
|
+
title: 'Not running inside a Tauri app',
|
|
11
|
+
reason: 'This engine renders in the app’s host process, through ' +
|
|
12
|
+
`${plugin}, and this page is not in a Tauri WebView.`,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Where one document's pages are served: `papyra://localhost/<id>/` on macOS, iOS
|
|
17
|
+
* and Linux, `http://papyra.localhost/<id>/` on Android and Windows — Tauri knows
|
|
18
|
+
* which, so ask it rather than sniff.
|
|
19
|
+
*/
|
|
20
|
+
export function pagesOf(id) {
|
|
21
|
+
return `${convertFileSrc('', 'papyra')}${id}/`;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* One page from the `papyra://` protocol, whichever format the document is: PNG
|
|
25
|
+
* decoded off the main thread to an `ImageBitmap`, or raw RGBA as papyra's own pages
|
|
26
|
+
* are.
|
|
27
|
+
*/
|
|
28
|
+
export async function fetchPage(url, format, signal) {
|
|
29
|
+
const res = await fetch(`${url}&format=${format}`, { signal });
|
|
30
|
+
if (!res.ok)
|
|
31
|
+
throw new Error(await res.text());
|
|
32
|
+
if (format === 'png')
|
|
33
|
+
return createImageBitmap(await res.blob());
|
|
34
|
+
const width = Number(res.headers.get('x-width'));
|
|
35
|
+
const height = Number(res.headers.get('x-height'));
|
|
36
|
+
return {
|
|
37
|
+
width,
|
|
38
|
+
height,
|
|
39
|
+
stride: width * 4,
|
|
40
|
+
format: 'rgba8',
|
|
41
|
+
data: new Uint8Array(await res.arrayBuffer()),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Free a document of any format; closing one already gone is not an error. */
|
|
45
|
+
export function closeDocument(id) {
|
|
46
|
+
return invoke(command('close'), { id });
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=host.js.map
|
package/dist/host.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAK9D,2DAA2D;AAC3D,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAY,EAAE,MAAM,GAAG,QAAQ,EAAE,EAAE,CACzD,UAAU,MAAM,IAAI,IAAI,EAAE,CAAC;AAE7B,oFAAoF;AACpF,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,IAAI,qBAAqB,IAAI,UAAU;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IAC7D,OAAO;QACL,EAAE,EAAE,KAAK;QACT,KAAK,EAAE,gCAAgC;QACvC,MAAM,EACJ,yDAAyD;YACzD,GAAG,MAAM,4CAA4C;KACxD,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,EAAU;IAChC,OAAO,GAAG,cAAc,CAAC,EAAE,EAAE,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;AACjD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAW,EACX,MAAuB,EACvB,MAAoB;IAEpB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,GAAG,WAAW,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/D,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/C,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,iBAAiB,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;IACnD,OAAO;QACL,KAAK;QACL,MAAM;QACN,MAAM,EAAE,KAAK,GAAG,CAAC;QACjB,MAAM,EAAE,OAAO;QACf,IAAI,EAAE,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC;KAC9C,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,aAAa,CAAC,EAAU;IACtC,OAAO,MAAM,CAAO,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AAChD,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tauri engines for papyra's viewer: PDFs, TIFFs, spreadsheets, and Word and
|
|
3
|
+
* PowerPoint parsed and rendered by papyra in the app's host process, through
|
|
4
|
+
* `tauri-plugin-papyra` (and `tauri-plugin-papyra-office` for office files).
|
|
5
|
+
*
|
|
6
|
+
* ```tsx
|
|
7
|
+
* import { TauriEngine, openTiff, openWorkbook } from '@build-qube/papyra-tauri';
|
|
8
|
+
* import { PdfEngineProvider } from '@/components/pdf-engine';
|
|
9
|
+
*
|
|
10
|
+
* <PdfEngineProvider engine={new TauriEngine()}>{app}</PdfEngineProvider>
|
|
11
|
+
* createFilePreview([tiffRenderer.with({ open: openTiff }), …]);
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export { TauriEngine, type TauriEngineOptions, type TauriPageFormat, } from './engine.js';
|
|
15
|
+
export { TauriOfficeEngine } from './office.js';
|
|
16
|
+
export { openTiff } from './tiff.js';
|
|
17
|
+
export { openWorkbook } from './workbook.js';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EACL,WAAW,EACX,KAAK,kBAAkB,EACvB,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tauri engines for papyra's viewer: PDFs, TIFFs, spreadsheets, and Word and
|
|
3
|
+
* PowerPoint parsed and rendered by papyra in the app's host process, through
|
|
4
|
+
* `tauri-plugin-papyra` (and `tauri-plugin-papyra-office` for office files).
|
|
5
|
+
*
|
|
6
|
+
* ```tsx
|
|
7
|
+
* import { TauriEngine, openTiff, openWorkbook } from '@build-qube/papyra-tauri';
|
|
8
|
+
* import { PdfEngineProvider } from '@/components/pdf-engine';
|
|
9
|
+
*
|
|
10
|
+
* <PdfEngineProvider engine={new TauriEngine()}>{app}</PdfEngineProvider>
|
|
11
|
+
* createFilePreview([tiffRenderer.with({ open: openTiff }), …]);
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export { TauriEngine, } from './engine.js';
|
|
15
|
+
export { TauriOfficeEngine } from './office.js';
|
|
16
|
+
export { openTiff } from './tiff.js';
|
|
17
|
+
export { openWorkbook } from './workbook.js';
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EACL,WAAW,GAGZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/office.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type Availability, type BackendRenderOptions, type PageBitmap, type PagedBackend, PagedEngine, type PageSize, type PdfSource } from '@build-qube/papyra/core';
|
|
2
|
+
import type { OfficeDoc, OfficeKind } from '@build-qube/papyra/office';
|
|
3
|
+
import type { TauriEngineOptions } from './engine.js';
|
|
4
|
+
import { type TauriPageFormat } from './host.js';
|
|
5
|
+
/** The office plugin's answer to `open`. */
|
|
6
|
+
interface Opened {
|
|
7
|
+
id: number;
|
|
8
|
+
kind: OfficeKind;
|
|
9
|
+
sizes: [number, number][];
|
|
10
|
+
}
|
|
11
|
+
/** One office document held by the host, as a papyra backend. */
|
|
12
|
+
declare class TauriOfficeBackend implements PagedBackend {
|
|
13
|
+
#private;
|
|
14
|
+
readonly capabilities: import("@build-qube/papyra/core").Capabilities;
|
|
15
|
+
readonly abortable = false;
|
|
16
|
+
readonly concurrency = 4;
|
|
17
|
+
readonly kind: OfficeKind;
|
|
18
|
+
constructor(opened: Opened, format: TauriPageFormat);
|
|
19
|
+
get pageCount(): number;
|
|
20
|
+
pageSize(index: number): PageSize;
|
|
21
|
+
/** `annotations` does not apply: an office page has none to switch off. */
|
|
22
|
+
renderPage(index: number, { dpi }: BackendRenderOptions, signal?: AbortSignal): Promise<PageBitmap>;
|
|
23
|
+
close(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* papyra's office engine — Word and PowerPoint, over WordCraft and DeckCraft — run
|
|
27
|
+
* in the Tauri app's host process through `tauri-plugin-papyra-office`, beside
|
|
28
|
+
* `tauri-plugin-papyra`. Allow `papyra:default` and `papyra-office:default`.
|
|
29
|
+
*
|
|
30
|
+
* Pass it where papyra's own `officeEngine` would go:
|
|
31
|
+
* `pptxRenderer.with({ engine })` and `docxPagedRenderer.with({ engine })`. Pages
|
|
32
|
+
* come over the `papyra://` protocol as a PDF's do, in the same `format`.
|
|
33
|
+
*
|
|
34
|
+
* The office crate is not on crates.io, because WordCraft and DeckCraft are not:
|
|
35
|
+
* an app takes it and `tauri-plugin-papyra` from the GitHub repository at one tag.
|
|
36
|
+
*/
|
|
37
|
+
export declare class TauriOfficeEngine extends PagedEngine<OfficeDoc, TauriOfficeBackend> {
|
|
38
|
+
#private;
|
|
39
|
+
readonly id = "tauri-office";
|
|
40
|
+
constructor(options?: TauriEngineOptions);
|
|
41
|
+
/**
|
|
42
|
+
* Make a TrueType or OpenType font available to the host's Word and PowerPoint
|
|
43
|
+
* layout, for every document opened after this call. Resolves to how many faces
|
|
44
|
+
* were added. See papyra's `PapyraOfficeEngine.addFont` for why it matters.
|
|
45
|
+
*/
|
|
46
|
+
addFont(source: PdfSource): Promise<number>;
|
|
47
|
+
availability(): Availability;
|
|
48
|
+
/** @internal */
|
|
49
|
+
protected load(source: PdfSource): Promise<TauriOfficeBackend>;
|
|
50
|
+
/** @internal */
|
|
51
|
+
protected handle(backend: TauriOfficeBackend): OfficeDoc;
|
|
52
|
+
}
|
|
53
|
+
export {};
|
|
54
|
+
//# sourceMappingURL=office.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"office.d.ts","sourceRoot":"","sources":["../src/office.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,WAAW,EACX,KAAK,QAAQ,EACb,KAAK,SAAS,EAGf,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,WAAW,CAAC;AAEnB,4CAA4C;AAC5C,UAAU,MAAM;IACd,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;CAC3B;AAED,iEAAiE;AACjE,cAAM,kBAAmB,YAAW,YAAY;;IAC9C,QAAQ,CAAC,YAAY,iDAAe;IAEpC,QAAQ,CAAC,SAAS,SAAS;IAC3B,QAAQ,CAAC,WAAW,KAAK;IACzB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAM1B,YAAY,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAMlD;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAIhC;IAED,2EAA2E;IAC3E,UAAU,CACR,KAAK,EAAE,MAAM,EACb,EAAE,GAAG,EAAE,EAAE,oBAAoB,EAC7B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,CAErB;IAED,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAErB;CACF;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,iBAAkB,SAAQ,WAAW,CAChD,SAAS,EACT,kBAAkB,CACnB;;IACC,QAAQ,CAAC,EAAE,kBAAkB;IAG7B,YAAY,OAAO,GAAE,kBAAuB,EAG3C;IAED;;;;OAIG;IACG,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAKhD;IAED,YAAY,IAAI,YAAY,CAE3B;IAED,gBAAgB;IAChB,UAAgB,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAMnE;IAED,gBAAgB;IAChB,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,kBAAkB,GAAG,SAAS,CAQvD;CACF"}
|
package/dist/office.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { PagedEngine, RENDER_ONLY, toBytes, } from '@build-qube/papyra/core';
|
|
2
|
+
import { invoke } from '@tauri-apps/api/core';
|
|
3
|
+
import { closeDocument, command, fetchPage, pagesOf, tauriAvailability, } from './host.js';
|
|
4
|
+
/** One office document held by the host, as a papyra backend. */
|
|
5
|
+
class TauriOfficeBackend {
|
|
6
|
+
capabilities = RENDER_ONLY;
|
|
7
|
+
// As TauriEngine: the render in Rust runs on whatever the fetch does.
|
|
8
|
+
abortable = false;
|
|
9
|
+
concurrency = 4;
|
|
10
|
+
kind;
|
|
11
|
+
#id;
|
|
12
|
+
#sizes;
|
|
13
|
+
#pages;
|
|
14
|
+
#format;
|
|
15
|
+
constructor(opened, format) {
|
|
16
|
+
this.#id = opened.id;
|
|
17
|
+
this.kind = opened.kind;
|
|
18
|
+
this.#sizes = opened.sizes.map(([width, height]) => ({ width, height }));
|
|
19
|
+
this.#pages = pagesOf(opened.id);
|
|
20
|
+
this.#format = format;
|
|
21
|
+
}
|
|
22
|
+
get pageCount() {
|
|
23
|
+
return this.#sizes.length;
|
|
24
|
+
}
|
|
25
|
+
pageSize(index) {
|
|
26
|
+
const size = this.#sizes[index];
|
|
27
|
+
if (!size)
|
|
28
|
+
throw new RangeError(`page ${index} is out of range`);
|
|
29
|
+
return size;
|
|
30
|
+
}
|
|
31
|
+
/** `annotations` does not apply: an office page has none to switch off. */
|
|
32
|
+
renderPage(index, { dpi }, signal) {
|
|
33
|
+
return fetchPage(`${this.#pages}${index}?dpi=${dpi}`, this.#format, signal);
|
|
34
|
+
}
|
|
35
|
+
close() {
|
|
36
|
+
return closeDocument(this.#id);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* papyra's office engine — Word and PowerPoint, over WordCraft and DeckCraft — run
|
|
41
|
+
* in the Tauri app's host process through `tauri-plugin-papyra-office`, beside
|
|
42
|
+
* `tauri-plugin-papyra`. Allow `papyra:default` and `papyra-office:default`.
|
|
43
|
+
*
|
|
44
|
+
* Pass it where papyra's own `officeEngine` would go:
|
|
45
|
+
* `pptxRenderer.with({ engine })` and `docxPagedRenderer.with({ engine })`. Pages
|
|
46
|
+
* come over the `papyra://` protocol as a PDF's do, in the same `format`.
|
|
47
|
+
*
|
|
48
|
+
* The office crate is not on crates.io, because WordCraft and DeckCraft are not:
|
|
49
|
+
* an app takes it and `tauri-plugin-papyra` from the GitHub repository at one tag.
|
|
50
|
+
*/
|
|
51
|
+
export class TauriOfficeEngine extends PagedEngine {
|
|
52
|
+
id = 'tauri-office';
|
|
53
|
+
#format;
|
|
54
|
+
constructor(options = {}) {
|
|
55
|
+
super();
|
|
56
|
+
this.#format = options.format ?? 'png';
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Make a TrueType or OpenType font available to the host's Word and PowerPoint
|
|
60
|
+
* layout, for every document opened after this call. Resolves to how many faces
|
|
61
|
+
* were added. See papyra's `PapyraOfficeEngine.addFont` for why it matters.
|
|
62
|
+
*/
|
|
63
|
+
async addFont(source) {
|
|
64
|
+
return invoke(command('add_font', 'papyra-office'), await toBytes(source));
|
|
65
|
+
}
|
|
66
|
+
availability() {
|
|
67
|
+
return tauriAvailability('tauri-plugin-papyra-office');
|
|
68
|
+
}
|
|
69
|
+
/** @internal */
|
|
70
|
+
async load(source) {
|
|
71
|
+
const opened = await invoke(command('open', 'papyra-office'), await toBytes(source));
|
|
72
|
+
return new TauriOfficeBackend(opened, this.#format);
|
|
73
|
+
}
|
|
74
|
+
/** @internal */
|
|
75
|
+
handle(backend) {
|
|
76
|
+
return {
|
|
77
|
+
engine: this.id,
|
|
78
|
+
kind: backend.kind,
|
|
79
|
+
pageCount: backend.pageCount,
|
|
80
|
+
pageSize: (index) => backend.pageSize(index),
|
|
81
|
+
capabilities: backend.capabilities,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=office.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"office.js","sourceRoot":"","sources":["../src/office.ts"],"names":[],"mappings":"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"}
|
package/dist/tiff.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type PageBitmap, type PdfSource, TiffImage } from '@build-qube/papyra/core';
|
|
2
|
+
import type { TauriEngineOptions } from './engine.js';
|
|
3
|
+
/**
|
|
4
|
+
* papyra's `openTiff`, read and decoded in the Tauri app's host process through
|
|
5
|
+
* `tauri-plugin-papyra` with its `tiff` feature, and allowed by `papyra:tiff`.
|
|
6
|
+
*
|
|
7
|
+
* Resolves to papyra's own `TiffImage`, so `tiffRenderer.with({ open: openTiff })`
|
|
8
|
+
* shows it unchanged. Pages come over the `papyra://` protocol as a PDF's do, in
|
|
9
|
+
* the same `format` — PNG by default, which for a bilevel fax page is a fraction of
|
|
10
|
+
* its RGBA — so `renderPage` resolves to an `ImageBitmap` unless `format` is
|
|
11
|
+
* `'rgba'`. The file stays in the host until `close()`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function openTiff(source: PdfSource, options?: TauriEngineOptions): Promise<TiffImage<PageBitmap>>;
|
|
14
|
+
//# sourceMappingURL=tiff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tiff.d.ts","sourceRoot":"","sources":["../src/tiff.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,SAAS,EACd,SAAS,EAGV,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAkBtD;;;;;;;;;GASG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,SAAS,EACjB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAkChC"}
|
package/dist/tiff.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { TiffImage, toBytes, } from '@build-qube/papyra/core';
|
|
2
|
+
import { invoke } from '@tauri-apps/api/core';
|
|
3
|
+
import { closeDocument, command, fetchPage, pagesOf, } from './host.js';
|
|
4
|
+
/** The widest page the protocol's `maxWidth` can name. */
|
|
5
|
+
const MAX_U32 = 0xffff_ffff;
|
|
6
|
+
/**
|
|
7
|
+
* papyra's `openTiff`, read and decoded in the Tauri app's host process through
|
|
8
|
+
* `tauri-plugin-papyra` with its `tiff` feature, and allowed by `papyra:tiff`.
|
|
9
|
+
*
|
|
10
|
+
* Resolves to papyra's own `TiffImage`, so `tiffRenderer.with({ open: openTiff })`
|
|
11
|
+
* shows it unchanged. Pages come over the `papyra://` protocol as a PDF's do, in
|
|
12
|
+
* the same `format` — PNG by default, which for a bilevel fax page is a fraction of
|
|
13
|
+
* its RGBA — so `renderPage` resolves to an `ImageBitmap` unless `format` is
|
|
14
|
+
* `'rgba'`. The file stays in the host until `close()`.
|
|
15
|
+
*/
|
|
16
|
+
export async function openTiff(source, options = {}) {
|
|
17
|
+
const format = options.format ?? 'png';
|
|
18
|
+
const opened = await invoke(command('open_tiff'), await toBytes(source));
|
|
19
|
+
const pages = pagesOf(opened.id);
|
|
20
|
+
return new TiffImage({
|
|
21
|
+
pages: opened.pages,
|
|
22
|
+
renderPage(index, { maxPixels, maxWidth } = {}) {
|
|
23
|
+
// The host reads both as integers, so refuse here what it would answer with
|
|
24
|
+
// a bare 400: `1e+21` is not one, and a cap below one pixel is no page.
|
|
25
|
+
if (maxWidth !== undefined && !(maxWidth >= 1 && maxWidth <= MAX_U32)) {
|
|
26
|
+
return Promise.reject(new RangeError(`papyra: maxWidth ${maxWidth} is not a width in pixels`));
|
|
27
|
+
}
|
|
28
|
+
if (maxPixels !== undefined && !(maxPixels >= 1)) {
|
|
29
|
+
return Promise.reject(new RangeError(`papyra: maxPixels ${maxPixels} is under one pixel`));
|
|
30
|
+
}
|
|
31
|
+
const query = new URLSearchParams();
|
|
32
|
+
if (maxWidth !== undefined)
|
|
33
|
+
query.set('maxWidth', String(maxWidth));
|
|
34
|
+
if (maxPixels !== undefined) {
|
|
35
|
+
// Clamped as the host would, so a huge value is not written as `1e+21`.
|
|
36
|
+
query.set('maxPixels', String(Math.floor(Math.min(maxPixels, 1e8))));
|
|
37
|
+
}
|
|
38
|
+
return fetchPage(`${pages}${index}?${query}`, format);
|
|
39
|
+
},
|
|
40
|
+
close: () => closeDocument(opened.id),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=tiff.js.map
|
package/dist/tiff.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tiff.js","sourceRoot":"","sources":["../src/tiff.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,SAAS,EAET,OAAO,GACR,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,OAAO,EACL,aAAa,EACb,OAAO,EACP,SAAS,EACT,OAAO,GAER,MAAM,WAAW,CAAC;AAEnB,0DAA0D;AAC1D,MAAM,OAAO,GAAG,WAAW,CAAC;AAQ5B;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,MAAiB,EACjB,OAAO,GAAuB,EAAE;IAEhC,MAAM,MAAM,GAAoB,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC;IACxD,MAAM,MAAM,GAAG,MAAM,MAAM,CACzB,OAAO,CAAC,WAAW,CAAC,EACpB,MAAM,OAAO,CAAC,MAAM,CAAC,CACtB,CAAC;IACF,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACjC,OAAO,IAAI,SAAS,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,EAAE;YAC5C,4EAA4E;YAC5E,wEAAwE;YACxE,IAAI,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,IAAI,QAAQ,IAAI,OAAO,CAAC,EAAE,CAAC;gBACtE,OAAO,OAAO,CAAC,MAAM,CACnB,IAAI,UAAU,CACZ,oBAAoB,QAAQ,2BAA2B,CACxD,CACF,CAAC;YACJ,CAAC;YACD,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC;gBACjD,OAAO,OAAO,CAAC,MAAM,CACnB,IAAI,UAAU,CAAC,qBAAqB,SAAS,qBAAqB,CAAC,CACpE,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;YACpC,IAAI,QAAQ,KAAK,SAAS;gBAAE,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YACpE,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC5B,wEAAwE;gBACxE,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACvE,CAAC;YACD,OAAO,SAAS,CAAC,GAAG,KAAK,GAAG,KAAK,IAAI,KAAK,EAAE,EAAE,MAAM,CAAC,CAAC;QACxD,CAAC;QACD,KAAK,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;KACtC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type PdfSource, type Workbook, type WorkbookOptions } from '@build-qube/papyra/core';
|
|
2
|
+
/**
|
|
3
|
+
* papyra's `openWorkbook`, parsed in the Tauri app's host process through
|
|
4
|
+
* `tauri-plugin-papyra` with its `tables` feature, and allowed by `papyra:tables`.
|
|
5
|
+
*
|
|
6
|
+
* Resolves to papyra's own `Workbook`, so `spreadsheetRenderer.with({ open:
|
|
7
|
+
* openWorkbook })` and `csvRenderer`'s show it unchanged. A grid reads a sheet's
|
|
8
|
+
* cells synchronously, a window per frame, and IPC is not synchronous — so each
|
|
9
|
+
* sheet's cells come over **whole**, in one binary call when the sheet is first
|
|
10
|
+
* read, and windows are cut from that copy. That is about 19 bytes a cell plus its
|
|
11
|
+
* text, held in the host and here, over the sheet's whole rectangle — so the host
|
|
12
|
+
* cuts a sheet to its first rows, marked `truncated`, past a million cells (~19 MB)
|
|
13
|
+
* or past sixteen times the cells it really holds. A lower `maxCells` lowers the
|
|
14
|
+
* first.
|
|
15
|
+
*
|
|
16
|
+
* The workbook stays in the host until `close()`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function openWorkbook(source: PdfSource, options?: WorkbookOptions): Promise<Workbook>;
|
|
19
|
+
//# sourceMappingURL=workbook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workbook.d.ts","sourceRoot":"","sources":["../src/workbook.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,SAAS,EAEd,KAAK,QAAQ,EACb,KAAK,eAAe,EAErB,MAAM,yBAAyB,CAAC;AAuHjC;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,EACjB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,QAAQ,CAAC,CA4CnB"}
|
package/dist/workbook.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { openWorkbookWith, } from '@build-qube/papyra/core';
|
|
2
|
+
import { invoke } from '@tauri-apps/api/core';
|
|
3
|
+
import { closeDocument, command } from './host.js';
|
|
4
|
+
/**
|
|
5
|
+
* A window as `pack` in the plugin's `tables.rs` lays it out: four u32s, then each
|
|
6
|
+
* array aligned for a view straight over the buffer, then the text as UTF-8.
|
|
7
|
+
*/
|
|
8
|
+
function unpack(raw) {
|
|
9
|
+
// An ArrayBuffer from the binary IPC; a number[] if Tauri fell back to
|
|
10
|
+
// postMessage. A view over the first copies nothing, and starts at offset 0 — the
|
|
11
|
+
// typed views below are aligned to that.
|
|
12
|
+
const { buffer } = new Uint8Array(raw);
|
|
13
|
+
const [rowStart = 0, colStart = 0, rows = 0, cols = 0] = new Uint32Array(buffer, 0, 4);
|
|
14
|
+
const n = rows * cols;
|
|
15
|
+
let at = 16;
|
|
16
|
+
const take = (make, bytes) => {
|
|
17
|
+
const out = make(at);
|
|
18
|
+
at += bytes;
|
|
19
|
+
return out;
|
|
20
|
+
};
|
|
21
|
+
const kinds = take((a) => new Uint8Array(buffer, a, n), n);
|
|
22
|
+
at = Math.ceil(at / 8) * 8;
|
|
23
|
+
const numbers = take((a) => new Float64Array(buffer, a, n), n * 8);
|
|
24
|
+
const colors = take((a) => new Uint32Array(buffer, a, n), n * 4);
|
|
25
|
+
const offsets = take((a) => new Uint32Array(buffer, a, n + 1), (n + 1) * 4);
|
|
26
|
+
const styles = take((a) => new Uint16Array(buffer, a, n), n * 2);
|
|
27
|
+
const text = new TextDecoder().decode(new Uint8Array(buffer, at));
|
|
28
|
+
return {
|
|
29
|
+
rowStart,
|
|
30
|
+
colStart,
|
|
31
|
+
rows,
|
|
32
|
+
cols,
|
|
33
|
+
kinds,
|
|
34
|
+
numbers,
|
|
35
|
+
text,
|
|
36
|
+
offsets,
|
|
37
|
+
styles,
|
|
38
|
+
colors,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* `[rowStart, rowEnd) x [colStart, colEnd)` of a window holding the whole sheet,
|
|
43
|
+
* clamped to it: what papyra's `Sheet.window` asks the addon for, answered here
|
|
44
|
+
* without a round trip.
|
|
45
|
+
*/
|
|
46
|
+
function slice(all, rowStart, rowEnd, colStart, colEnd) {
|
|
47
|
+
const r1 = Math.min(rowEnd, all.rows);
|
|
48
|
+
const c1 = Math.min(colEnd, all.cols);
|
|
49
|
+
const r0 = Math.min(rowStart, r1);
|
|
50
|
+
const c0 = Math.min(colStart, c1);
|
|
51
|
+
const rows = r1 - r0;
|
|
52
|
+
const cols = c1 - c0;
|
|
53
|
+
const n = rows * cols;
|
|
54
|
+
const out = {
|
|
55
|
+
rowStart: r0,
|
|
56
|
+
colStart: c0,
|
|
57
|
+
rows,
|
|
58
|
+
cols,
|
|
59
|
+
kinds: new Uint8Array(n),
|
|
60
|
+
numbers: new Float64Array(n),
|
|
61
|
+
styles: new Uint16Array(n),
|
|
62
|
+
colors: new Uint32Array(n),
|
|
63
|
+
offsets: new Uint32Array(n + 1),
|
|
64
|
+
text: '',
|
|
65
|
+
};
|
|
66
|
+
const parts = [];
|
|
67
|
+
let length = 0;
|
|
68
|
+
for (let r = 0; r < rows; r++) {
|
|
69
|
+
const from = (r0 + r) * all.cols + c0;
|
|
70
|
+
const to = from + cols;
|
|
71
|
+
const into = r * cols;
|
|
72
|
+
out.kinds.set(all.kinds.subarray(from, to), into);
|
|
73
|
+
out.numbers.set(all.numbers.subarray(from, to), into);
|
|
74
|
+
out.styles.set(all.styles.subarray(from, to), into);
|
|
75
|
+
out.colors.set(all.colors.subarray(from, to), into);
|
|
76
|
+
const base = all.offsets[from] ?? 0;
|
|
77
|
+
for (let c = 0; c < cols; c++) {
|
|
78
|
+
out.offsets[into + c] = length + (all.offsets[from + c] ?? 0) - base;
|
|
79
|
+
}
|
|
80
|
+
const end = all.offsets[to] ?? base;
|
|
81
|
+
parts.push(all.text.slice(base, end));
|
|
82
|
+
length += end - base;
|
|
83
|
+
}
|
|
84
|
+
out.offsets[n] = length;
|
|
85
|
+
out.text = parts.join('');
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* papyra's `openWorkbook`, parsed in the Tauri app's host process through
|
|
90
|
+
* `tauri-plugin-papyra` with its `tables` feature, and allowed by `papyra:tables`.
|
|
91
|
+
*
|
|
92
|
+
* Resolves to papyra's own `Workbook`, so `spreadsheetRenderer.with({ open:
|
|
93
|
+
* openWorkbook })` and `csvRenderer`'s show it unchanged. A grid reads a sheet's
|
|
94
|
+
* cells synchronously, a window per frame, and IPC is not synchronous — so each
|
|
95
|
+
* sheet's cells come over **whole**, in one binary call when the sheet is first
|
|
96
|
+
* read, and windows are cut from that copy. That is about 19 bytes a cell plus its
|
|
97
|
+
* text, held in the host and here, over the sheet's whole rectangle — so the host
|
|
98
|
+
* cuts a sheet to its first rows, marked `truncated`, past a million cells (~19 MB)
|
|
99
|
+
* or past sixteen times the cells it really holds. A lower `maxCells` lowers the
|
|
100
|
+
* first.
|
|
101
|
+
*
|
|
102
|
+
* The workbook stays in the host until `close()`.
|
|
103
|
+
*/
|
|
104
|
+
export function openWorkbook(source, options = {}) {
|
|
105
|
+
return openWorkbookWith(async (bytes, options) => {
|
|
106
|
+
const { id, ...book } = await invoke(command('open_workbook'), bytes, {
|
|
107
|
+
headers: {
|
|
108
|
+
// JSON in a header: ASCII, so percent-encoded like the password.
|
|
109
|
+
'x-papyra-options': encodeURIComponent(JSON.stringify(options)),
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
return {
|
|
113
|
+
...book,
|
|
114
|
+
async sheet(index) {
|
|
115
|
+
const meta = await invoke(command('sheet'), { id, index });
|
|
116
|
+
const all = unpack(await invoke(command('window'), {
|
|
117
|
+
id,
|
|
118
|
+
sheet: index,
|
|
119
|
+
rowStart: 0,
|
|
120
|
+
rowEnd: meta.rows,
|
|
121
|
+
colStart: 0,
|
|
122
|
+
colEnd: meta.cols,
|
|
123
|
+
}));
|
|
124
|
+
return {
|
|
125
|
+
...meta,
|
|
126
|
+
merges: Uint32Array.from(meta.merges),
|
|
127
|
+
layout: meta.layout && {
|
|
128
|
+
...meta.layout,
|
|
129
|
+
cols: Float64Array.from(meta.layout.cols),
|
|
130
|
+
rows: Float64Array.from(meta.layout.rows),
|
|
131
|
+
},
|
|
132
|
+
window: (r0, r1, c0, c1) => slice(all, r0, r1, c0, c1),
|
|
133
|
+
};
|
|
134
|
+
},
|
|
135
|
+
close: () => closeDocument(id),
|
|
136
|
+
};
|
|
137
|
+
}, source, options);
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=workbook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workbook.js","sourceRoot":"","sources":["../src/workbook.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@build-qube/papyra-tauri",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "A Tauri engine for papyra's viewer: PDFs parsed and rendered in the app's host process by tauri-plugin-papyra",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/BuildQube/papyra.git",
|
|
9
|
+
"directory": "packages/tauri"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"pdf",
|
|
13
|
+
"tauri",
|
|
14
|
+
"papyra",
|
|
15
|
+
"viewer",
|
|
16
|
+
"mobile"
|
|
17
|
+
],
|
|
18
|
+
"type": "module",
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"default": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"registry": "https://registry.npmjs.org/",
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc -p tsconfig.json",
|
|
36
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
37
|
+
"clean": "rm -rf dist *.tsbuildinfo",
|
|
38
|
+
"test:integration": "bun test test/integration"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@build-qube/papyra": "^0.6.0",
|
|
42
|
+
"@tauri-apps/api": "^2.0.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@build-qube/papyra": "0.6.0",
|
|
46
|
+
"@tauri-apps/api": "^2.9.0",
|
|
47
|
+
"typescript": "^7.0.2"
|
|
48
|
+
}
|
|
49
|
+
}
|