@build-qube/papyra 0.2.0 → 0.4.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.
Files changed (51) hide show
  1. package/README.md +18 -0
  2. package/dist/attachments.d.ts +76 -0
  3. package/dist/attachments.d.ts.map +1 -0
  4. package/dist/attachments.js +65 -0
  5. package/dist/attachments.js.map +1 -0
  6. package/dist/canvas.d.ts +27 -2
  7. package/dist/canvas.d.ts.map +1 -1
  8. package/dist/canvas.js +58 -5
  9. package/dist/canvas.js.map +1 -1
  10. package/dist/cells.d.ts +148 -0
  11. package/dist/cells.d.ts.map +1 -0
  12. package/dist/cells.js +144 -0
  13. package/dist/cells.js.map +1 -0
  14. package/dist/document.d.ts +61 -0
  15. package/dist/document.d.ts.map +1 -1
  16. package/dist/document.js +105 -7
  17. package/dist/document.js.map +1 -1
  18. package/dist/errors.d.ts +12 -0
  19. package/dist/errors.d.ts.map +1 -1
  20. package/dist/errors.js +17 -1
  21. package/dist/errors.js.map +1 -1
  22. package/dist/index.d.ts +18 -2
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +10 -2
  25. package/dist/index.js.map +1 -1
  26. package/dist/structure.d.ts +119 -0
  27. package/dist/structure.d.ts.map +1 -0
  28. package/dist/structure.js +115 -0
  29. package/dist/structure.js.map +1 -0
  30. package/dist/styles.d.ts +96 -0
  31. package/dist/styles.d.ts.map +1 -0
  32. package/dist/styles.js +69 -0
  33. package/dist/styles.js.map +1 -0
  34. package/dist/text.d.ts +18 -0
  35. package/dist/text.d.ts.map +1 -1
  36. package/dist/text.js.map +1 -1
  37. package/dist/tiff.d.ts +95 -0
  38. package/dist/tiff.d.ts.map +1 -0
  39. package/dist/tiff.js +78 -0
  40. package/dist/tiff.js.map +1 -0
  41. package/dist/types.d.ts +25 -0
  42. package/dist/types.d.ts.map +1 -1
  43. package/dist/viewport.d.ts +129 -0
  44. package/dist/viewport.d.ts.map +1 -0
  45. package/dist/viewport.js +163 -0
  46. package/dist/viewport.js.map +1 -0
  47. package/dist/workbook.d.ts +152 -0
  48. package/dist/workbook.d.ts.map +1 -0
  49. package/dist/workbook.js +158 -0
  50. package/dist/workbook.js.map +1 -0
  51. package/package.json +2 -2
@@ -0,0 +1,129 @@
1
+ import type { Quad, Rect } from './text.js';
2
+ import type { PageSize, RenderedPage } from './types.js';
3
+ /**
4
+ * Quarter turns clockwise, applied on top of whatever the page's own `/Rotate` already
5
+ * did.
6
+ *
7
+ * A viewer's rotate button, not a property of the document: papyra has already applied
8
+ * `/Rotate` by the time you see a {@link PageSize} or a {@link PageText}, so this turns
9
+ * the page relative to how it is meant to be read.
10
+ */
11
+ export type Rotation = 0 | 90 | 180 | 270;
12
+ /** How to build a {@link Viewport}. Size it by `dpi` or by `fitWidth`, not both. */
13
+ export interface ViewportOptions {
14
+ /** Dots per inch. 72 is the page's natural size. Defaults to 72. */
15
+ dpi?: number;
16
+ /**
17
+ * Target output width in pixels, measured **after** rotation. Overrides `dpi`.
18
+ *
19
+ * This is the reason to size through a viewport rather than through
20
+ * {@link RenderOptions.fitWidth} directly: at 90° the width on screen is the page's
21
+ * height, so the two mean different things the moment the page is turned. Fitting a
22
+ * column of viewport width is what a viewer actually wants, and
23
+ * {@link Viewport.dpi} carries the answer back to the render call.
24
+ */
25
+ fitWidth?: number;
26
+ /** Quarter turns clockwise. Defaults to none. */
27
+ rotation?: Rotation;
28
+ }
29
+ /**
30
+ * A page's mapping from PDF points to the pixels on screen: a scale, a rotation, and
31
+ * the output size the two produce.
32
+ *
33
+ * papyra renders pages unrotated — the engine has no transform knob, and turning a
34
+ * bitmap is a copy nobody needs when a canvas can do it in the paint call — so a
35
+ * rotated view is a viewer-side construction. This is that construction, and it is the
36
+ * one place the rotation is written down: {@link viewportRect} and
37
+ * {@link viewportQuad} put link regions and text highlights in the same space as the
38
+ * pixels {@link paintToCanvas} draws, and disagreeing about the angle is the whole
39
+ * failure mode.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * const vp = viewport(doc.pageSize(0), { fitWidth: 1600, rotation: 90 });
44
+ * const page = await doc.renderPage(0, { dpi: vp.dpi });
45
+ * paintToCanvas(page, canvas, { rotation: vp.rotation });
46
+ * for (const link of await doc.links(0)) {
47
+ * place(hitbox, viewportRect(link.rect, vp));
48
+ * }
49
+ * ```
50
+ */
51
+ export interface Viewport {
52
+ /**
53
+ * Output width in pixels, after rotation.
54
+ *
55
+ * A whole number, and the same one the renderer arrives at: it floors a page's
56
+ * scaled point size to get its pixmap, so a viewport that reported the fractional
57
+ * value would be a pixel wider than the bitmap it describes about half the time.
58
+ */
59
+ readonly width: number;
60
+ /** Output height in pixels, after rotation. Floored, as {@link width} is. */
61
+ readonly height: number;
62
+ /** Pixels per PDF point. `1` is 72 DPI. */
63
+ readonly scale: number;
64
+ /**
65
+ * The DPI to render at. Pass straight to {@link RenderOptions.dpi}.
66
+ *
67
+ * Always describes the *unrotated* render, which is the only kind there is — the
68
+ * rotation is applied when the bitmap is painted, not when it is produced.
69
+ */
70
+ readonly dpi: number;
71
+ /** The rotation this viewport applies. */
72
+ readonly rotation: Rotation;
73
+ /** The page size it was built from, in points, before rotation. */
74
+ readonly page: PageSize;
75
+ }
76
+ /**
77
+ * A page's dimensions after a rotation, still in points.
78
+ *
79
+ * The layout half of a rotated view, and separate from {@link viewport} because it is
80
+ * needed *before* a scale exists: a viewer sizing its page boxes, or resolving a
81
+ * fit-to-width against the turned page, is working in points and cannot use a
82
+ * viewport's floored pixel dimensions without losing the fraction of a point that
83
+ * decides a box's final rounding.
84
+ *
85
+ * Returns the same object when there is nothing to swap, so it is safe in a
86
+ * dependency array.
87
+ */
88
+ export declare function rotateSize(size: PageSize, rotation: Rotation): PageSize;
89
+ /**
90
+ * Build a {@link Viewport} for a page.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * const vp = viewport(doc.pageSize(0), { fitWidth: 1600, rotation: 90 });
95
+ * canvasEl.style.width = `${vp.width}px`;
96
+ * canvasEl.style.height = `${vp.height}px`;
97
+ * ```
98
+ */
99
+ export declare function viewport(size: PageSize, options?: ViewportOptions): Viewport;
100
+ /**
101
+ * Put a rect — a {@link PageLink.rect}, a {@link quadBounds} result — into a viewport's
102
+ * pixels.
103
+ *
104
+ * Stays axis-aligned, because the rotations are quarter turns: only the corners swap
105
+ * roles. Supersedes {@link scaleRect} for anything drawn over a rotated view;
106
+ * `scaleRect` remains the cheaper call when the rotation is always zero.
107
+ */
108
+ export declare function viewportRect(rect: Rect, vp: Viewport): Rect;
109
+ /**
110
+ * Put a {@link lineQuad} into a viewport's pixels.
111
+ *
112
+ * The corner order survives: rotating the page rotates the text with it, so the
113
+ * quad's top-left is still the top-left of the text's own orientation. That is what
114
+ * keeps a highlight on a drawing's vertical dimension label correct at every angle
115
+ * rather than only at zero.
116
+ */
117
+ export declare function viewportQuad(quad: Quad, vp: Viewport): Quad;
118
+ /**
119
+ * Turn a rendered page's pixels, returning a new {@link RenderedPage}.
120
+ *
121
+ * **Prefer {@link paintToCanvas} with a `rotation` in a browser** — a canvas rotates
122
+ * in the draw call and this copies the whole buffer, which is 7 MB and a few tens of
123
+ * milliseconds for a 1600px ARCH-E sheet. This exists for the paths that have no
124
+ * canvas: writing a rotated PNG from Node, handing bytes to an encoder.
125
+ *
126
+ * A rotation of `0` returns the page unchanged rather than copying it.
127
+ */
128
+ export declare function rotatePage(page: RenderedPage, rotation: Rotation): RenderedPage;
129
+ //# sourceMappingURL=viewport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewport.d.ts","sourceRoot":"","sources":["../src/viewport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC;AAE1C,oFAAoF;AACpF,MAAM,WAAW,eAAe;IAC9B,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,QAAQ;IACvB;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;CACzB;AAOD;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,GAAG,QAAQ,CAEvE;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,QAAQ,EACd,OAAO,GAAE,eAAoB,GAC5B,QAAQ,CAmBV;AA0BD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,GAAG,IAAI,CAW3D;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,GAAG,IAAI,CAe3D;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,YAAY,EAClB,QAAQ,EAAE,QAAQ,GACjB,YAAY,CA6Cd"}
@@ -0,0 +1,163 @@
1
+ /** True when the rotation exchanges the page's width and height. */
2
+ function turned(rotation) {
3
+ return rotation === 90 || rotation === 270;
4
+ }
5
+ /**
6
+ * A page's dimensions after a rotation, still in points.
7
+ *
8
+ * The layout half of a rotated view, and separate from {@link viewport} because it is
9
+ * needed *before* a scale exists: a viewer sizing its page boxes, or resolving a
10
+ * fit-to-width against the turned page, is working in points and cannot use a
11
+ * viewport's floored pixel dimensions without losing the fraction of a point that
12
+ * decides a box's final rounding.
13
+ *
14
+ * Returns the same object when there is nothing to swap, so it is safe in a
15
+ * dependency array.
16
+ */
17
+ export function rotateSize(size, rotation) {
18
+ return turned(rotation) ? { width: size.height, height: size.width } : size;
19
+ }
20
+ /**
21
+ * Build a {@link Viewport} for a page.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const vp = viewport(doc.pageSize(0), { fitWidth: 1600, rotation: 90 });
26
+ * canvasEl.style.width = `${vp.width}px`;
27
+ * canvasEl.style.height = `${vp.height}px`;
28
+ * ```
29
+ */
30
+ export function viewport(size, options = {}) {
31
+ const rotation = options.rotation ?? 0;
32
+ // `fitWidth` is measured on screen, so at 90° it constrains the page's height.
33
+ const along = turned(rotation) ? size.height : size.width;
34
+ const scale = options.fitWidth !== undefined && along > 0
35
+ ? options.fitWidth / along
36
+ : (options.dpi ?? 72) / 72;
37
+ const shown = rotateSize(size, rotation);
38
+ return {
39
+ width: Math.floor(shown.width * scale),
40
+ height: Math.floor(shown.height * scale),
41
+ scale,
42
+ dpi: scale * 72,
43
+ rotation,
44
+ page: size,
45
+ };
46
+ }
47
+ /**
48
+ * Map a point from 72-DPI page space into a viewport's pixels.
49
+ *
50
+ * Rotation happens in point space and the scale is applied after, so a rect and a quad
51
+ * on the same page cannot drift apart by a rounding step.
52
+ */
53
+ function project(x, y, vp) {
54
+ const { width: w, height: h } = vp.page;
55
+ switch (vp.rotation) {
56
+ case 90:
57
+ return { x: (h - y) * vp.scale, y: x * vp.scale };
58
+ case 180:
59
+ return { x: (w - x) * vp.scale, y: (h - y) * vp.scale };
60
+ case 270:
61
+ return { x: y * vp.scale, y: (w - x) * vp.scale };
62
+ default:
63
+ return { x: x * vp.scale, y: y * vp.scale };
64
+ }
65
+ }
66
+ /**
67
+ * Put a rect — a {@link PageLink.rect}, a {@link quadBounds} result — into a viewport's
68
+ * pixels.
69
+ *
70
+ * Stays axis-aligned, because the rotations are quarter turns: only the corners swap
71
+ * roles. Supersedes {@link scaleRect} for anything drawn over a rotated view;
72
+ * `scaleRect` remains the cheaper call when the rotation is always zero.
73
+ */
74
+ export function viewportRect(rect, vp) {
75
+ const a = project(rect.x, rect.y, vp);
76
+ const b = project(rect.x + rect.width, rect.y + rect.height, vp);
77
+ const x = Math.min(a.x, b.x);
78
+ const y = Math.min(a.y, b.y);
79
+ return {
80
+ x,
81
+ y,
82
+ width: Math.max(a.x, b.x) - x,
83
+ height: Math.max(a.y, b.y) - y,
84
+ };
85
+ }
86
+ /**
87
+ * Put a {@link lineQuad} into a viewport's pixels.
88
+ *
89
+ * The corner order survives: rotating the page rotates the text with it, so the
90
+ * quad's top-left is still the top-left of the text's own orientation. That is what
91
+ * keeps a highlight on a drawing's vertical dimension label correct at every angle
92
+ * rather than only at zero.
93
+ */
94
+ export function viewportQuad(quad, vp) {
95
+ const p0 = project(quad.x0, quad.y0, vp);
96
+ const p1 = project(quad.x1, quad.y1, vp);
97
+ const p2 = project(quad.x2, quad.y2, vp);
98
+ const p3 = project(quad.x3, quad.y3, vp);
99
+ return {
100
+ x0: p0.x,
101
+ y0: p0.y,
102
+ x1: p1.x,
103
+ y1: p1.y,
104
+ x2: p2.x,
105
+ y2: p2.y,
106
+ x3: p3.x,
107
+ y3: p3.y,
108
+ };
109
+ }
110
+ /**
111
+ * Turn a rendered page's pixels, returning a new {@link RenderedPage}.
112
+ *
113
+ * **Prefer {@link paintToCanvas} with a `rotation` in a browser** — a canvas rotates
114
+ * in the draw call and this copies the whole buffer, which is 7 MB and a few tens of
115
+ * milliseconds for a 1600px ARCH-E sheet. This exists for the paths that have no
116
+ * canvas: writing a rotated PNG from Node, handing bytes to an encoder.
117
+ *
118
+ * A rotation of `0` returns the page unchanged rather than copying it.
119
+ */
120
+ export function rotatePage(page, rotation) {
121
+ if (rotation === 0)
122
+ return page;
123
+ const { width: w, height: h, stride } = page;
124
+ const outW = turned(rotation) ? h : w;
125
+ const outH = turned(rotation) ? w : h;
126
+ const out = new Uint8Array(outW * outH * 4);
127
+ // One 32-bit move per pixel instead of four 8-bit ones, when the views allow it. On
128
+ // wasm `page.data` is a window onto shared linear memory at an arbitrary offset, and
129
+ // `Uint32Array` refuses one that is not 4-byte aligned — hence the byte fallback
130
+ // rather than an assumption.
131
+ const aligned = page.data.byteOffset % 4 === 0 && stride % 4 === 0;
132
+ const src32 = aligned
133
+ ? new Uint32Array(page.data.buffer, page.data.byteOffset, (h * stride) / 4)
134
+ : null;
135
+ const dst32 = src32 ? new Uint32Array(out.buffer) : null;
136
+ const srcRow = stride / 4;
137
+ for (let y = 0; y < h; y++) {
138
+ for (let x = 0; x < w; x++) {
139
+ // Where this source pixel lands, per quarter turn clockwise.
140
+ const dx = rotation === 90 ? h - 1 - y : rotation === 180 ? w - 1 - x : y;
141
+ const dy = rotation === 90 ? x : rotation === 180 ? h - 1 - y : w - 1 - x;
142
+ if (src32 && dst32) {
143
+ dst32[dy * outW + dx] = src32[y * srcRow + x];
144
+ }
145
+ else {
146
+ const s = y * stride + x * 4;
147
+ const d = (dy * outW + dx) * 4;
148
+ out[d] = page.data[s];
149
+ out[d + 1] = page.data[s + 1];
150
+ out[d + 2] = page.data[s + 2];
151
+ out[d + 3] = page.data[s + 3];
152
+ }
153
+ }
154
+ }
155
+ return {
156
+ width: outW,
157
+ height: outH,
158
+ stride: outW * 4,
159
+ format: page.format,
160
+ data: out,
161
+ };
162
+ }
163
+ //# sourceMappingURL=viewport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewport.js","sourceRoot":"","sources":["../src/viewport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,152 @@
1
+ import { type Sheet as NativeSheet, type Workbook as NativeWorkbook } from '@build-qube/papyra-native';
2
+ import { CellWindow, type MergedRange } from './cells.js';
3
+ import { type CellStyle, type SheetLayout } from './styles.js';
4
+ import type { PdfSource } from './types.js';
5
+ /**
6
+ * The container a workbook was read from.
7
+ *
8
+ * Macro-enabled and template variants read as their base format: an `.xlsm` is
9
+ * `'xlsx'` here, because to a reader that is what it is.
10
+ */
11
+ export type WorkbookFormat = 'xlsx' | 'xlsb' | 'xls' | 'ods' | 'csv' | 'tsv';
12
+ /** How to read a workbook. Everything is sniffed when left out. */
13
+ export interface WorkbookOptions {
14
+ /**
15
+ * Skip sniffing. Only needed to read a file as TSV that was not named `.tsv` —
16
+ * the binary formats identify themselves, and text is sniffed for its delimiter.
17
+ * `'xlsm'` is accepted and reads as `'xlsx'`.
18
+ */
19
+ format?: WorkbookFormat | 'xlsm';
20
+ /** CSV and TSV only: the delimiter, a single ASCII character such as `';'`. */
21
+ delimiter?: string;
22
+ /**
23
+ * CSV and TSV only: a WHATWG encoding label, e.g. `'windows-1252'` or
24
+ * `'shift_jis'`. Without one, a byte-order mark decides, then valid UTF-8, then a
25
+ * statistical guess.
26
+ */
27
+ encoding?: string;
28
+ /**
29
+ * Cells to hold per sheet before truncating it to its first whole rows. Defaults
30
+ * to 8 million — about 400 MB, which a browser tab survives. A sheet that hits it
31
+ * reports {@link Sheet.truncated}.
32
+ */
33
+ maxCells?: number;
34
+ }
35
+ /** Whether a sheet shows in the application's tab bar. */
36
+ export type SheetVisibility = 'visible' | 'hidden' | 'veryHidden';
37
+ /** What a sheet holds. Only worksheets have cells worth showing. */
38
+ export type SheetKind = 'worksheet' | 'chartsheet' | 'dialog' | 'macro' | 'vba';
39
+ /** A sheet's entry in the workbook, known without reading its cells. */
40
+ export interface SheetInfo {
41
+ /** The name on the sheet's tab. CSV and TSV have one sheet, named `Sheet1`. */
42
+ name: string;
43
+ /** What the sheet holds. */
44
+ kind: SheetKind;
45
+ /**
46
+ * `'hidden'` sheets can be unhidden by the user; `'veryHidden'` ones only by a
47
+ * macro. A viewer usually lists the first and omits the second.
48
+ */
49
+ visibility: SheetVisibility;
50
+ }
51
+ /** Rows and columns to read, half-open like `Array.prototype.slice`. */
52
+ export interface WindowRange {
53
+ /** First row, 0-based. */
54
+ rowStart: number;
55
+ /** One past the last row. Clamped to the sheet. */
56
+ rowEnd: number;
57
+ /** First column. Defaults to 0. */
58
+ colStart?: number;
59
+ /** One past the last column. Defaults to every column. */
60
+ colEnd?: number;
61
+ }
62
+ /**
63
+ * Open a spreadsheet: Excel (`.xlsx`, `.xlsm`, `.xlsb`, `.xls`), OpenDocument
64
+ * (`.ods`), or delimited text (`.csv`, `.tsv`).
65
+ *
66
+ * Parsing happens in Rust and off the JS thread, and only sheet names are read up
67
+ * front: a sheet's cells are read the first time {@link Workbook.sheet} asks for it.
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * const book = await openWorkbook(file);
72
+ * const sheet = await book.sheet(0);
73
+ * const cells = sheet.window({ rowStart: 0, rowEnd: 50 });
74
+ * cells.text(0, 0); // 'Item'
75
+ * ```
76
+ *
77
+ * @throws {@link EncryptedWorkbookError} when the workbook is password-protected.
78
+ */
79
+ export declare function openWorkbook(source: PdfSource, options?: WorkbookOptions): Promise<Workbook>;
80
+ /** An opened spreadsheet. Get one from {@link openWorkbook}. */
81
+ export declare class Workbook {
82
+ #private;
83
+ /** The container actually read. */
84
+ readonly format: WorkbookFormat;
85
+ /** Every sheet in tab order, hidden ones included. */
86
+ readonly sheets: readonly SheetInfo[];
87
+ /**
88
+ * CSV and TSV only: the encoding the text was decoded from, e.g.
89
+ * `'windows-1252'`. Worth showing when it was guessed — a wrong guess reads as
90
+ * mojibake, and {@link WorkbookOptions.encoding} is the fix.
91
+ */
92
+ readonly encoding: string | undefined;
93
+ /** CSV and TSV only: the delimiter, given or sniffed. */
94
+ readonly delimiter: string | undefined;
95
+ /** @internal — construct via {@link openWorkbook}. */
96
+ constructor(native: NativeWorkbook);
97
+ /**
98
+ * Read one sheet, by index or by name.
99
+ *
100
+ * The first call parses the sheet, off the JS thread; later calls for the same
101
+ * sheet share that result.
102
+ */
103
+ sheet(which: number | string): Promise<Sheet>;
104
+ }
105
+ /**
106
+ * One sheet, parsed and held in Rust. Read it a window at a time.
107
+ *
108
+ * Coordinates are the sheet's own and 0-based: row 0 is the row Excel labels 1, and
109
+ * a sheet whose data starts at F10 still reports ten rows and six columns, so a grid
110
+ * draws the data where its author put it.
111
+ */
112
+ export declare class Sheet {
113
+ #private;
114
+ /** The name on the sheet's tab. */
115
+ readonly name: string;
116
+ /** One past the last row holding a value or covered by a merge. */
117
+ readonly rows: number;
118
+ /**
119
+ * The sheet held more cells than {@link WorkbookOptions.maxCells}, and this is
120
+ * its first whole rows. Worth saying on screen: the rest of the file is not here.
121
+ */
122
+ readonly truncated: boolean;
123
+ /** One past the last column holding a value or covered by a merge. */
124
+ readonly cols: number;
125
+ /**
126
+ * Merged regions. The value lives in each one's top-left cell; the others are
127
+ * empty. Always empty for `.xlsb` and `.ods`, which calamine does not read
128
+ * merges from.
129
+ */
130
+ readonly merges: readonly MergedRange[];
131
+ /**
132
+ * The styles {@link CellWindow.style} indexes into, with the default at 0.
133
+ * xlsx only: empty for every other format, whose cells all read as style 0.
134
+ */
135
+ readonly styles: readonly CellStyle[];
136
+ /**
137
+ * Column widths and row heights as the author set them. xlsx only; for anything
138
+ * else a grid sizes columns itself.
139
+ */
140
+ readonly layout: SheetLayout | undefined;
141
+ /** @internal — construct via {@link Workbook.sheet}. */
142
+ constructor(native: NativeSheet);
143
+ /**
144
+ * Read a rectangle of cells, clamped to the sheet.
145
+ *
146
+ * Synchronous, because the sheet is already parsed and the cost is the size of
147
+ * the window. Ask for what is on screen plus some overscan, not the whole sheet:
148
+ * a screenful is a few thousand cells and well under a millisecond.
149
+ */
150
+ window(range: WindowRange): CellWindow;
151
+ }
152
+ //# sourceMappingURL=workbook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workbook.d.ts","sourceRoot":"","sources":["../src/workbook.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,158 @@
1
+ import { loadWorkbook, } from '@build-qube/papyra-native';
2
+ import { CellWindow, toMergedRanges } from './cells.js';
3
+ import { rethrowLoadError } from './errors.js';
4
+ import { toBytes } from './source.js';
5
+ import { toCellStyle, toSheetLayout, } from './styles.js';
6
+ /**
7
+ * Open a spreadsheet: Excel (`.xlsx`, `.xlsm`, `.xlsb`, `.xls`), OpenDocument
8
+ * (`.ods`), or delimited text (`.csv`, `.tsv`).
9
+ *
10
+ * Parsing happens in Rust and off the JS thread, and only sheet names are read up
11
+ * front: a sheet's cells are read the first time {@link Workbook.sheet} asks for it.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const book = await openWorkbook(file);
16
+ * const sheet = await book.sheet(0);
17
+ * const cells = sheet.window({ rowStart: 0, rowEnd: 50 });
18
+ * cells.text(0, 0); // 'Item'
19
+ * ```
20
+ *
21
+ * @throws {@link EncryptedWorkbookError} when the workbook is password-protected.
22
+ */
23
+ export async function openWorkbook(source, options = {}) {
24
+ const bytes = await toBytes(source);
25
+ const format = options.format ?? formatFromName(source);
26
+ try {
27
+ const native = await loadWorkbook(bytes, { ...options, format });
28
+ return new Workbook(native);
29
+ }
30
+ catch (error) {
31
+ rethrowLoadError(error);
32
+ }
33
+ }
34
+ /**
35
+ * A `File` named `.tsv` is TSV whatever its contents sniff as.
36
+ *
37
+ * Only that one extension is trusted. The binary formats announce themselves in
38
+ * their first bytes, and a `.xls` downloaded from a web app is as often an HTML
39
+ * table or a CSV as it is BIFF — believing the name would refuse a readable file.
40
+ */
41
+ function formatFromName(source) {
42
+ const name = source.name;
43
+ return typeof name === 'string' && /\.(tsv|tab)$/i.test(name)
44
+ ? 'tsv'
45
+ : undefined;
46
+ }
47
+ /** An opened spreadsheet. Get one from {@link openWorkbook}. */
48
+ export class Workbook {
49
+ /** The container actually read. */
50
+ format;
51
+ /** Every sheet in tab order, hidden ones included. */
52
+ sheets;
53
+ /**
54
+ * CSV and TSV only: the encoding the text was decoded from, e.g.
55
+ * `'windows-1252'`. Worth showing when it was guessed — a wrong guess reads as
56
+ * mojibake, and {@link WorkbookOptions.encoding} is the fix.
57
+ */
58
+ encoding;
59
+ /** CSV and TSV only: the delimiter, given or sniffed. */
60
+ delimiter;
61
+ #native;
62
+ #sheets = new Map();
63
+ /** @internal — construct via {@link openWorkbook}. */
64
+ constructor(native) {
65
+ this.#native = native;
66
+ this.format = native.format;
67
+ this.sheets = native.sheets.map((s) => ({
68
+ name: s.name,
69
+ kind: s.kind,
70
+ visibility: s.visibility,
71
+ }));
72
+ this.encoding = native.encoding ?? undefined;
73
+ this.delimiter = native.delimiter ?? undefined;
74
+ }
75
+ /**
76
+ * Read one sheet, by index or by name.
77
+ *
78
+ * The first call parses the sheet, off the JS thread; later calls for the same
79
+ * sheet share that result.
80
+ */
81
+ sheet(which) {
82
+ const index = typeof which === 'number'
83
+ ? which
84
+ : this.sheets.findIndex((s) => s.name === which);
85
+ if (!Number.isInteger(index) || index < 0 || index >= this.sheets.length) {
86
+ return Promise.reject(new RangeError(`papyra: no sheet ${JSON.stringify(which)}`));
87
+ }
88
+ let sheet = this.#sheets.get(index);
89
+ if (!sheet) {
90
+ sheet = this.#native.sheet(index).then((native) => new Sheet(native));
91
+ // A failed read is not cached, so a retry gets a fresh attempt.
92
+ sheet.catch(() => this.#sheets.delete(index));
93
+ this.#sheets.set(index, sheet);
94
+ }
95
+ return sheet;
96
+ }
97
+ }
98
+ /**
99
+ * One sheet, parsed and held in Rust. Read it a window at a time.
100
+ *
101
+ * Coordinates are the sheet's own and 0-based: row 0 is the row Excel labels 1, and
102
+ * a sheet whose data starts at F10 still reports ten rows and six columns, so a grid
103
+ * draws the data where its author put it.
104
+ */
105
+ export class Sheet {
106
+ /** The name on the sheet's tab. */
107
+ name;
108
+ /** One past the last row holding a value or covered by a merge. */
109
+ rows;
110
+ /**
111
+ * The sheet held more cells than {@link WorkbookOptions.maxCells}, and this is
112
+ * its first whole rows. Worth saying on screen: the rest of the file is not here.
113
+ */
114
+ truncated;
115
+ /** One past the last column holding a value or covered by a merge. */
116
+ cols;
117
+ /**
118
+ * Merged regions. The value lives in each one's top-left cell; the others are
119
+ * empty. Always empty for `.xlsb` and `.ods`, which calamine does not read
120
+ * merges from.
121
+ */
122
+ merges;
123
+ /**
124
+ * The styles {@link CellWindow.style} indexes into, with the default at 0.
125
+ * xlsx only: empty for every other format, whose cells all read as style 0.
126
+ */
127
+ styles;
128
+ /**
129
+ * Column widths and row heights as the author set them. xlsx only; for anything
130
+ * else a grid sizes columns itself.
131
+ */
132
+ layout;
133
+ #native;
134
+ /** @internal — construct via {@link Workbook.sheet}. */
135
+ constructor(native) {
136
+ this.#native = native;
137
+ this.name = native.name;
138
+ this.rows = native.rows;
139
+ this.truncated = native.truncated;
140
+ this.cols = native.cols;
141
+ this.merges = toMergedRanges(native.merges);
142
+ this.styles = native.styles.map(toCellStyle);
143
+ this.layout = native.layout ? toSheetLayout(native.layout) : undefined;
144
+ }
145
+ /**
146
+ * Read a rectangle of cells, clamped to the sheet.
147
+ *
148
+ * Synchronous, because the sheet is already parsed and the cost is the size of
149
+ * the window. Ask for what is on screen plus some overscan, not the whole sheet:
150
+ * a screenful is a few thousand cells and well under a millisecond.
151
+ */
152
+ window(range) {
153
+ // The bindings take u32s, which a negative overscan would wrap rather than clamp.
154
+ const at = (n) => Math.max(0, Math.floor(n));
155
+ return new CellWindow(this.#native.window(at(range.rowStart), at(range.rowEnd), at(range.colStart ?? 0), at(range.colEnd ?? this.cols)));
156
+ }
157
+ }
158
+ //# 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,6 @@
1
1
  {
2
2
  "name": "@build-qube/papyra",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Fast PDF rendering for Node and the browser, powered by Rust",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -40,7 +40,7 @@
40
40
  "test:integration": "bun test test/integration"
41
41
  },
42
42
  "dependencies": {
43
- "@build-qube/papyra-native": "0.2.0"
43
+ "@build-qube/papyra-native": "0.4.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "typescript": "^7.0.2"