quickwin 2026.5.2-3.145209
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 +6 -0
- package/examples/pdf_preview.js +440 -0
- package/examples/pdf_preview.ts +470 -0
- package/examples/preact_demo.js +35 -0
- package/examples/preact_demo.tsx +49 -0
- package/examples/tray_demo.js +75 -0
- package/examples/tray_demo.tsx +79 -0
- package/lib/fetch.js +746 -0
- package/lib/fetch.ts +811 -0
- package/lib/polyfill.js +500 -0
- package/lib/polyfill.ts +454 -0
- package/lib/preact/hooks.js +287 -0
- package/lib/preact/hooks.ts +330 -0
- package/lib/preact/jsx-runtime.js +1 -0
- package/lib/preact/jsx-runtime.ts +2 -0
- package/lib/preact/jsx.d.ts +36 -0
- package/lib/preact/layout.js +153 -0
- package/lib/preact/layout.ts +183 -0
- package/lib/preact/preact.js +54 -0
- package/lib/preact/preact.ts +133 -0
- package/lib/preact/props.js +99 -0
- package/lib/preact/props.ts +119 -0
- package/lib/preact/render.js +320 -0
- package/lib/preact/render.ts +353 -0
- package/lib/websocket.js +540 -0
- package/lib/websocket.ts +574 -0
- package/package.json +32 -0
- package/quickwin.d.ts +657 -0
- package/test/add.wasm +0 -0
- package/test/complex.wasm +0 -0
- package/test/complex_imports.wasm +0 -0
- package/test/global_imports.wasm +0 -0
- package/test/import_func.wasm +0 -0
- package/test/imports.wasm +0 -0
- package/test/run.js +86 -0
- package/test/run.ts +90 -0
- package/test/sjlj.wasm +0 -0
- package/test/test_basic.js +7 -0
- package/test/test_basic.ts +9 -0
- package/test/test_brotli.js +48 -0
- package/test/test_brotli.ts +52 -0
- package/test/test_fetch_cache.js +131 -0
- package/test/test_fetch_cache.ts +141 -0
- package/test/test_ffi.js +157 -0
- package/test/test_ffi.ts +174 -0
- package/test/test_frame_encoding.js +128 -0
- package/test/test_frame_encoding.ts +132 -0
- package/test/test_helper.js +84 -0
- package/test/test_helper.ts +80 -0
- package/test/test_http_import.js +78 -0
- package/test/test_http_import.ts +74 -0
- package/test/test_mupdf_render.js +69 -0
- package/test/test_mupdf_render.ts +74 -0
- package/test/test_mupdf_twice.js +77 -0
- package/test/test_mupdf_twice.ts +81 -0
- package/test/test_mupdf_wasm.js +33 -0
- package/test/test_mupdf_wasm.ts +30 -0
- package/test/test_net_event.js +63 -0
- package/test/test_net_event.ts +59 -0
- package/test/test_net_fetch.js +153 -0
- package/test/test_net_fetch.ts +131 -0
- package/test/test_net_websocket.js +158 -0
- package/test/test_net_websocket.ts +144 -0
- package/test/test_polyfill.js +58 -0
- package/test/test_polyfill.ts +60 -0
- package/test/test_url.js +173 -0
- package/test/test_url.ts +183 -0
- package/test/test_wasm_basic.js +82 -0
- package/test/test_wasm_basic.ts +70 -0
- package/test/test_wasm_import_global.js +41 -0
- package/test/test_wasm_import_global.ts +39 -0
- package/test/test_wasm_sjlj.js +153 -0
- package/test/test_wasm_sjlj.ts +134 -0
- package/test/test_wasm_types.js +96 -0
- package/test/test_wasm_types.ts +108 -0
- package/test/types.wasm +0 -0
- package/tsconfig.json +18 -0
- package/vendor/mupdf-wasm/mupdf-wasm.d.ts +571 -0
- package/vendor/mupdf-wasm/mupdf-wasm.js +2749 -0
- package/vendor/mupdf-wasm/mupdf-wasm.wasm +0 -0
- package/vendor/mupdf-wasm/mupdf.d.ts +939 -0
- package/vendor/mupdf-wasm/mupdf.js +3317 -0
- package/win-mingw64.exe +0 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as std from 'std'
|
|
2
|
+
|
|
3
|
+
const GREEN = '\x1b[32m'
|
|
4
|
+
const RED = '\x1b[31m'
|
|
5
|
+
const BOLD = '\x1b[1m'
|
|
6
|
+
const RESET = '\x1b[0m'
|
|
7
|
+
|
|
8
|
+
function formatDuration(ms: number): string {
|
|
9
|
+
if (ms >= 1000) return (ms / 1000).toFixed(2) + 's'
|
|
10
|
+
return ms + 'ms'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function readWasmFile(path: string): ArrayBuffer | null {
|
|
14
|
+
const base = import.meta.url.slice(0, import.meta.url.lastIndexOf('/') + 1)
|
|
15
|
+
const parts = (base + path).split('/')
|
|
16
|
+
const out: string[] = []
|
|
17
|
+
for (const p of parts) {
|
|
18
|
+
if (p === '..') out.pop()
|
|
19
|
+
else if (p !== '.') out.push(p)
|
|
20
|
+
}
|
|
21
|
+
let filePath = out.join('/').slice(7)
|
|
22
|
+
if (filePath.length >= 3 && filePath[1] === ':') filePath = filePath.slice(1)
|
|
23
|
+
const fp = std.open(filePath, 'rb')
|
|
24
|
+
if (!fp) return null
|
|
25
|
+
fp.seek(0, 2)
|
|
26
|
+
const size = fp.tell()
|
|
27
|
+
fp.seek(0, 0)
|
|
28
|
+
const buffer = new ArrayBuffer(size)
|
|
29
|
+
fp.read(buffer, 0, size)
|
|
30
|
+
fp.close()
|
|
31
|
+
return buffer
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class Tester {
|
|
35
|
+
ok = 0
|
|
36
|
+
fail = 0
|
|
37
|
+
private startTime = Date.now()
|
|
38
|
+
private sectionStart = this.startTime
|
|
39
|
+
private lastSection = ''
|
|
40
|
+
|
|
41
|
+
section(name: string): void {
|
|
42
|
+
const now = Date.now()
|
|
43
|
+
if (this.lastSection) {
|
|
44
|
+
const elapsed = now - this.sectionStart
|
|
45
|
+
std.printf(' (%s)\n', formatDuration(elapsed))
|
|
46
|
+
}
|
|
47
|
+
this.sectionStart = now
|
|
48
|
+
this.lastSection = name
|
|
49
|
+
std.printf('\n%s=== %s ===%s\n', BOLD, name, RESET)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
check(name: string, expected: unknown, actual: unknown): void {
|
|
53
|
+
if (typeof expected === 'number' && typeof actual === 'number') {
|
|
54
|
+
if (Math.abs(expected - actual) < 0.0001) {
|
|
55
|
+
this.ok++; std.printf(' %sPASS:%s %s = %s\n', GREEN, RESET, name, String(actual))
|
|
56
|
+
} else {
|
|
57
|
+
this.fail++; std.printf(' %sFAIL:%s %s = %s (expected %s)\n', RED, RESET, name, String(actual), String(expected))
|
|
58
|
+
}
|
|
59
|
+
} else if (expected === actual) {
|
|
60
|
+
this.ok++; std.printf(' %sPASS:%s %s = %s\n', GREEN, RESET, name, String(actual))
|
|
61
|
+
} else {
|
|
62
|
+
this.fail++; std.printf(' %sFAIL:%s %s = %s (expected %s)\n', RED, RESET, name, String(actual), String(expected))
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
checkTrue(name: string, actual: boolean): void {
|
|
67
|
+
this.check(name, true, actual)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
summary(): void {
|
|
71
|
+
const now = Date.now()
|
|
72
|
+
if (this.lastSection) {
|
|
73
|
+
const elapsed = now - this.sectionStart
|
|
74
|
+
std.printf(' (%s)\n', formatDuration(elapsed))
|
|
75
|
+
}
|
|
76
|
+
const total = now - this.startTime
|
|
77
|
+
const color = this.fail > 0 ? RED : GREEN
|
|
78
|
+
std.printf('\n%s%d/%d passed (%s)%s\n', color, this.ok, this.ok + this.fail, formatDuration(total), RESET)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as std from 'std';
|
|
2
|
+
import * as os from 'os';
|
|
3
|
+
function getCacheDir() {
|
|
4
|
+
// import.meta.url: file:///C:/.../_build/test/file.js
|
|
5
|
+
const url = import.meta.url;
|
|
6
|
+
// Strip file:// (7 chars), giving /C:/.../
|
|
7
|
+
let path = url.slice(7);
|
|
8
|
+
// Remove leading / before drive letter
|
|
9
|
+
if (path.length >= 3 && path[1] === ':')
|
|
10
|
+
path = path.slice(1);
|
|
11
|
+
// path now: C:/Users/Docker/quickwin/_build/test/file.js
|
|
12
|
+
// Go up to _build/
|
|
13
|
+
const idx = path.lastIndexOf('/');
|
|
14
|
+
if (idx < 0)
|
|
15
|
+
return '_cache';
|
|
16
|
+
const buildDir = path.slice(0, idx); // .../_build/test
|
|
17
|
+
const idx2 = buildDir.lastIndexOf('/');
|
|
18
|
+
if (idx2 < 0)
|
|
19
|
+
return '_cache';
|
|
20
|
+
return buildDir.slice(0, idx2 + 1) + '_cache'; // .../_build/_cache
|
|
21
|
+
}
|
|
22
|
+
export const suite = {
|
|
23
|
+
name: 'http-import',
|
|
24
|
+
run: async (t) => {
|
|
25
|
+
function assert(name, ok) {
|
|
26
|
+
if (ok) {
|
|
27
|
+
t.ok++;
|
|
28
|
+
std.printf(' PASS: %s\n', name);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
t.fail++;
|
|
32
|
+
std.printf(' FAIL: %s\n', name);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const url = 'https://esm.sh/left-pad@1.3.0';
|
|
36
|
+
const cacheDir = getCacheDir();
|
|
37
|
+
t.section('list cache before');
|
|
38
|
+
const [before, err] = os.readdir(cacheDir);
|
|
39
|
+
if (err !== 0) {
|
|
40
|
+
std.printf(' cache dir: %s (error %d, will be created on first request)\n', cacheDir, err);
|
|
41
|
+
}
|
|
42
|
+
const beforeCount = err === 0 ? before.length : 0;
|
|
43
|
+
t.section('import module');
|
|
44
|
+
let mod;
|
|
45
|
+
try {
|
|
46
|
+
mod = await import(url);
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
assert('import succeeds', false);
|
|
50
|
+
std.printf(' ERROR: %s\n', String(e));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
assert('import succeeds', true);
|
|
54
|
+
assert('default is function', typeof mod.default === 'function');
|
|
55
|
+
assert('left-pad works', mod.default('hello', 8) === ' hello');
|
|
56
|
+
t.section('check cache');
|
|
57
|
+
const [after, err2] = os.readdir(cacheDir);
|
|
58
|
+
assert('cache dir readable', err2 === 0);
|
|
59
|
+
if (err2 !== 0)
|
|
60
|
+
return;
|
|
61
|
+
assert('cache has body files', after.some(function (f) { return f.endsWith('.body'); }));
|
|
62
|
+
assert('cache has meta files', after.some(function (f) { return f.endsWith('.meta'); }));
|
|
63
|
+
for (const f of after) {
|
|
64
|
+
if (f.endsWith('.meta') && (err !== 0 || before.indexOf(f) < 0)) {
|
|
65
|
+
std.printf(' meta: %s\n', f);
|
|
66
|
+
const fp = std.open(cacheDir + '/' + f, 'r');
|
|
67
|
+
if (fp) {
|
|
68
|
+
const metaStr = fp.readAsString();
|
|
69
|
+
fp.close();
|
|
70
|
+
if (metaStr) {
|
|
71
|
+
const meta = JSON.parse(metaStr);
|
|
72
|
+
std.printf(' storedAt: %d, maxAge: %d\n', meta.storedAt, meta.maxAge);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as std from 'std'
|
|
2
|
+
import * as os from 'os'
|
|
3
|
+
import { Tester } from './test_helper.js'
|
|
4
|
+
|
|
5
|
+
function getCacheDir(): string {
|
|
6
|
+
// import.meta.url: file:///C:/.../_build/test/file.js
|
|
7
|
+
const url = import.meta.url
|
|
8
|
+
// Strip file:// (7 chars), giving /C:/.../
|
|
9
|
+
let path = url.slice(7)
|
|
10
|
+
// Remove leading / before drive letter
|
|
11
|
+
if (path.length >= 3 && path[1] === ':') path = path.slice(1)
|
|
12
|
+
// path now: C:/Users/Docker/quickwin/_build/test/file.js
|
|
13
|
+
// Go up to _build/
|
|
14
|
+
const idx = path.lastIndexOf('/')
|
|
15
|
+
if (idx < 0) return '_cache'
|
|
16
|
+
const buildDir = path.slice(0, idx) // .../_build/test
|
|
17
|
+
const idx2 = buildDir.lastIndexOf('/')
|
|
18
|
+
if (idx2 < 0) return '_cache'
|
|
19
|
+
return buildDir.slice(0, idx2 + 1) + '_cache' // .../_build/_cache
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const suite = {
|
|
23
|
+
name: 'http-import',
|
|
24
|
+
run: async (t: Tester) => {
|
|
25
|
+
function assert(name: string, ok: boolean): void {
|
|
26
|
+
if (ok) { t.ok++; std.printf(' PASS: %s\n', name) }
|
|
27
|
+
else { t.fail++; std.printf(' FAIL: %s\n', name) }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const url = 'https://esm.sh/left-pad@1.3.0'
|
|
31
|
+
const cacheDir = getCacheDir()
|
|
32
|
+
|
|
33
|
+
t.section('list cache before')
|
|
34
|
+
const [before, err] = os.readdir(cacheDir)
|
|
35
|
+
if (err !== 0) {
|
|
36
|
+
std.printf(' cache dir: %s (error %d, will be created on first request)\n', cacheDir, err)
|
|
37
|
+
}
|
|
38
|
+
const beforeCount = err === 0 ? before.length : 0
|
|
39
|
+
|
|
40
|
+
t.section('import module')
|
|
41
|
+
let mod: any
|
|
42
|
+
try {
|
|
43
|
+
mod = await import(url)
|
|
44
|
+
} catch (e) {
|
|
45
|
+
assert('import succeeds', false)
|
|
46
|
+
std.printf(' ERROR: %s\n', String(e))
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
assert('import succeeds', true)
|
|
50
|
+
assert('default is function', typeof mod.default === 'function')
|
|
51
|
+
assert('left-pad works', mod.default('hello', 8) === ' hello')
|
|
52
|
+
|
|
53
|
+
t.section('check cache')
|
|
54
|
+
const [after, err2] = os.readdir(cacheDir)
|
|
55
|
+
assert('cache dir readable', err2 === 0)
|
|
56
|
+
if (err2 !== 0) return
|
|
57
|
+
assert('cache has body files', after.some(function(f: string) { return f.endsWith('.body') }))
|
|
58
|
+
assert('cache has meta files', after.some(function(f: string) { return f.endsWith('.meta') }))
|
|
59
|
+
for (const f of after) {
|
|
60
|
+
if (f.endsWith('.meta') && (err !== 0 || before.indexOf(f) < 0)) {
|
|
61
|
+
std.printf(' meta: %s\n', f)
|
|
62
|
+
const fp = std.open(cacheDir + '/' + f, 'r')
|
|
63
|
+
if (fp) {
|
|
64
|
+
const metaStr = fp.readAsString()
|
|
65
|
+
fp.close()
|
|
66
|
+
if (metaStr) {
|
|
67
|
+
const meta = JSON.parse(metaStr)
|
|
68
|
+
std.printf(' storedAt: %d, maxAge: %d\n', meta.storedAt, meta.maxAge)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as std from 'std';
|
|
2
|
+
import '../lib/polyfill.js';
|
|
3
|
+
function loadWasmBytes(path) {
|
|
4
|
+
const fp = std.open(path, 'rb');
|
|
5
|
+
if (!fp)
|
|
6
|
+
throw new Error('Cannot open: ' + path);
|
|
7
|
+
fp.seek(0, 2);
|
|
8
|
+
const size = fp.tell();
|
|
9
|
+
fp.seek(0, 0);
|
|
10
|
+
const buffer = new ArrayBuffer(size);
|
|
11
|
+
fp.read(buffer, 0, size);
|
|
12
|
+
fp.close();
|
|
13
|
+
return buffer;
|
|
14
|
+
}
|
|
15
|
+
function setupMupdfModule(wasmPath) {
|
|
16
|
+
const wasmBinary = loadWasmBytes(wasmPath);
|
|
17
|
+
globalThis["$libmupdf_wasm_Module"] = {
|
|
18
|
+
wasmBinary,
|
|
19
|
+
locateFile: (p) => p
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export const suite = {
|
|
23
|
+
name: 'mupdf-render',
|
|
24
|
+
run: async (t) => {
|
|
25
|
+
try {
|
|
26
|
+
setupMupdfModule('./vendor/mupdf-wasm/mupdf-wasm.wasm');
|
|
27
|
+
const mupdf = await import('../vendor/mupdf-wasm/mupdf.js');
|
|
28
|
+
const fp = std.open('./example.pdf', 'rb');
|
|
29
|
+
if (!fp) {
|
|
30
|
+
console.log('SKIP: no example.pdf');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
fp.seek(0, 2);
|
|
34
|
+
const size = fp.tell();
|
|
35
|
+
fp.seek(0, 0);
|
|
36
|
+
const buf = new ArrayBuffer(size);
|
|
37
|
+
fp.read(buf, 0, size);
|
|
38
|
+
fp.close();
|
|
39
|
+
console.log('pdf size:', size);
|
|
40
|
+
const doc = mupdf.Document.openDocument(new Uint8Array(buf), "application/pdf");
|
|
41
|
+
console.log('pages:', doc.countPages());
|
|
42
|
+
const page = doc.loadPage(0);
|
|
43
|
+
const bounds = page.getBounds();
|
|
44
|
+
if (bounds && bounds.length === 4)
|
|
45
|
+
console.log('page size:', bounds[2] - bounds[0], 'x', bounds[3] - bounds[1]);
|
|
46
|
+
const pixmap = page.toPixmap(mupdf.Matrix.scale(2, 2), mupdf.ColorSpace.DeviceRGB, false);
|
|
47
|
+
if (pixmap) {
|
|
48
|
+
console.log('pixmap:', pixmap.getWidth(), 'x', pixmap.getHeight());
|
|
49
|
+
const png = pixmap.asPNG();
|
|
50
|
+
console.log('png size:', png.byteLength);
|
|
51
|
+
const out = std.open('./output.png', 'wb');
|
|
52
|
+
if (out) {
|
|
53
|
+
const pngBuf = new ArrayBuffer(png.byteLength);
|
|
54
|
+
new Uint8Array(pngBuf).set(png);
|
|
55
|
+
out.write(pngBuf, 0, png.byteLength);
|
|
56
|
+
out.close();
|
|
57
|
+
}
|
|
58
|
+
console.log('saved to output.png');
|
|
59
|
+
}
|
|
60
|
+
doc.destroy();
|
|
61
|
+
console.log('done');
|
|
62
|
+
t.ok++;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
console.log('ERROR:', error);
|
|
66
|
+
t.fail++;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as std from 'std'
|
|
2
|
+
import { Tester } from './test_helper.js'
|
|
3
|
+
import '../lib/polyfill.js'
|
|
4
|
+
|
|
5
|
+
function loadWasmBytes(path: string): ArrayBuffer {
|
|
6
|
+
const fp = std.open(path, 'rb')
|
|
7
|
+
if (!fp) throw new Error('Cannot open: ' + path)
|
|
8
|
+
fp.seek(0, 2)
|
|
9
|
+
const size = fp.tell()
|
|
10
|
+
fp.seek(0, 0)
|
|
11
|
+
const buffer = new ArrayBuffer(size)
|
|
12
|
+
fp.read(buffer, 0, size)
|
|
13
|
+
fp.close()
|
|
14
|
+
return buffer
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function setupMupdfModule(wasmPath: string) {
|
|
18
|
+
const wasmBinary = loadWasmBytes(wasmPath)
|
|
19
|
+
;(globalThis as any)["$libmupdf_wasm_Module"] = {
|
|
20
|
+
wasmBinary,
|
|
21
|
+
locateFile: (p: string) => p
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const suite = {
|
|
26
|
+
name: 'mupdf-render',
|
|
27
|
+
run: async (t: Tester): Promise<void> => {
|
|
28
|
+
try {
|
|
29
|
+
setupMupdfModule('./vendor/mupdf-wasm/mupdf-wasm.wasm')
|
|
30
|
+
|
|
31
|
+
const mupdf: typeof import('../vendor/mupdf-wasm/mupdf.js') = await import('../vendor/mupdf-wasm/mupdf.js')
|
|
32
|
+
|
|
33
|
+
const fp = std.open('./example.pdf', 'rb')
|
|
34
|
+
if (!fp) { console.log('SKIP: no example.pdf'); return }
|
|
35
|
+
fp.seek(0, 2)
|
|
36
|
+
const size = fp.tell()
|
|
37
|
+
fp.seek(0, 0)
|
|
38
|
+
const buf = new ArrayBuffer(size)
|
|
39
|
+
fp.read(buf, 0, size)
|
|
40
|
+
fp.close()
|
|
41
|
+
console.log('pdf size:', size)
|
|
42
|
+
|
|
43
|
+
const doc = mupdf.Document.openDocument(new Uint8Array(buf), "application/pdf")
|
|
44
|
+
console.log('pages:', doc.countPages())
|
|
45
|
+
|
|
46
|
+
const page = doc.loadPage(0)
|
|
47
|
+
const bounds = page.getBounds()
|
|
48
|
+
if (bounds && bounds.length === 4)
|
|
49
|
+
console.log('page size:', bounds[2] - bounds[0], 'x', bounds[3] - bounds[1])
|
|
50
|
+
|
|
51
|
+
const pixmap = page.toPixmap(mupdf.Matrix.scale(2, 2), mupdf.ColorSpace.DeviceRGB, false)
|
|
52
|
+
if (pixmap) {
|
|
53
|
+
console.log('pixmap:', pixmap.getWidth(), 'x', pixmap.getHeight())
|
|
54
|
+
const png = pixmap.asPNG()
|
|
55
|
+
console.log('png size:', png.byteLength)
|
|
56
|
+
const out = std.open('./output.png', 'wb')
|
|
57
|
+
if (out) {
|
|
58
|
+
const pngBuf = new ArrayBuffer(png.byteLength)
|
|
59
|
+
new Uint8Array(pngBuf).set(png)
|
|
60
|
+
out.write(pngBuf, 0, png.byteLength)
|
|
61
|
+
out.close()
|
|
62
|
+
}
|
|
63
|
+
console.log('saved to output.png')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
doc.destroy()
|
|
67
|
+
console.log('done')
|
|
68
|
+
t.ok++
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.log('ERROR:', error)
|
|
71
|
+
t.fail++
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import '../lib/polyfill.js';
|
|
2
|
+
import * as std from 'std';
|
|
3
|
+
function loadWasmBytes(path) {
|
|
4
|
+
const fp = std.open(path, 'rb');
|
|
5
|
+
if (!fp)
|
|
6
|
+
throw new Error('Cannot open: ' + path);
|
|
7
|
+
fp.seek(0, 2);
|
|
8
|
+
const size = fp.tell();
|
|
9
|
+
fp.seek(0, 0);
|
|
10
|
+
const buffer = new ArrayBuffer(size);
|
|
11
|
+
fp.read(buffer, 0, size);
|
|
12
|
+
fp.close();
|
|
13
|
+
return buffer;
|
|
14
|
+
}
|
|
15
|
+
export const suite = {
|
|
16
|
+
name: 'mupdf-twice',
|
|
17
|
+
run: async (t) => {
|
|
18
|
+
try {
|
|
19
|
+
console.log(' Loading WASM binary...');
|
|
20
|
+
const wasmBinary = loadWasmBytes('./vendor/mupdf-wasm/mupdf-wasm.wasm');
|
|
21
|
+
console.log(' WASM binary loaded, size:', wasmBinary.byteLength);
|
|
22
|
+
globalThis["$libmupdf_wasm_Module"] = {
|
|
23
|
+
wasmBinary,
|
|
24
|
+
locateFile: (p) => p
|
|
25
|
+
};
|
|
26
|
+
console.log(' Importing mupdf module...');
|
|
27
|
+
const mupdf = await import('../vendor/mupdf-wasm/mupdf.js');
|
|
28
|
+
console.log(' mupdf module imported ok');
|
|
29
|
+
const fp = std.open('./example.pdf', 'rb');
|
|
30
|
+
if (!fp) {
|
|
31
|
+
console.log('SKIP: no example.pdf');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
fp.seek(0, 2);
|
|
35
|
+
const size = fp.tell();
|
|
36
|
+
fp.seek(0, 0);
|
|
37
|
+
const pdfBuf = new ArrayBuffer(size);
|
|
38
|
+
fp.read(pdfBuf, 0, size);
|
|
39
|
+
fp.close();
|
|
40
|
+
console.log(' PDF loaded, size:', size);
|
|
41
|
+
t.section('Test 1: two toPixmap on same page (no destroy)');
|
|
42
|
+
try {
|
|
43
|
+
const doc = mupdf.Document.openDocument(new Uint8Array(pdfBuf), "application/pdf");
|
|
44
|
+
const page = doc.loadPage(0);
|
|
45
|
+
console.log(' 1: toPixmap #1...');
|
|
46
|
+
const pm1 = page.toPixmap(mupdf.Matrix.scale(1, 1), mupdf.ColorSpace.DeviceRGB, false);
|
|
47
|
+
console.log(' 1: toPixmap #1 done:', pm1.getWidth(), 'x', pm1.getHeight());
|
|
48
|
+
t.check('toPixmap #1 ok', true, pm1 !== null);
|
|
49
|
+
console.log(' 1: toPixmap #2...');
|
|
50
|
+
const pm2 = page.toPixmap(mupdf.Matrix.scale(1, 1), mupdf.ColorSpace.DeviceRGB, false);
|
|
51
|
+
console.log(' 1: toPixmap #2 done:', pm2.getWidth(), 'x', pm2.getHeight());
|
|
52
|
+
t.check('toPixmap #2 ok', true, pm2 !== null);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
console.log(' 1: ERROR:', e instanceof Error ? e.message : String(e));
|
|
56
|
+
}
|
|
57
|
+
t.section('Test 2: second openDocument + toPixmap (no destroy of first)');
|
|
58
|
+
try {
|
|
59
|
+
console.log(' 2: openDocument #2...');
|
|
60
|
+
const doc2 = mupdf.Document.openDocument(new Uint8Array(pdfBuf), "application/pdf");
|
|
61
|
+
console.log(' 2: loadPage #2...');
|
|
62
|
+
const page2 = doc2.loadPage(0);
|
|
63
|
+
console.log(' 2: toPixmap #3...');
|
|
64
|
+
const pm3 = page2.toPixmap(mupdf.Matrix.scale(1, 1), mupdf.ColorSpace.DeviceRGB, false);
|
|
65
|
+
console.log(' 2: toPixmap #3 done:', pm3.getWidth(), 'x', pm3.getHeight());
|
|
66
|
+
t.check('toPixmap #3 ok', true, pm3 !== null);
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
console.log(' 2: ERROR:', e instanceof Error ? e.message : String(e));
|
|
70
|
+
}
|
|
71
|
+
console.log('All mupdf-twice tests done');
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
console.log('TOP-LEVEL ERROR:', e instanceof Error ? e.message : String(e));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import '../lib/polyfill.js'
|
|
2
|
+
import * as std from 'std'
|
|
3
|
+
import { Tester } from './test_helper.js'
|
|
4
|
+
|
|
5
|
+
function loadWasmBytes(path: string): ArrayBuffer {
|
|
6
|
+
const fp = std.open(path, 'rb')
|
|
7
|
+
if (!fp) throw new Error('Cannot open: ' + path)
|
|
8
|
+
fp.seek(0, 2)
|
|
9
|
+
const size = fp.tell()
|
|
10
|
+
fp.seek(0, 0)
|
|
11
|
+
const buffer = new ArrayBuffer(size)
|
|
12
|
+
fp.read(buffer, 0, size)
|
|
13
|
+
fp.close()
|
|
14
|
+
return buffer
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const suite = {
|
|
18
|
+
name: 'mupdf-twice',
|
|
19
|
+
run: async (t: Tester): Promise<void> => {
|
|
20
|
+
try {
|
|
21
|
+
console.log(' Loading WASM binary...')
|
|
22
|
+
const wasmBinary = loadWasmBytes('./vendor/mupdf-wasm/mupdf-wasm.wasm')
|
|
23
|
+
console.log(' WASM binary loaded, size:', wasmBinary.byteLength)
|
|
24
|
+
|
|
25
|
+
;(globalThis as any)["$libmupdf_wasm_Module"] = {
|
|
26
|
+
wasmBinary,
|
|
27
|
+
locateFile: (p: string) => p
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
console.log(' Importing mupdf module...')
|
|
31
|
+
const mupdf: typeof import('../vendor/mupdf-wasm/mupdf.js') = await import('../vendor/mupdf-wasm/mupdf.js')
|
|
32
|
+
console.log(' mupdf module imported ok')
|
|
33
|
+
|
|
34
|
+
const fp = std.open('./example.pdf', 'rb')
|
|
35
|
+
if (!fp) { console.log('SKIP: no example.pdf'); return }
|
|
36
|
+
fp.seek(0, 2)
|
|
37
|
+
const size = fp.tell()
|
|
38
|
+
fp.seek(0, 0)
|
|
39
|
+
const pdfBuf = new ArrayBuffer(size)
|
|
40
|
+
fp.read(pdfBuf, 0, size)
|
|
41
|
+
fp.close()
|
|
42
|
+
console.log(' PDF loaded, size:', size)
|
|
43
|
+
|
|
44
|
+
t.section('Test 1: two toPixmap on same page (no destroy)')
|
|
45
|
+
try {
|
|
46
|
+
const doc = mupdf.Document.openDocument(new Uint8Array(pdfBuf), "application/pdf")
|
|
47
|
+
const page = doc.loadPage(0)
|
|
48
|
+
|
|
49
|
+
console.log(' 1: toPixmap #1...')
|
|
50
|
+
const pm1 = page.toPixmap(mupdf.Matrix.scale(1, 1), mupdf.ColorSpace.DeviceRGB, false)
|
|
51
|
+
console.log(' 1: toPixmap #1 done:', pm1.getWidth(), 'x', pm1.getHeight())
|
|
52
|
+
t.check('toPixmap #1 ok', true, pm1 !== null)
|
|
53
|
+
|
|
54
|
+
console.log(' 1: toPixmap #2...')
|
|
55
|
+
const pm2 = page.toPixmap(mupdf.Matrix.scale(1, 1), mupdf.ColorSpace.DeviceRGB, false)
|
|
56
|
+
console.log(' 1: toPixmap #2 done:', pm2.getWidth(), 'x', pm2.getHeight())
|
|
57
|
+
t.check('toPixmap #2 ok', true, pm2 !== null)
|
|
58
|
+
} catch (e: any) {
|
|
59
|
+
console.log(' 1: ERROR:', e instanceof Error ? e.message : String(e))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
t.section('Test 2: second openDocument + toPixmap (no destroy of first)')
|
|
63
|
+
try {
|
|
64
|
+
console.log(' 2: openDocument #2...')
|
|
65
|
+
const doc2 = mupdf.Document.openDocument(new Uint8Array(pdfBuf), "application/pdf")
|
|
66
|
+
console.log(' 2: loadPage #2...')
|
|
67
|
+
const page2 = doc2.loadPage(0)
|
|
68
|
+
console.log(' 2: toPixmap #3...')
|
|
69
|
+
const pm3 = page2.toPixmap(mupdf.Matrix.scale(1, 1), mupdf.ColorSpace.DeviceRGB, false)
|
|
70
|
+
console.log(' 2: toPixmap #3 done:', pm3.getWidth(), 'x', pm3.getHeight())
|
|
71
|
+
t.check('toPixmap #3 ok', true, pm3 !== null)
|
|
72
|
+
} catch (e: any) {
|
|
73
|
+
console.log(' 2: ERROR:', e instanceof Error ? e.message : String(e))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log('All mupdf-twice tests done')
|
|
77
|
+
} catch (e: any) {
|
|
78
|
+
console.log('TOP-LEVEL ERROR:', e instanceof Error ? e.message : String(e))
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readWasmFile } from './test_helper.js';
|
|
2
|
+
export const suite = {
|
|
3
|
+
name: 'vendor/mupdf-wasm',
|
|
4
|
+
run: (t) => {
|
|
5
|
+
t.section('validate binary');
|
|
6
|
+
const buf = readWasmFile('../vendor/mupdf-wasm/mupdf-wasm.wasm');
|
|
7
|
+
if (!buf) {
|
|
8
|
+
t.fail++;
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
t.check('file size > 0', true, buf.byteLength > 0);
|
|
12
|
+
const header = new Uint8Array(buf);
|
|
13
|
+
t.check('magic', true, header[0] === 0x00 && header[1] === 0x61 && header[2] === 0x73 && header[3] === 0x6d);
|
|
14
|
+
t.check('version 1', true, header[4] === 0x01 && header[5] === 0x00 && header[6] === 0x00 && header[7] === 0x00);
|
|
15
|
+
t.check('WebAssembly.validate', true, WebAssembly.validate(buf));
|
|
16
|
+
t.section('Module');
|
|
17
|
+
var mod = null;
|
|
18
|
+
try {
|
|
19
|
+
mod = new WebAssembly.Module(buf);
|
|
20
|
+
t.ok++;
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
t.fail++;
|
|
24
|
+
}
|
|
25
|
+
t.section('imports/exports');
|
|
26
|
+
if (mod) {
|
|
27
|
+
const exps = WebAssembly.Module.exports(mod);
|
|
28
|
+
t.check('exports > 0', true, exps.length > 0);
|
|
29
|
+
const imps = WebAssembly.Module.imports(mod);
|
|
30
|
+
t.check('imports', imps.length, imps.length);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Tester, readWasmFile } from './test_helper.js'
|
|
2
|
+
|
|
3
|
+
export const suite = {
|
|
4
|
+
name: 'vendor/mupdf-wasm',
|
|
5
|
+
run: (t: Tester) => {
|
|
6
|
+
t.section('validate binary')
|
|
7
|
+
const buf = readWasmFile('../vendor/mupdf-wasm/mupdf-wasm.wasm')
|
|
8
|
+
if (!buf) { t.fail++; return }
|
|
9
|
+
t.check('file size > 0', true, buf.byteLength > 0)
|
|
10
|
+
const header = new Uint8Array(buf)
|
|
11
|
+
t.check('magic', true, header[0] === 0x00 && header[1] === 0x61 && header[2] === 0x73 && header[3] === 0x6d)
|
|
12
|
+
t.check('version 1', true, header[4] === 0x01 && header[5] === 0x00 && header[6] === 0x00 && header[7] === 0x00)
|
|
13
|
+
t.check('WebAssembly.validate', true, WebAssembly.validate(buf))
|
|
14
|
+
|
|
15
|
+
t.section('Module')
|
|
16
|
+
var mod: WebAssembly.Module | null = null
|
|
17
|
+
try {
|
|
18
|
+
mod = new WebAssembly.Module(buf)
|
|
19
|
+
t.ok++
|
|
20
|
+
} catch (e) { t.fail++ }
|
|
21
|
+
|
|
22
|
+
t.section('imports/exports')
|
|
23
|
+
if (mod) {
|
|
24
|
+
const exps = WebAssembly.Module.exports(mod)
|
|
25
|
+
t.check('exports > 0', true, exps.length > 0)
|
|
26
|
+
const imps = WebAssembly.Module.imports(mod)
|
|
27
|
+
t.check('imports', imps.length, imps.length)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as sock from "sock";
|
|
2
|
+
import * as os from "os";
|
|
3
|
+
export const suite = {
|
|
4
|
+
name: 'net-event',
|
|
5
|
+
run: async (t) => {
|
|
6
|
+
await new Promise((resolve, reject) => {
|
|
7
|
+
const s = sock.socket();
|
|
8
|
+
if (!s || s === 0) {
|
|
9
|
+
t.fail++;
|
|
10
|
+
reject(new Error('socket() failed'));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
var connected = false, gotData = false;
|
|
14
|
+
const hostIP = sock.resolve("httpbin.org");
|
|
15
|
+
if (!hostIP) {
|
|
16
|
+
t.fail++;
|
|
17
|
+
reject(new Error('dns failed'));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const timeoutId = os.setTimeout(() => {
|
|
21
|
+
if (!connected) {
|
|
22
|
+
sock.closesocket(s);
|
|
23
|
+
reject(new Error('connect timeout'));
|
|
24
|
+
}
|
|
25
|
+
}, 10000);
|
|
26
|
+
sock.set_on_event(s, (event) => {
|
|
27
|
+
if (event.lNetworkEvents & sock.FD_CONNECT) {
|
|
28
|
+
if (event.iErrorCode[0] === 0) {
|
|
29
|
+
connected = true;
|
|
30
|
+
t.ok++;
|
|
31
|
+
os.clearTimeout(timeoutId);
|
|
32
|
+
const req = "GET / HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\n\r\n";
|
|
33
|
+
const buf = new ArrayBuffer(req.length);
|
|
34
|
+
const v = new Uint8Array(buf);
|
|
35
|
+
for (let i = 0; i < req.length; i++)
|
|
36
|
+
v[i] = req.charCodeAt(i);
|
|
37
|
+
sock.send(s, buf);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
sock.closesocket(s);
|
|
41
|
+
reject(new Error('connect error: ' + event.iErrorCode[0]));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (event.lNetworkEvents & sock.FD_READ && connected && !gotData) {
|
|
45
|
+
const data = sock.recv(s, 4096);
|
|
46
|
+
if (data && data.byteLength > 0) {
|
|
47
|
+
gotData = true;
|
|
48
|
+
t.ok++;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (event.lNetworkEvents & sock.FD_CLOSE) {
|
|
52
|
+
os.clearTimeout(timeoutId);
|
|
53
|
+
sock.closesocket(s);
|
|
54
|
+
if (gotData && connected)
|
|
55
|
+
resolve();
|
|
56
|
+
else
|
|
57
|
+
reject(new Error('test incomplete'));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
sock.connect(s, hostIP, 80);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
};
|