quickwin 1.0.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.en.md +197 -0
- package/README.md +196 -0
- package/examples/PdfCanvas.js +67 -0
- package/examples/PdfCanvas.tsx +83 -0
- package/examples/pdf_preview2.js +431 -0
- package/examples/pdf_preview2.ts +375 -0
- package/examples/pdf_viewer.js +17623 -0
- package/examples/pdf_viewer.tsx +279 -0
- package/examples/test_gallery.js +17452 -0
- package/examples/test_gallery.tsx +285 -0
- package/examples/test_react_lazy.js +17336 -0
- package/examples/test_react_lazy.tsx +63 -0
- package/examples/test_react_listbox.js +17339 -0
- package/examples/test_react_listbox.tsx +71 -0
- package/examples/test_root_window.js +17313 -0
- package/examples/test_root_window.tsx +41 -0
- package/examples/test_tab.js +17339 -0
- package/examples/test_tab.tsx +75 -0
- package/examples/tray_demo.js +79 -0
- package/examples/tray_demo.tsx +81 -0
- package/lib/assert.js +8 -0
- package/lib/assert.ts +7 -0
- package/lib/cache-utils.js +43 -0
- package/lib/cache-utils.ts +40 -0
- package/lib/dumpRects.js +25 -0
- package/lib/dumpRects.ts +25 -0
- package/lib/fetch.js +490 -0
- package/lib/fetch.ts +502 -0
- package/lib/http-common.js +342 -0
- package/lib/http-common.ts +399 -0
- package/lib/http-server.js +416 -0
- package/lib/http-server.ts +424 -0
- package/lib/polyfill.js +156 -0
- package/lib/polyfill.ts +171 -0
- package/lib/react-qw/components/Button.js +8 -0
- package/lib/react-qw/components/CheckBox.js +31 -0
- package/lib/react-qw/components/ComboBox.js +42 -0
- package/lib/react-qw/components/DateTimePicker.js +96 -0
- package/lib/react-qw/components/Input.js +50 -0
- package/lib/react-qw/components/Link.js +43 -0
- package/lib/react-qw/components/ListBox.js +48 -0
- package/lib/react-qw/components/ListView.js +328 -0
- package/lib/react-qw/components/PathPicker.js +136 -0
- package/lib/react-qw/components/ProgressBar.js +22 -0
- package/lib/react-qw/components/RadioButton.js +31 -0
- package/lib/react-qw/components/ScrollView.js +139 -0
- package/lib/react-qw/components/Slider.js +45 -0
- package/lib/react-qw/components/Tab.js +61 -0
- package/lib/react-qw/components/Tooltip.js +55 -0
- package/lib/react-qw/components/TreeView.js +130 -0
- package/lib/react-qw/components/index.js +16 -0
- package/lib/react-qw/components/index.ts +17 -0
- package/lib/react-qw/index.js +73 -0
- package/lib/react-qw/index.ts +90 -0
- package/lib/react-qw/jsx.d.ts +34 -0
- package/lib/react-qw/layout.js +82 -0
- package/lib/react-qw/layout.ts +111 -0
- package/lib/react-qw/props.js +27 -0
- package/lib/react-qw/props.ts +36 -0
- package/lib/react-qw/reconciler.js +333 -0
- package/lib/react-qw/reconciler.ts +373 -0
- package/lib/stream.js +2 -0
- package/lib/stream.ts +22 -0
- package/lib/text-codec.js +197 -0
- package/lib/text-codec.ts +171 -0
- package/lib/text-measure.js +52 -0
- package/lib/text-measure.ts +54 -0
- package/lib/url.js +278 -0
- package/lib/url.ts +201 -0
- package/lib/vendor/web-streams/ponyfill.d.mts +829 -0
- package/lib/vendor/web-streams/ponyfill.mjs +8 -0
- package/lib/websocket.js +541 -0
- package/lib/websocket.ts +555 -0
- package/package.json +44 -0
- package/quickwin.d.ts +594 -0
- package/quickwin_const.d.ts +900 -0
- package/qwin-nowasm.exe +0 -0
- package/qwin.exe +0 -0
- package/test/add.wasm +0 -0
- package/test/complex.wasm +0 -0
- package/test/complex_imports.wasm +0 -0
- package/test/debug/http_server_e2e.js +31 -0
- package/test/debug/minwasm.js +52 -0
- package/test/debug/mupdf_debug.js +104 -0
- package/test/debug/wasm.js +122 -0
- package/test/debug/wasm_export_debug.js +64 -0
- package/test/debug/wasm_names.js +46 -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 +91 -0
- package/test/run.ts +96 -0
- package/test/sjlj.wasm +0 -0
- package/test/test_basic.js +7 -0
- package/test/test_basic.ts +9 -0
- package/test/test_basic_fetch.js +34 -0
- package/test/test_basic_fetch.ts +33 -0
- package/test/test_brotli.js +48 -0
- package/test/test_brotli.ts +52 -0
- package/test/test_fetch_cache.js +169 -0
- package/test/test_fetch_cache.ts +187 -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_http_server.js +142 -0
- package/test/test_http_server.ts +136 -0
- package/test/test_import_dir.js +4 -0
- package/test/test_import_dir.ts +5 -0
- package/test/test_ipv6.js +134 -0
- package/test/test_ipv6.ts +129 -0
- package/test/test_jsx.js +1314 -0
- package/test/test_meta_url.js +3 -0
- package/test/test_meta_url.ts +4 -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 +57 -0
- package/test/test_net_event.ts +55 -0
- package/test/test_net_fetch.js +259 -0
- package/test/test_net_fetch.ts +238 -0
- package/test/test_net_websocket.js +180 -0
- package/test/test_net_websocket.ts +164 -0
- package/test/test_polyfill.js +144 -0
- package/test/test_polyfill.ts +159 -0
- package/test/test_react_complex.js +17440 -0
- package/test/test_react_counter.js +17331 -0
- package/test/test_react_flex.js +17643 -0
- package/test/test_react_hidden.js +17336 -0
- package/test/test_react_listview_link.js +17371 -0
- package/test/test_react_render.js +17326 -0
- package/test/test_url.js +173 -0
- package/test/test_url.ts +183 -0
- package/test/test_wasm_basic.js +85 -0
- package/test/test_wasm_basic.ts +73 -0
- package/test/test_wasm_import_global.js +41 -0
- package/test/test_wasm_import_global.ts +45 -0
- package/test/test_wasm_sjlj.js +145 -0
- package/test/test_wasm_sjlj.ts +125 -0
- package/test/test_wasm_types.js +97 -0
- package/test/test_wasm_types.ts +124 -0
- package/test/test_worker.js +21 -0
- package/test/test_worker.ts +24 -0
- package/test/test_worker_fetch.js +26 -0
- package/test/test_worker_fetch.ts +27 -0
- package/test/test_worker_net.js +51 -0
- package/test/test_worker_net.ts +54 -0
- package/test/test_worker_wasm_concurrent.js +44 -0
- package/test/test_worker_wasm_concurrent.ts +45 -0
- package/test/types.wasm +0 -0
- package/test/worker_fetch.js +13 -0
- package/test/worker_fetch.ts +13 -0
- package/test/worker_helper.js +1 -0
- package/test/worker_helper.ts +1 -0
- package/test/worker_http.js +18 -0
- package/test/worker_http.ts +20 -0
- package/test/worker_import_lib.js +12 -0
- package/test/worker_import_lib.ts +13 -0
- package/test/worker_module.js +16 -0
- package/test/worker_module.ts +17 -0
- package/test/worker_wasm_concurrent.js +22 -0
- package/test/worker_wasm_concurrent.ts +22 -0
- package/tsconfig.json +27 -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
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
import '../lib/polyfill.js';
|
|
2
|
+
import * as std from 'std';
|
|
3
|
+
import * as gui from 'gui';
|
|
4
|
+
import * as win from 'win';
|
|
5
|
+
import * as ffi from 'ffi';
|
|
6
|
+
import { assertNonNullable } from '../lib/assert.js';
|
|
7
|
+
const FFI_PTR = ffi.FFI_TYPE_POINTER;
|
|
8
|
+
const FFI_U32 = ffi.FFI_TYPE_UINT32;
|
|
9
|
+
const FFI_S32 = ffi.FFI_TYPE_SINT32;
|
|
10
|
+
const _user32 = win.LoadLibrary('user32.dll');
|
|
11
|
+
const _gdi32 = win.LoadLibrary('gdi32.dll');
|
|
12
|
+
const _comdlg32 = win.LoadLibrary('comdlg32.dll');
|
|
13
|
+
if (!(_user32 && _gdi32 && _comdlg32))
|
|
14
|
+
std.exit(0);
|
|
15
|
+
function loadProc(lib, name) {
|
|
16
|
+
const ptr = win.GetProcAddress(lib, name);
|
|
17
|
+
if (!ptr) {
|
|
18
|
+
std.printf('Error: cannot load %s\n', name);
|
|
19
|
+
std.exit(1);
|
|
20
|
+
}
|
|
21
|
+
return ptr;
|
|
22
|
+
}
|
|
23
|
+
const GetOpenFileNameW = loadProc(_comdlg32, 'GetOpenFileNameW');
|
|
24
|
+
const SetDIBitsToDevice = loadProc(_gdi32, 'SetDIBitsToDevice');
|
|
25
|
+
const GetDC = loadProc(_user32, 'GetDC');
|
|
26
|
+
const ReleaseDC = loadProc(_user32, 'ReleaseDC');
|
|
27
|
+
const PatBlt = loadProc(_gdi32, 'PatBlt');
|
|
28
|
+
const WHITENESS = 0x00FF0062;
|
|
29
|
+
function makeBitmapInfo(w, h) {
|
|
30
|
+
const bmi = new ArrayBuffer(40);
|
|
31
|
+
const bv = new DataView(bmi);
|
|
32
|
+
bv.setUint32(0, 40, true);
|
|
33
|
+
bv.setInt32(4, w, true);
|
|
34
|
+
bv.setInt32(8, -h, true);
|
|
35
|
+
bv.setUint16(12, 1, true);
|
|
36
|
+
bv.setUint16(14, 24, true);
|
|
37
|
+
return bmi;
|
|
38
|
+
}
|
|
39
|
+
function strToWide(str) {
|
|
40
|
+
return new TextEncoder('utf-16le').encode(str + '\0').buffer;
|
|
41
|
+
}
|
|
42
|
+
function wideToStr(buf) {
|
|
43
|
+
const dv = new DataView(buf);
|
|
44
|
+
const chars = [];
|
|
45
|
+
for (let i = 0; i < buf.byteLength; i += 2) {
|
|
46
|
+
const c = dv.getUint16(i, true);
|
|
47
|
+
if (c === 0)
|
|
48
|
+
break;
|
|
49
|
+
chars.push(c);
|
|
50
|
+
}
|
|
51
|
+
return String.fromCharCode(...chars);
|
|
52
|
+
}
|
|
53
|
+
function setPtr(dv, off, ptr) {
|
|
54
|
+
dv.setUint32(off, ptr & 0xFFFFFFFF, true);
|
|
55
|
+
dv.setUint32(off + 4, Math.floor(ptr / 0x100000000), true);
|
|
56
|
+
}
|
|
57
|
+
let hwndMain = null;
|
|
58
|
+
let hwndEdit = null;
|
|
59
|
+
let hwndCanvas = null;
|
|
60
|
+
let hwndContent = null;
|
|
61
|
+
let currentPixmap = null;
|
|
62
|
+
let currentPage = 0, totalPages = 0;
|
|
63
|
+
let scrollX = 0, scrollY = 0;
|
|
64
|
+
let cachedPath = '';
|
|
65
|
+
let cachedDoc = null;
|
|
66
|
+
function clearCachedDoc() {
|
|
67
|
+
if (cachedDoc) {
|
|
68
|
+
try {
|
|
69
|
+
cachedDoc.destroy();
|
|
70
|
+
}
|
|
71
|
+
catch { }
|
|
72
|
+
}
|
|
73
|
+
cachedDoc = null;
|
|
74
|
+
cachedPath = '';
|
|
75
|
+
}
|
|
76
|
+
async function loadMupdf() {
|
|
77
|
+
const wasmPath = './vendor/mupdf-wasm/mupdf-wasm.wasm';
|
|
78
|
+
const fp = std.open(wasmPath, 'rb');
|
|
79
|
+
if (!fp) {
|
|
80
|
+
std.printf('Error: cannot open %s\n', wasmPath);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
fp.seek(0, 2);
|
|
84
|
+
const size = fp.tell();
|
|
85
|
+
fp.seek(0, 0);
|
|
86
|
+
const buf = new ArrayBuffer(size);
|
|
87
|
+
fp.read(buf, 0, size);
|
|
88
|
+
fp.close();
|
|
89
|
+
(globalThis).$libmupdf_wasm_Module = { wasmBinary: buf, locateFile: (p) => p };
|
|
90
|
+
try {
|
|
91
|
+
return await import('../vendor/mupdf-wasm/mupdf.js');
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
std.printf('Error: mupdf load failed: %s\n', String(e));
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function renderPdfPage(mupdf, filePath, pageIndex) {
|
|
99
|
+
if (filePath !== cachedPath) {
|
|
100
|
+
clearCachedDoc();
|
|
101
|
+
const fp = std.open(filePath, 'rb');
|
|
102
|
+
if (!fp) {
|
|
103
|
+
std.printf('Error: cannot open %s\n', filePath);
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
let buf = null;
|
|
107
|
+
try {
|
|
108
|
+
fp.seek(0, 2);
|
|
109
|
+
const size = fp.tell();
|
|
110
|
+
fp.seek(0, 0);
|
|
111
|
+
buf = new ArrayBuffer(size);
|
|
112
|
+
fp.read(buf, 0, size);
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
fp.close();
|
|
116
|
+
}
|
|
117
|
+
if (!buf)
|
|
118
|
+
return null;
|
|
119
|
+
try {
|
|
120
|
+
cachedDoc = mupdf.Document.openDocument(new Uint8Array(buf), 'application/pdf');
|
|
121
|
+
cachedPath = filePath;
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
std.printf('Error opening document: %s\n', String(e));
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (!cachedDoc)
|
|
129
|
+
return null;
|
|
130
|
+
let page = null;
|
|
131
|
+
let pixmap = null;
|
|
132
|
+
try {
|
|
133
|
+
page = cachedDoc.loadPage(pageIndex);
|
|
134
|
+
pixmap = page.toPixmap(mupdf.Matrix.scale(1.5, 1.5), mupdf.ColorSpace.DeviceRGB, false);
|
|
135
|
+
if (!pixmap)
|
|
136
|
+
return null;
|
|
137
|
+
const srcPixels = pixmap.getPixels();
|
|
138
|
+
const srcStride = pixmap.getStride();
|
|
139
|
+
const w = pixmap.getWidth(), h = pixmap.getHeight();
|
|
140
|
+
const dibStride = Math.floor((w * 3 + 3) / 4) * 4;
|
|
141
|
+
const dib = new Uint8Array(h * dibStride);
|
|
142
|
+
for (let y = 0; y < h; y++) {
|
|
143
|
+
const srcOff = y * srcStride, dstOff = y * dibStride;
|
|
144
|
+
for (let x = 0; x < w; x++) {
|
|
145
|
+
const sx = srcOff + x * 3, dx = dstOff + x * 3;
|
|
146
|
+
dib[dx] = srcPixels[sx + 2];
|
|
147
|
+
dib[dx + 1] = srcPixels[sx + 1];
|
|
148
|
+
dib[dx + 2] = srcPixels[sx];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const totalPages = cachedDoc.countPages();
|
|
152
|
+
return { data: dib.buffer, w, h, totalPages };
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
std.printf('Error rendering: %s\n', String(e));
|
|
156
|
+
clearCachedDoc();
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
if (pixmap)
|
|
161
|
+
try {
|
|
162
|
+
pixmap.destroy();
|
|
163
|
+
}
|
|
164
|
+
catch { }
|
|
165
|
+
;
|
|
166
|
+
if (page)
|
|
167
|
+
try {
|
|
168
|
+
page.destroy();
|
|
169
|
+
}
|
|
170
|
+
catch { }
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function openPdfFileDialog() {
|
|
174
|
+
assertNonNullable(hwndMain);
|
|
175
|
+
const structBuf = new ArrayBuffer(152), sv = new DataView(structBuf);
|
|
176
|
+
const fileBuf = new ArrayBuffer(260 * 2);
|
|
177
|
+
const filterWide = strToWide('PDF Files\0*.pdf\0All Files\0*.*\0\0');
|
|
178
|
+
sv.setUint32(0, 152, true);
|
|
179
|
+
sv.setUint32(8, hwndMain & 0xFFFFFFFF, true);
|
|
180
|
+
sv.setUint32(12, Math.floor(hwndMain / 0x100000000), true);
|
|
181
|
+
setPtr(sv, 24, ffi.bufferPtr(filterWide));
|
|
182
|
+
setPtr(sv, 48, ffi.bufferPtr(fileBuf));
|
|
183
|
+
sv.setUint32(56, 260, true);
|
|
184
|
+
sv.setUint32(96, 0x1000 | 0x0800 | 0x0004, true);
|
|
185
|
+
const ret = ffi.ffiCall(GetOpenFileNameW, [FFI_PTR], [structBuf], FFI_U32);
|
|
186
|
+
return ret ? wideToStr(fileBuf) : null;
|
|
187
|
+
}
|
|
188
|
+
function updateScrollRange() {
|
|
189
|
+
assertNonNullable(hwndCanvas);
|
|
190
|
+
assertNonNullable(hwndContent);
|
|
191
|
+
if (!currentPixmap)
|
|
192
|
+
return;
|
|
193
|
+
const cr = gui.GetClientRect(hwndCanvas);
|
|
194
|
+
if (!cr)
|
|
195
|
+
return;
|
|
196
|
+
const cw = cr.right - cr.left, ch = cr.bottom - cr.top;
|
|
197
|
+
const maxX = Math.max(0, currentPixmap.w - cw);
|
|
198
|
+
const maxY = Math.max(0, currentPixmap.h - ch);
|
|
199
|
+
scrollX = Math.min(scrollX, maxX);
|
|
200
|
+
scrollY = Math.min(scrollY, maxY);
|
|
201
|
+
if (currentPixmap.w > cw) {
|
|
202
|
+
const style = gui.GetWindowLongPtr(hwndCanvas, -16 /* gui.Gwlp.STYLE */) | 0;
|
|
203
|
+
if (!(style & 1048576 /* gui.WindowStyle.HSCROLL */)) {
|
|
204
|
+
gui.SetWindowLongPtr(hwndCanvas, -16 /* gui.Gwlp.STYLE */, style | 1048576 /* gui.WindowStyle.HSCROLL */);
|
|
205
|
+
gui.SetWindowPos(hwndCanvas, 0 /* gui.SetWindowPosHwnd.TOP */, 0, 0, 0, 0, 2 /* gui.SetWindowPosFlag.SWP_NOMOVE */ | 1 /* gui.SetWindowPosFlag.SWP_NOSIZE */ | 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */ | 32 /* gui.SetWindowPosFlag.SWP_FRAMECHANGED */);
|
|
206
|
+
}
|
|
207
|
+
gui.SetScrollInfo(hwndCanvas, 0 /* gui.ScrollBar.HORZ */, { min: 0, max: currentPixmap.w - 1, page: cw, pos: scrollX }, true);
|
|
208
|
+
gui.ShowScrollBar(hwndCanvas, 0 /* gui.ScrollBar.HORZ */, true);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
scrollX = 0;
|
|
212
|
+
const style = gui.GetWindowLongPtr(hwndCanvas, -16 /* gui.Gwlp.STYLE */) | 0;
|
|
213
|
+
if (style & 1048576 /* gui.WindowStyle.HSCROLL */) {
|
|
214
|
+
gui.SetWindowLongPtr(hwndCanvas, -16 /* gui.Gwlp.STYLE */, style & ~1048576 /* gui.WindowStyle.HSCROLL */);
|
|
215
|
+
gui.SetWindowPos(hwndCanvas, 0 /* gui.SetWindowPosHwnd.TOP */, 0, 0, 0, 0, 2 /* gui.SetWindowPosFlag.SWP_NOMOVE */ | 1 /* gui.SetWindowPosFlag.SWP_NOSIZE */ | 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */ | 32 /* gui.SetWindowPosFlag.SWP_FRAMECHANGED */);
|
|
216
|
+
}
|
|
217
|
+
gui.ShowScrollBar(hwndCanvas, 0 /* gui.ScrollBar.HORZ */, false);
|
|
218
|
+
}
|
|
219
|
+
if (currentPixmap.h > ch) {
|
|
220
|
+
gui.SetScrollInfo(hwndCanvas, 1 /* gui.ScrollBar.VERT */, { min: 0, max: currentPixmap.h - 1, page: ch, pos: scrollY }, true);
|
|
221
|
+
gui.ShowScrollBar(hwndCanvas, 1 /* gui.ScrollBar.VERT */, true);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
scrollY = 0;
|
|
225
|
+
gui.ShowScrollBar(hwndCanvas, 1 /* gui.ScrollBar.VERT */, false);
|
|
226
|
+
}
|
|
227
|
+
gui.SetWindowPos(hwndContent, 0 /* gui.SetWindowPosHwnd.TOP */, -scrollX, -scrollY, 0, 0, 1 /* gui.SetWindowPosFlag.SWP_NOSIZE */ | 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */);
|
|
228
|
+
}
|
|
229
|
+
function showPdf(mupdf, path, pageIdx) {
|
|
230
|
+
assertNonNullable(hwndMain);
|
|
231
|
+
assertNonNullable(hwndContent);
|
|
232
|
+
const pix = renderPdfPage(mupdf, path, pageIdx);
|
|
233
|
+
if (!pix) {
|
|
234
|
+
gui.MessageBox('渲染 PDF 失败');
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
currentPixmap = pix;
|
|
238
|
+
currentPage = pageIdx;
|
|
239
|
+
totalPages = pix.totalPages;
|
|
240
|
+
gui.SetWindowText(hwndMain, 'PDF 预览 - 第 ' + (pageIdx + 1) + '/' + totalPages + ' 页');
|
|
241
|
+
scrollX = 0;
|
|
242
|
+
scrollY = 0;
|
|
243
|
+
gui.SetWindowPos(hwndContent, 0 /* gui.SetWindowPosHwnd.TOP */, 0, 0, pix.w, pix.h, 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */ | 2 /* gui.SetWindowPosFlag.SWP_NOMOVE */);
|
|
244
|
+
updateScrollRange();
|
|
245
|
+
gui.InvalidateRect(hwndContent, null, true);
|
|
246
|
+
}
|
|
247
|
+
function doScroll(dx, dy) {
|
|
248
|
+
assertNonNullable(hwndCanvas);
|
|
249
|
+
assertNonNullable(hwndContent);
|
|
250
|
+
const cr = gui.GetClientRect(hwndCanvas);
|
|
251
|
+
if (!cr || !currentPixmap)
|
|
252
|
+
return;
|
|
253
|
+
const cw = cr.right - cr.left, ch = cr.bottom - cr.top;
|
|
254
|
+
const maxX = Math.max(0, currentPixmap.w - cw);
|
|
255
|
+
const maxY = Math.max(0, currentPixmap.h - ch);
|
|
256
|
+
scrollX = Math.max(0, Math.min(maxX, scrollX + dx));
|
|
257
|
+
scrollY = Math.max(0, Math.min(maxY, scrollY + dy));
|
|
258
|
+
gui.SetWindowPos(hwndContent, 0 /* gui.SetWindowPosHwnd.TOP */, -scrollX, -scrollY, 0, 0, 1 /* gui.SetWindowPosFlag.SWP_NOSIZE */ | 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */);
|
|
259
|
+
gui.SetScrollInfo(hwndCanvas, 0 /* gui.ScrollBar.HORZ */, { pos: scrollX }, true);
|
|
260
|
+
gui.SetScrollInfo(hwndCanvas, 1 /* gui.ScrollBar.VERT */, { pos: scrollY }, true);
|
|
261
|
+
}
|
|
262
|
+
async function main() {
|
|
263
|
+
const mupdf = await loadMupdf();
|
|
264
|
+
if (!mupdf) {
|
|
265
|
+
gui.MessageBox('加载 mupdf WASM 失败');
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const WS_CHILD = 1073741824 /* gui.WindowStyle.CHILD */;
|
|
269
|
+
const WS_VIS = 268435456 /* gui.WindowStyle.VISIBLE */;
|
|
270
|
+
const WS_BORDER = 8388608 /* gui.WindowStyle.BORDER */;
|
|
271
|
+
const ctrlY = 12, ctrlH = 26, gap = 4, btnW = 80, btnPageW = 72;
|
|
272
|
+
let hwndBtnOpen = null;
|
|
273
|
+
let hwndBtnPrev = null;
|
|
274
|
+
let hwndBtnNext = null;
|
|
275
|
+
gui.RegisterClass('PdfViewer2', (hwnd, msg, wParam, lParam) => {
|
|
276
|
+
assertNonNullable(hwndCanvas);
|
|
277
|
+
assertNonNullable(hwndEdit);
|
|
278
|
+
assertNonNullable(hwndBtnOpen);
|
|
279
|
+
assertNonNullable(hwndBtnPrev);
|
|
280
|
+
assertNonNullable(hwndBtnNext);
|
|
281
|
+
if (msg === 2 /* gui.WmMsg.DESTROY */) {
|
|
282
|
+
gui.PostQuitMessage(0);
|
|
283
|
+
return 0;
|
|
284
|
+
}
|
|
285
|
+
if (msg === 5 /* gui.WmMsg.SIZE */) {
|
|
286
|
+
const cr = gui.GetClientRect(hwnd);
|
|
287
|
+
if (cr) {
|
|
288
|
+
const cw = cr.right - cr.left, ch = cr.bottom - cr.top;
|
|
289
|
+
gui.SetWindowPos(hwndCanvas, 0 /* gui.SetWindowPosHwnd.TOP */, 0, 50, cw, Math.max(1, ch - 50), 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */);
|
|
290
|
+
gui.SetWindowPos(hwndEdit, 0 /* gui.SetWindowPosHwnd.TOP */, 0, 0, Math.max(100, cw - 320), 0, 2 /* gui.SetWindowPosFlag.SWP_NOMOVE */ | 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */);
|
|
291
|
+
}
|
|
292
|
+
return gui.DefWindowProc(hwnd, msg, wParam, lParam);
|
|
293
|
+
}
|
|
294
|
+
if (msg === 273 /* gui.WmMsg.COMMAND */) {
|
|
295
|
+
const hCtrl = lParam;
|
|
296
|
+
if (hCtrl === hwndBtnOpen) {
|
|
297
|
+
const path = openPdfFileDialog();
|
|
298
|
+
if (path) {
|
|
299
|
+
gui.SetWindowText(hwndEdit, path);
|
|
300
|
+
showPdf(mupdf, path, 0);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
else if (hCtrl === hwndBtnPrev) {
|
|
304
|
+
const path = gui.GetWindowText(hwndEdit);
|
|
305
|
+
if (path && currentPage > 0)
|
|
306
|
+
showPdf(mupdf, path, currentPage - 1);
|
|
307
|
+
}
|
|
308
|
+
else if (hCtrl === hwndBtnNext) {
|
|
309
|
+
const path = gui.GetWindowText(hwndEdit);
|
|
310
|
+
if (path && currentPage < totalPages - 1)
|
|
311
|
+
showPdf(mupdf, path, currentPage + 1);
|
|
312
|
+
}
|
|
313
|
+
return 0;
|
|
314
|
+
}
|
|
315
|
+
return gui.DefWindowProc(hwnd, msg, wParam, lParam);
|
|
316
|
+
});
|
|
317
|
+
hwndMain = gui.CreateWindow('PdfViewer2', 'PDF 预览 2', 13565952 /* gui.WindowStyle.OVERLAPPEDWINDOW */ | 33554432 /* gui.WindowStyle.CLIPCHILDREN */, 100, 100, 1100, 800, null, null);
|
|
318
|
+
if (!hwndMain) {
|
|
319
|
+
gui.MessageBox('创建主窗口失败');
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
// 控件直接挂在主窗口上
|
|
323
|
+
hwndEdit = gui.CreateWindow('EDIT', '', WS_CHILD | WS_VIS | WS_BORDER, ctrlY + btnW + gap, ctrlY, 480, ctrlH, hwndMain, null);
|
|
324
|
+
hwndBtnOpen = gui.CreateWindow('BUTTON', '打开 PDF', WS_CHILD | WS_VIS, ctrlY, ctrlY, btnW, ctrlH, hwndMain, null);
|
|
325
|
+
hwndBtnPrev = gui.CreateWindow('BUTTON', '上一页', WS_CHILD | WS_VIS, ctrlY + btnW + gap + 480 + gap, ctrlY, btnPageW, ctrlH, hwndMain, null);
|
|
326
|
+
hwndBtnNext = gui.CreateWindow('BUTTON', '下一页', WS_CHILD | WS_VIS, ctrlY + btnW + gap + 480 + gap + btnPageW + gap, ctrlY, btnPageW, ctrlH, hwndMain, null);
|
|
327
|
+
// 画布(带滚动条)
|
|
328
|
+
hwndCanvas = gui.CreateWindow('STATIC', '', WS_CHILD | WS_VIS | 33554432 /* gui.WindowStyle.CLIPCHILDREN */ | 2097152 /* gui.WindowStyle.VSCROLL */ | 1048576 /* gui.WindowStyle.HSCROLL */, 0, 50, 1100, 750, hwndMain, null);
|
|
329
|
+
// 内容窗口(画 pixmap)
|
|
330
|
+
hwndContent = gui.CreateWindow('STATIC', '', WS_CHILD | WS_VIS, 0, 0, 100, 100, hwndCanvas, null);
|
|
331
|
+
assertNonNullable(hwndMain);
|
|
332
|
+
assertNonNullable(hwndEdit);
|
|
333
|
+
assertNonNullable(hwndCanvas);
|
|
334
|
+
assertNonNullable(hwndContent);
|
|
335
|
+
assertNonNullable(hwndBtnOpen);
|
|
336
|
+
assertNonNullable(hwndBtnPrev);
|
|
337
|
+
assertNonNullable(hwndBtnNext);
|
|
338
|
+
// 内容窗口 PAINT
|
|
339
|
+
gui.SetWindowProc(hwndContent, (hwnd, msg, wParam, lParam) => {
|
|
340
|
+
if (msg === 20 /* gui.WmMsg.ERASEBKGND */)
|
|
341
|
+
return 1;
|
|
342
|
+
if (msg === 15 /* gui.WmMsg.PAINT */) {
|
|
343
|
+
gui.DefWindowProc(hwnd, msg, wParam, lParam);
|
|
344
|
+
const pm = currentPixmap;
|
|
345
|
+
if (!pm)
|
|
346
|
+
return 0;
|
|
347
|
+
const hdc = ffi.ffiCall(GetDC, [ffi.FFI_TYPE_UINT64], [hwnd], ffi.FFI_TYPE_UINT64);
|
|
348
|
+
if (hdc) {
|
|
349
|
+
const bmi = makeBitmapInfo(pm.w, pm.h);
|
|
350
|
+
ffi.ffiCall(SetDIBitsToDevice, [
|
|
351
|
+
ffi.FFI_TYPE_UINT64, FFI_S32, FFI_S32, FFI_U32, FFI_U32,
|
|
352
|
+
FFI_S32, FFI_S32, FFI_U32, FFI_U32,
|
|
353
|
+
FFI_PTR, FFI_PTR, FFI_U32
|
|
354
|
+
], [hdc, 0, 0, pm.w, pm.h, 0, 0, 0, pm.h, pm.data, bmi, 0], FFI_S32);
|
|
355
|
+
ffi.ffiCall(ReleaseDC, [ffi.FFI_TYPE_UINT64, ffi.FFI_TYPE_UINT64], [hwnd, hdc], FFI_S32);
|
|
356
|
+
}
|
|
357
|
+
return 0;
|
|
358
|
+
}
|
|
359
|
+
return gui.DefWindowProc(hwnd, msg, wParam, lParam);
|
|
360
|
+
});
|
|
361
|
+
// 画布消息处理(PAINT 填白、滚动条、滚轮)
|
|
362
|
+
gui.SetWindowProc(hwndCanvas, (hwnd, msg, wParam, lParam) => {
|
|
363
|
+
if (msg === 20 /* gui.WmMsg.ERASEBKGND */)
|
|
364
|
+
return 1;
|
|
365
|
+
if (msg === 15 /* gui.WmMsg.PAINT */) {
|
|
366
|
+
const cr = gui.GetClientRect(hwnd);
|
|
367
|
+
if (cr) {
|
|
368
|
+
const cw = cr.right - cr.left, ch = cr.bottom - cr.top;
|
|
369
|
+
const hdc = ffi.ffiCall(GetDC, [ffi.FFI_TYPE_UINT64], [hwnd], ffi.FFI_TYPE_UINT64);
|
|
370
|
+
if (hdc) {
|
|
371
|
+
ffi.ffiCall(PatBlt, [ffi.FFI_TYPE_UINT64, FFI_S32, FFI_S32, FFI_S32, FFI_S32, FFI_U32], [hdc, 0, 0, cw, ch, WHITENESS], FFI_U32);
|
|
372
|
+
ffi.ffiCall(ReleaseDC, [ffi.FFI_TYPE_UINT64, ffi.FFI_TYPE_UINT64], [hwnd, hdc], FFI_S32);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
gui.DefWindowProc(hwnd, msg, wParam, lParam);
|
|
376
|
+
return 0;
|
|
377
|
+
}
|
|
378
|
+
if (msg === 277 /* gui.WmMsg.VSCROLL */ || msg === 276 /* gui.WmMsg.HSCROLL */) {
|
|
379
|
+
const bar = msg === 277 /* gui.WmMsg.VSCROLL */ ? 1 /* gui.ScrollBar.VERT */ : 0 /* gui.ScrollBar.HORZ */;
|
|
380
|
+
const info = gui.GetScrollInfo(hwnd, bar);
|
|
381
|
+
const maxPos = info.max - info.page + 1;
|
|
382
|
+
const code = wParam & 0xFFFF;
|
|
383
|
+
let newPos = bar === 1 /* gui.ScrollBar.VERT */ ? scrollY : scrollX;
|
|
384
|
+
if (code === 5 /* gui.ScrollCmd.THUMBTRACK */ || code === 4 /* gui.ScrollCmd.THUMBPOSITION */)
|
|
385
|
+
newPos = Math.max(0, Math.min(maxPos, (wParam >> 16) & 0xFFFF));
|
|
386
|
+
else if (code === 0 /* gui.ScrollCmd.LINEUP */)
|
|
387
|
+
newPos -= 20;
|
|
388
|
+
else if (code === 1 /* gui.ScrollCmd.LINEDOWN */)
|
|
389
|
+
newPos += 20;
|
|
390
|
+
else if (code === 2 /* gui.ScrollCmd.PAGEUP */)
|
|
391
|
+
newPos -= info.page;
|
|
392
|
+
else if (code === 3 /* gui.ScrollCmd.PAGEDOWN */)
|
|
393
|
+
newPos += info.page;
|
|
394
|
+
else
|
|
395
|
+
return 0;
|
|
396
|
+
newPos = Math.max(0, Math.min(maxPos, newPos));
|
|
397
|
+
if (bar === 1 /* gui.ScrollBar.VERT */)
|
|
398
|
+
doScroll(0, newPos - scrollY);
|
|
399
|
+
else
|
|
400
|
+
doScroll(newPos - scrollX, 0);
|
|
401
|
+
return 0;
|
|
402
|
+
}
|
|
403
|
+
if (msg === 522 /* gui.WmMsg.MOUSEWHEEL */) {
|
|
404
|
+
const raw = (wParam >>> 16) & 0xFFFF;
|
|
405
|
+
const wheel = raw >= 0x8000 ? raw - 0x10000 : raw;
|
|
406
|
+
const isHorz = (wParam & 4 /* gui.MouseKeyFlag.MK_SHIFT */) !== 0;
|
|
407
|
+
const bar = isHorz ? 0 /* gui.ScrollBar.HORZ */ : 1 /* gui.ScrollBar.VERT */;
|
|
408
|
+
const info = gui.GetScrollInfo(hwnd, bar);
|
|
409
|
+
const maxPos = info.max - info.page + 1;
|
|
410
|
+
let newPos = (isHorz ? scrollX : scrollY) - Math.round(wheel * 40 / 120);
|
|
411
|
+
newPos = Math.max(0, Math.min(maxPos, newPos));
|
|
412
|
+
if (isHorz)
|
|
413
|
+
doScroll(newPos - scrollX, 0);
|
|
414
|
+
else
|
|
415
|
+
doScroll(0, newPos - scrollY);
|
|
416
|
+
return 0;
|
|
417
|
+
}
|
|
418
|
+
if (msg === 5 /* gui.WmMsg.SIZE */) {
|
|
419
|
+
updateScrollRange();
|
|
420
|
+
return gui.DefWindowProc(hwnd, msg, wParam, lParam);
|
|
421
|
+
}
|
|
422
|
+
return gui.DefWindowProc(hwnd, msg, wParam, lParam);
|
|
423
|
+
});
|
|
424
|
+
gui.ShowWindow(hwndMain);
|
|
425
|
+
const test = std.open('example.pdf', 'rb');
|
|
426
|
+
if (test) {
|
|
427
|
+
test.close();
|
|
428
|
+
showPdf(mupdf, 'example.pdf', 0);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
main();
|