@weasel-js/p-touch 0.0.0-stage → 1.16.1
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/LICENSE +21 -0
- package/README.md +77 -2
- package/dist/index.d.ts +211 -0
- package/dist/index.js +364 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 orochi235
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,78 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @weasel-js/p-touch
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Brother P-touch labels from weasel scenes: `.lbx` files P-touch Editor opens, and printing from
|
|
4
|
+
the browser over WebUSB or Web Serial.
|
|
5
|
+
|
|
6
|
+
Part of [weasel](https://github.com/orochi235/weasel), a domain-agnostic 2D
|
|
7
|
+
scene-graph canvas kit for React. See the
|
|
8
|
+
[API reference](https://orochi235.github.io/weasel/#pkg/p-touch).
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npm install @weasel-js/p-touch
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
A label is a region of a scene in points: it runs along x for its length and down y across the
|
|
15
|
+
tape, from `origin` (default `(0, 0)`). `TAPES` lists the cassette widths; `tapeForWidthMm(12)`
|
|
16
|
+
picks one.
|
|
17
|
+
|
|
18
|
+
## Printing
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { createBrotherPrinter, Printers, printLabel, renderLabel, tapeForWidthMm } from '@weasel-js/p-touch';
|
|
22
|
+
|
|
23
|
+
const printer = createBrotherPrinter();
|
|
24
|
+
const tape = tapeForWidthMm(12)!;
|
|
25
|
+
const media = (await printer.queryMedia()) ?? Printers.ptP710bt.media(tape.widthMm);
|
|
26
|
+
|
|
27
|
+
const image = await renderLabel({ scene, drawOne, length: 144, tape, media });
|
|
28
|
+
await printLabel({ printer, image, media, cuts: [72], requestDevice: true });
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`renderLabel` draws the label through the same renderer as the canvas, at one pixel per printer
|
|
32
|
+
dot, over the band the printhead reaches (`printableBand`); content outside it does not print.
|
|
33
|
+
`printLabel` turns gray into dots through `@weasel-js/dither` (`dither`, default a plain
|
|
34
|
+
threshold; `labelRaster` is that step alone). To keep a barcode and its quiet zone out of the
|
|
35
|
+
dither, pass a `protect` mask: `protectRects(items, rectOf, labelGrid({ tape, media }))` puts rects
|
|
36
|
+
in dots, and `protectMask` fills them. It then splits the label into
|
|
37
|
+
pages at `cuts`, and sends one job; with `requestDevice` it shows the browser's picker when no
|
|
38
|
+
printer has been granted, which only works inside a user gesture. The printer session, its
|
|
39
|
+
status and the device picker are [obwat](https://github.com/orochi235/obwat)'s
|
|
40
|
+
`createBrotherPrinter`, re-exported here.
|
|
41
|
+
|
|
42
|
+
## `.lbx` files
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { sceneToLbx, tapeForWidthMm } from '@weasel-js/p-touch';
|
|
46
|
+
|
|
47
|
+
const { bytes, skipped } = await sceneToLbx({ scene, drawOne, length: 144, tape: tapeForWidthMm(12)! });
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Written as objects P-touch Editor can edit:
|
|
51
|
+
|
|
52
|
+
| Draw command | `.lbx` object |
|
|
53
|
+
|---|---|
|
|
54
|
+
| a rect path, solid fill and stroke, mitered | `draw:rect`, framed at the stroke's outer edge |
|
|
55
|
+
| an open polyline, solid stroke, no fill | `draw:poly` |
|
|
56
|
+
| text in one face, size and solid color, in a box of known size | `text:text` |
|
|
57
|
+
|
|
58
|
+
Each holds only under a transform with no rotation, skew or mirror, at full opacity. Everything
|
|
59
|
+
else — curves, gradients, images, translucency, clips, effects — is drawn through a raster session
|
|
60
|
+
at `rasterDpi` (default 180) and placed as an image, one per run of such commands, so the file keeps
|
|
61
|
+
the scene's paint order. P-touch sets text again in its own copy of the face, so lines can break
|
|
62
|
+
where the canvas did not.
|
|
63
|
+
|
|
64
|
+
A node whose object its paint cannot say, such as a barcode drawn as bars, is written as that
|
|
65
|
+
object by the `drawOne` passed to `sceneToLbx` returning `lbxObjects([...])`: a command that draws
|
|
66
|
+
nothing and puts bil-lbx `LabelObject`s in the file where it stands. Their positions are in the
|
|
67
|
+
frame the command is drawn in and go through the transforms above it; under a rotation they are
|
|
68
|
+
skipped. The canvas keeps its own `drawOne`, which draws the bars.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
const exportDrawOne = (node, pose, view, ctx) => node.data.kind === 'barcode'
|
|
72
|
+
? [lbxObjects([{ type: 'barcode', protocol: 'CODE128', data: node.data.value, position: pose }])]
|
|
73
|
+
: drawOne(node, pose, view, ctx);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`writeLbx(page, { rasterize })` does the same from draw commands with no scene, as `writePdf`
|
|
77
|
+
does; `commandRasterizer` from `@weasel-js/core` is the `rasterize` hook. Without one, what it
|
|
78
|
+
cannot draw comes back in `skipped`. `lbxConfig` returns the label without building the file.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { RenderSceneToPixelsArgs, RasterImage, CommandRasterizer } from '@weasel-js/core';
|
|
2
|
+
import { OutputGrid, GlMat3 } from '@weasel-js/geom';
|
|
3
|
+
import { MediaSpec, BrotherPrinter, RgbaImage, Raster1bpp, PrinterStatus } from 'obwat';
|
|
4
|
+
export { BrotherPrinter, BrotherPrinterOptions, MediaSpec, NoGrantedDeviceError, PrinterStatus, Printers, Raster1bpp, RgbaImage, createBrotherPrinter } from 'obwat';
|
|
5
|
+
import { DitherOptions } from '@weasel-js/dither';
|
|
6
|
+
export { DitherMethod, DitherOptions } from '@weasel-js/dither';
|
|
7
|
+
import { DrawCommand } from '@weasel-js/renderer';
|
|
8
|
+
import { LabelConfig, LabelObject } from 'bil-lbx';
|
|
9
|
+
export { BarcodeObject, LabelConfig, LabelObject } from 'bil-lbx';
|
|
10
|
+
|
|
11
|
+
/** A P-touch tape width, in the units `.lbx` files and printers each name it by. */
|
|
12
|
+
interface Tape {
|
|
13
|
+
/** The cassette's name, as P-touch prints it: `'12mm'`. */
|
|
14
|
+
name: string;
|
|
15
|
+
/** Width across the tape in millimeters, what a printer's status reply and media lookup use. */
|
|
16
|
+
widthMm: number;
|
|
17
|
+
/** Width across the tape in points, a label's height in the scene and in an `.lbx` file. */
|
|
18
|
+
widthPt: number;
|
|
19
|
+
/** Brother's format code for the tape, written to an `.lbx` file's paper. */
|
|
20
|
+
format: number;
|
|
21
|
+
}
|
|
22
|
+
/** Every tape width `.lbx` files carry, narrowest first. */
|
|
23
|
+
declare const TAPES: readonly Tape[];
|
|
24
|
+
/** The tape `widthMm` wide, or undefined when no cassette is. */
|
|
25
|
+
declare function tapeForWidthMm(widthMm: number): Tape | undefined;
|
|
26
|
+
/** The tape named `name` (`'12mm'`), or undefined when no cassette is. */
|
|
27
|
+
declare function tapeNamed(name: string): Tape | undefined;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A label's scene to the pixels a printer lays down: one pixel per dot, the printable band only.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/** Where a label sits in its scene and what it prints on. Scene units are points. */
|
|
34
|
+
interface LabelGeometry {
|
|
35
|
+
/** Length along the tape, in points: the label's width in the scene. */
|
|
36
|
+
length: number;
|
|
37
|
+
tape: Tape;
|
|
38
|
+
/** The printer's geometry for this tape: `Printers.ptP710bt.media(tape.widthMm)`, or what `queryMedia` reports. */
|
|
39
|
+
media: MediaSpec;
|
|
40
|
+
/** The label's top-left corner in the scene. Default `(0, 0)`. */
|
|
41
|
+
origin?: {
|
|
42
|
+
x: number;
|
|
43
|
+
y: number;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The strip across the tape the printhead reaches, in points from the tape's top edge:
|
|
48
|
+
* `printableDots` at `dpi`, centered. Content outside it does not print.
|
|
49
|
+
*/
|
|
50
|
+
declare function printableBand(tape: Tape, media: MediaSpec): {
|
|
51
|
+
y: number;
|
|
52
|
+
height: number;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* The source rect and scale that render a label at one pixel per printer dot. The rect is the
|
|
56
|
+
* printable band, so the output is exactly `printableDots` tall and the label's length in dots long.
|
|
57
|
+
*/
|
|
58
|
+
declare function labelRenderPlan({ length, tape, media, origin }: LabelGeometry): {
|
|
59
|
+
sourceRect: {
|
|
60
|
+
x: number;
|
|
61
|
+
y: number;
|
|
62
|
+
width: number;
|
|
63
|
+
height: number;
|
|
64
|
+
};
|
|
65
|
+
scale: {
|
|
66
|
+
x: number;
|
|
67
|
+
y: number;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* The printer's dot grid in scene units: one dot's size, and the corner of the dot that prints
|
|
72
|
+
* first, at the top of the printable band. `protectRects` from `@weasel-js/dither` takes it to place
|
|
73
|
+
* rects a dither must leave at a plain threshold, such as a barcode and its quiet zone.
|
|
74
|
+
*/
|
|
75
|
+
declare function labelGrid({ tape, media, origin }: Omit<LabelGeometry, 'length'>): OutputGrid;
|
|
76
|
+
/** What {@link renderLabel} takes: the label's geometry, and what `renderSceneToPixels` takes besides its rect and scale. */
|
|
77
|
+
type RenderLabelArgs<TData, TLayer extends string, TPose> = LabelGeometry & Omit<RenderSceneToPixelsArgs<TData, TLayer, TPose>, 'sourceRect' | 'scale'>;
|
|
78
|
+
/**
|
|
79
|
+
* Render a label to straight RGBA at the printer's resolution, on white unless `background` says
|
|
80
|
+
* otherwise: the image `printLabel` takes. Loads the fonts and paint kinds the scene draws with first.
|
|
81
|
+
*/
|
|
82
|
+
declare function renderLabel<TData, TLayer extends string, TPose>(args: RenderLabelArgs<TData, TLayer, TPose>): Promise<RasterImage>;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* A rendered label to paper: dots, pages at the cut marks, and the job sent through obwat.
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Split a raster into pages at `cutsPt`, positions along the label in points from its leading
|
|
90
|
+
* edge. The printer cuts between pages when auto-cut is on. Marks at or past either end, and marks
|
|
91
|
+
* landing on the same dot, are dropped; with none left the raster is one page.
|
|
92
|
+
*/
|
|
93
|
+
declare function sliceAtCuts(raster: Raster1bpp, cutsPt: readonly number[], dpi: number): Raster1bpp[];
|
|
94
|
+
/**
|
|
95
|
+
* `image` as the raster a printer lays down: dithered by `@weasel-js/dither`, then packed. The dots
|
|
96
|
+
* reach obwat as pure black on white, which its own threshold passes through unchanged.
|
|
97
|
+
*/
|
|
98
|
+
declare function labelRaster(image: RgbaImage, media: MediaSpec, dither?: DitherOptions): Raster1bpp;
|
|
99
|
+
/** What {@link printLabel} sends, and how. */
|
|
100
|
+
interface PrintLabelOptions {
|
|
101
|
+
printer: BrotherPrinter;
|
|
102
|
+
/** The label at one pixel per dot, `media.printableDots` tall: what `renderLabel` returns. */
|
|
103
|
+
image: RgbaImage;
|
|
104
|
+
media: MediaSpec;
|
|
105
|
+
/** Cut after each page and at the end. Default true. */
|
|
106
|
+
autoCut?: boolean;
|
|
107
|
+
/** Where to cut along the label, in points: see {@link sliceAtCuts}. */
|
|
108
|
+
cuts?: readonly number[];
|
|
109
|
+
/** How gray becomes dots, as `ditherPixels` takes it; `labelGrid` places a `protect` mask's rects. Default a plain threshold. */
|
|
110
|
+
dither?: DitherOptions;
|
|
111
|
+
/**
|
|
112
|
+
* When no printer has been granted yet, show the browser's device picker and print to what is
|
|
113
|
+
* chosen, rather than rejecting with obwat's `NoGrantedDeviceError`. Only works inside a user
|
|
114
|
+
* gesture. Default false.
|
|
115
|
+
*/
|
|
116
|
+
requestDevice?: boolean;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Print `image` as one job. Resolves with the printer's status reply, which reports what it found
|
|
120
|
+
* as the job started (a missing tape, an open cover), not that it finished.
|
|
121
|
+
*/
|
|
122
|
+
declare function printLabel(opts: PrintLabelOptions): Promise<PrinterStatus>;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* A label's draw commands as a P-touch `.lbx` file.
|
|
126
|
+
*/
|
|
127
|
+
|
|
128
|
+
/** One label: its size and the commands painted on it, in points with the origin at its top left. */
|
|
129
|
+
interface LbxPage {
|
|
130
|
+
/** Length along the tape, in points. */
|
|
131
|
+
length: number;
|
|
132
|
+
tape: Tape;
|
|
133
|
+
commands: readonly DrawCommand[];
|
|
134
|
+
/** What a `world`-unit paint in `commands` resolves through, as `PdfPage.view`. */
|
|
135
|
+
view?: GlMat3;
|
|
136
|
+
/** Let P-touch size the label to what is on it. `length` is still written as the fitted length. */
|
|
137
|
+
autoLength?: boolean;
|
|
138
|
+
/** Where the printer cuts, in points along the label: a strip of labels in one file. */
|
|
139
|
+
cuts?: readonly number[];
|
|
140
|
+
}
|
|
141
|
+
/** Settings and the host callback for {@link writeLbx}. */
|
|
142
|
+
interface WriteLbxOptions {
|
|
143
|
+
/** Draws what an `.lbx` object cannot state; `commandRasterizer` from `@weasel-js/core` is one. Without it those commands are skipped. */
|
|
144
|
+
rasterize?: CommandRasterizer;
|
|
145
|
+
/** Pixels per inch for rasterized content. Default 180, the PT-P710BT's. */
|
|
146
|
+
rasterDpi?: number;
|
|
147
|
+
}
|
|
148
|
+
/** A command the writer left out of the file, with the reason. */
|
|
149
|
+
interface LbxSkip {
|
|
150
|
+
kind: DrawCommand['kind'];
|
|
151
|
+
detail: string;
|
|
152
|
+
}
|
|
153
|
+
/** The finished file, the label it describes, and what it had to leave out. */
|
|
154
|
+
interface LbxResult {
|
|
155
|
+
bytes: Uint8Array;
|
|
156
|
+
config: LabelConfig;
|
|
157
|
+
skipped: readonly LbxSkip[];
|
|
158
|
+
}
|
|
159
|
+
/** The `.lbx` label `page` describes, without the file: its paper, cuts and objects. */
|
|
160
|
+
declare function lbxConfig(page: LbxPage, opts?: WriteLbxOptions): Promise<{
|
|
161
|
+
config: LabelConfig;
|
|
162
|
+
skipped: LbxSkip[];
|
|
163
|
+
}>;
|
|
164
|
+
/**
|
|
165
|
+
* Write `page` as an `.lbx` file. Axis-aligned rects, open polylines and plain text boxes become
|
|
166
|
+
* objects P-touch Editor can edit; everything else is drawn through `rasterize` and placed as an
|
|
167
|
+
* image at `rasterDpi`. Objects keep the commands' paint order.
|
|
168
|
+
*/
|
|
169
|
+
declare function writeLbx(page: LbxPage, opts?: WriteLbxOptions): Promise<LbxResult>;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* A label's scene as an `.lbx` file.
|
|
173
|
+
*/
|
|
174
|
+
|
|
175
|
+
/** What {@link sceneToLbx} takes: the label's place in its scene, and what `renderSceneToPixels` takes besides its rect and scale. */
|
|
176
|
+
type SceneToLbxArgs<TData, TLayer extends string, TPose> = Omit<RenderSceneToPixelsArgs<TData, TLayer, TPose>, 'sourceRect' | 'scale' | 'background' | 'gl' | 'createCanvas'> & {
|
|
177
|
+
/** Length along the tape, in points: the label's width in the scene. */
|
|
178
|
+
length: number;
|
|
179
|
+
tape: Tape;
|
|
180
|
+
/** The label's top-left corner in the scene. Default `(0, 0)`. */
|
|
181
|
+
origin?: {
|
|
182
|
+
x: number;
|
|
183
|
+
y: number;
|
|
184
|
+
};
|
|
185
|
+
autoLength?: boolean;
|
|
186
|
+
cuts?: readonly number[];
|
|
187
|
+
rasterDpi?: WriteLbxOptions['rasterDpi'];
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* Write the label at `origin` in `scene` as an `.lbx` file, drawn the way the canvas draws it
|
|
191
|
+
* (`drawOne` included). What the file cannot state as an object is drawn through a raster session
|
|
192
|
+
* of its own and placed as an image, so this needs WebGL2 where the scene has any.
|
|
193
|
+
*/
|
|
194
|
+
declare function sceneToLbx<TData, TLayer extends string, TPose>(args: SceneToLbxArgs<TData, TLayer, TPose>): Promise<LbxResult>;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* `.lbx` objects carried through a scene's draw commands: how a node the writer would otherwise see
|
|
198
|
+
* as paint (a barcode's bars) reaches the file as the object it is.
|
|
199
|
+
*/
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* A command that draws nothing and puts `objects` in the `.lbx` file where it stands, in its paint
|
|
203
|
+
* order. Positions (and a line's points) are in the frame the command is drawn in, and the writer
|
|
204
|
+
* carries them through the transforms above it; sizes inside an object, such as a font size or a
|
|
205
|
+
* barcode's bar width, are written as given. Return it from the `drawOne` passed to `sceneToLbx` for
|
|
206
|
+
* a node whose object the draw commands cannot say, as a barcode's bars do not say it is a barcode.
|
|
207
|
+
*/
|
|
208
|
+
declare function lbxObjects(objects: readonly LabelObject[]): DrawCommand;
|
|
209
|
+
|
|
210
|
+
export { TAPES, labelGrid, labelRaster, labelRenderPlan, lbxConfig, lbxObjects, printLabel, printableBand, renderLabel, sceneToLbx, sliceAtCuts, tapeForWidthMm, tapeNamed, writeLbx };
|
|
211
|
+
export type { LabelGeometry, LbxPage, LbxResult, LbxSkip, PrintLabelOptions, RenderLabelArgs, SceneToLbxArgs, Tape, WriteLbxOptions };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { TAPE, AUTO_LENGTH_MAX_PT, backgroundFor, buildLbx, encodeBmp32 } from 'bil-lbx';
|
|
2
|
+
import { warmRender, renderSceneToPixels, planPixelRender, createRasterSession, viewToMat3, commandRasterizer } from '@weasel-js/core';
|
|
3
|
+
import { ditherPixels, maskToRgba } from '@weasel-js/dither';
|
|
4
|
+
import { rgbaToRaster, NoGrantedDeviceError } from 'obwat';
|
|
5
|
+
export { NoGrantedDeviceError, Printers, createBrotherPrinter } from 'obwat';
|
|
6
|
+
import { mat3, PATH_M, PATH_L, PATH_CMD_LENGTHS } from '@weasel-js/geom';
|
|
7
|
+
import { isSolidPaint, resolveScreenLength, parseColor } from '@weasel-js/paint';
|
|
8
|
+
|
|
9
|
+
// src/tape.ts
|
|
10
|
+
var TAPES = Object.entries(TAPE).map(([name, { width, format }]) => ({
|
|
11
|
+
name,
|
|
12
|
+
// From the name, not the point width: 3.5mm does not round-trip through 72/25.4.
|
|
13
|
+
widthMm: Number.parseFloat(name),
|
|
14
|
+
widthPt: width,
|
|
15
|
+
format
|
|
16
|
+
}));
|
|
17
|
+
function tapeForWidthMm(widthMm) {
|
|
18
|
+
return TAPES.find((tape) => tape.widthMm === widthMm);
|
|
19
|
+
}
|
|
20
|
+
function tapeNamed(name) {
|
|
21
|
+
return TAPES.find((tape) => tape.name === name);
|
|
22
|
+
}
|
|
23
|
+
function printableBand(tape, media) {
|
|
24
|
+
const height = media.printableDots * (72 / media.dpi);
|
|
25
|
+
return { y: (tape.widthPt - height) / 2, height };
|
|
26
|
+
}
|
|
27
|
+
function labelRenderPlan({ length, tape, media, origin = { x: 0, y: 0 } }) {
|
|
28
|
+
const band = printableBand(tape, media);
|
|
29
|
+
const dotsPerPt = media.dpi / 72;
|
|
30
|
+
return {
|
|
31
|
+
sourceRect: { x: origin.x, y: origin.y + band.y, width: length, height: band.height },
|
|
32
|
+
scale: { x: dotsPerPt, y: dotsPerPt }
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function labelGrid({ tape, media, origin = { x: 0, y: 0 } }) {
|
|
36
|
+
return { pitch: 72 / media.dpi, origin: { x: origin.x, y: origin.y + printableBand(tape, media).y } };
|
|
37
|
+
}
|
|
38
|
+
async function renderLabel(args) {
|
|
39
|
+
const { length, tape, media, origin, ...rest } = args;
|
|
40
|
+
const render = { background: "#ffffff", ...rest, ...labelRenderPlan({ length, tape, media, origin }) };
|
|
41
|
+
await warmRender({ render });
|
|
42
|
+
return renderSceneToPixels(render);
|
|
43
|
+
}
|
|
44
|
+
function sliceAtCuts(raster, cutsPt, dpi) {
|
|
45
|
+
const cutDots = [...new Set(cutsPt.map((x) => Math.round(x * (dpi / 72))))].filter((d) => d > 0 && d < raster.lineCount).sort((a, b) => a - b);
|
|
46
|
+
const bounds = [0, ...cutDots, raster.lineCount];
|
|
47
|
+
const pages = [];
|
|
48
|
+
for (let i = 0; i < bounds.length - 1; i++) {
|
|
49
|
+
const rows = raster.rows.slice(bounds[i], bounds[i + 1]);
|
|
50
|
+
pages.push({ lineBytes: raster.lineBytes, lineCount: rows.length, rows });
|
|
51
|
+
}
|
|
52
|
+
return pages;
|
|
53
|
+
}
|
|
54
|
+
var INK = [0, 0, 0, 255];
|
|
55
|
+
var BLANK = [255, 255, 255, 255];
|
|
56
|
+
function labelRaster(image, media, dither) {
|
|
57
|
+
const mask = ditherPixels(image, dither);
|
|
58
|
+
const dots = maskToRgba(mask, image.width, image.height, { ink: INK, blank: BLANK });
|
|
59
|
+
return rgbaToRaster({ width: image.width, height: image.height, data: dots }, media);
|
|
60
|
+
}
|
|
61
|
+
async function printLabel(opts) {
|
|
62
|
+
const { printer, image, media, autoCut = true, cuts = [], dither, requestDevice = false } = opts;
|
|
63
|
+
const pages = sliceAtCuts(labelRaster(image, media, dither), cuts, media.dpi);
|
|
64
|
+
const job = { tapeWidthMm: media.tapeWidthMm, autoCut, marginDots: 0 };
|
|
65
|
+
try {
|
|
66
|
+
return await printer.print(pages, job);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
if (!requestDevice || !(err instanceof NoGrantedDeviceError)) throw err;
|
|
69
|
+
await printer.requestDevice();
|
|
70
|
+
return await printer.print(pages, job);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
var EPS = 1e-6;
|
|
74
|
+
function axisMap(m) {
|
|
75
|
+
if (Math.abs(m[1]) > EPS || Math.abs(m[3]) > EPS) return void 0;
|
|
76
|
+
if (m[0] <= 0 || m[4] <= 0) return void 0;
|
|
77
|
+
return { sx: m[0], sy: m[4], tx: m[6], ty: m[7] };
|
|
78
|
+
}
|
|
79
|
+
function uniformScale(map) {
|
|
80
|
+
return Math.abs(map.sx - map.sy) <= EPS * Math.max(map.sx, map.sy) ? map.sx : void 0;
|
|
81
|
+
}
|
|
82
|
+
function hexByte(v) {
|
|
83
|
+
return Math.round(Math.min(1, Math.max(0, v)) * 255).toString(16).padStart(2, "0");
|
|
84
|
+
}
|
|
85
|
+
function opaqueHex(color, opacity) {
|
|
86
|
+
const [r, g, b, a] = typeof color === "string" ? parseColor(color) : color;
|
|
87
|
+
if (Math.abs(a * opacity - 1) > 1e-3) return void 0;
|
|
88
|
+
return `#${hexByte(r)}${hexByte(g)}${hexByte(b)}`;
|
|
89
|
+
}
|
|
90
|
+
function fillColor(fill, alpha) {
|
|
91
|
+
if (fill?.paint === void 0) return null;
|
|
92
|
+
if (!isSolidPaint(fill.paint)) return void 0;
|
|
93
|
+
return opaqueHex(fill.paint.color, (fill.paint.opacity ?? 1) * alpha);
|
|
94
|
+
}
|
|
95
|
+
var PLAIN_STROKE_KEYS = /* @__PURE__ */ new Set(["paint", "width", "cap", "join", "miterLimit", "align"]);
|
|
96
|
+
function plainStroke(stroke, alpha, scale) {
|
|
97
|
+
if (stroke?.paint === void 0) return null;
|
|
98
|
+
for (const [key, value] of Object.entries(stroke)) {
|
|
99
|
+
if (value !== void 0 && !PLAIN_STROKE_KEYS.has(key)) return void 0;
|
|
100
|
+
}
|
|
101
|
+
if (!isSolidPaint(stroke.paint)) return void 0;
|
|
102
|
+
const color = opaqueHex(stroke.paint.color, (stroke.paint.opacity ?? 1) * alpha);
|
|
103
|
+
if (color === void 0) return void 0;
|
|
104
|
+
const width = resolveScreenLength(stroke.width ?? 1, scale) * scale;
|
|
105
|
+
if (!(width > 0)) return null;
|
|
106
|
+
return { color, width, cap: stroke.cap ?? "butt", join: stroke.join ?? "miter", align: stroke.align ?? "center" };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/lbx/carried.ts
|
|
110
|
+
var CARRIED = /* @__PURE__ */ Symbol("weasel.lbxObjects");
|
|
111
|
+
function lbxObjects(objects) {
|
|
112
|
+
const carrier = { kind: "group", children: [], [CARRIED]: objects };
|
|
113
|
+
return carrier;
|
|
114
|
+
}
|
|
115
|
+
function carriedObjects(cmd) {
|
|
116
|
+
return cmd[CARRIED];
|
|
117
|
+
}
|
|
118
|
+
function placeObject(obj, map) {
|
|
119
|
+
const { x, y, width, height } = obj.position;
|
|
120
|
+
const position = { ...obj.position, x: x * map.sx + map.tx, y: y * map.sy + map.ty, width: width * map.sx, height: height * map.sy };
|
|
121
|
+
if (obj.type !== "line") return { ...obj, position };
|
|
122
|
+
const points = obj.points.map((p) => ({ x: p.x * map.sx + map.tx, y: p.y * map.sy + map.ty }));
|
|
123
|
+
return { ...obj, position, points };
|
|
124
|
+
}
|
|
125
|
+
function placeCarried(objects, state) {
|
|
126
|
+
const map = axisMap(state.transform);
|
|
127
|
+
return map && objects.map((obj) => placeObject(obj, map));
|
|
128
|
+
}
|
|
129
|
+
var NO_PEN = { style: "NULL" };
|
|
130
|
+
var NO_BRUSH = { style: "NULL" };
|
|
131
|
+
function rectObject(cmd, state) {
|
|
132
|
+
if (cmd.path.kind !== "rect" || cmd.vertexColors) return void 0;
|
|
133
|
+
const map = axisMap(state.transform);
|
|
134
|
+
if (!map) return void 0;
|
|
135
|
+
const fill = fillColor(cmd.fill, state.alpha);
|
|
136
|
+
if (fill === void 0) return void 0;
|
|
137
|
+
const scale = cmd.stroke?.paint === void 0 ? map.sx : uniformScale(map);
|
|
138
|
+
if (scale === void 0) return void 0;
|
|
139
|
+
const stroke = plainStroke(cmd.stroke, state.alpha, scale);
|
|
140
|
+
if (stroke === void 0 || stroke && stroke.join !== "miter") return void 0;
|
|
141
|
+
if (fill === null && stroke === null) return [];
|
|
142
|
+
const { x, y, width, height } = cmd.path;
|
|
143
|
+
const grow = stroke === null ? 0 : stroke.align === "center" ? stroke.width / 2 : stroke.align === "outer" ? stroke.width : 0;
|
|
144
|
+
return [{
|
|
145
|
+
type: "rect",
|
|
146
|
+
shape: "RECTANGLE",
|
|
147
|
+
position: {
|
|
148
|
+
x: x * map.sx + map.tx - grow,
|
|
149
|
+
y: y * map.sy + map.ty - grow,
|
|
150
|
+
width: width * map.sx + grow * 2,
|
|
151
|
+
height: height * map.sy + grow * 2
|
|
152
|
+
},
|
|
153
|
+
pen: stroke === null ? NO_PEN : { style: "INSIDEFRAME", widthX: stroke.width, widthY: stroke.width, color: stroke.color },
|
|
154
|
+
brush: fill === null ? NO_BRUSH : { style: "SOLID", color: fill }
|
|
155
|
+
}];
|
|
156
|
+
}
|
|
157
|
+
function polylinePoints(path) {
|
|
158
|
+
if (path.corners?.some((c) => c !== null)) return void 0;
|
|
159
|
+
const ox = path.origin?.x ?? 0;
|
|
160
|
+
const oy = path.origin?.y ?? 0;
|
|
161
|
+
const points = [];
|
|
162
|
+
let at = 0;
|
|
163
|
+
for (let i = 0; i < path.commands.length; i++) {
|
|
164
|
+
const code = path.commands[i];
|
|
165
|
+
if (code !== (i === 0 ? PATH_M : PATH_L)) return void 0;
|
|
166
|
+
points.push({ x: path.coords[at] + ox, y: path.coords[at + 1] + oy });
|
|
167
|
+
at += PATH_CMD_LENGTHS[code];
|
|
168
|
+
}
|
|
169
|
+
return points.length >= 2 ? points : void 0;
|
|
170
|
+
}
|
|
171
|
+
var ARROW_FOR_CAP = { butt: "SQUARE", round: "ROUND", square: "SQUARE" };
|
|
172
|
+
function lineObject(cmd, state) {
|
|
173
|
+
if (cmd.path.kind !== "polygon" || cmd.vertexColors || cmd.fill?.paint !== void 0) return void 0;
|
|
174
|
+
const map = axisMap(state.transform);
|
|
175
|
+
const scale = map && uniformScale(map);
|
|
176
|
+
if (!map || scale === void 0) return void 0;
|
|
177
|
+
const local = polylinePoints(cmd.path);
|
|
178
|
+
const stroke = plainStroke(cmd.stroke, state.alpha, scale);
|
|
179
|
+
if (!local || !stroke || stroke.align !== "center") return void 0;
|
|
180
|
+
const points = local.map((p) => {
|
|
181
|
+
const [x2, y2] = mat3.apply(state.transform, p.x, p.y);
|
|
182
|
+
return { x: x2, y: y2 };
|
|
183
|
+
});
|
|
184
|
+
const xs = points.map((p) => p.x);
|
|
185
|
+
const ys = points.map((p) => p.y);
|
|
186
|
+
const x = Math.min(...xs);
|
|
187
|
+
const y = Math.min(...ys);
|
|
188
|
+
const arrow = ARROW_FOR_CAP[stroke.cap];
|
|
189
|
+
return {
|
|
190
|
+
type: "line",
|
|
191
|
+
position: { x, y, width: Math.max(...xs) - x, height: Math.max(...ys) - y },
|
|
192
|
+
points,
|
|
193
|
+
arrowBegin: arrow,
|
|
194
|
+
arrowEnd: arrow,
|
|
195
|
+
pen: { style: "SOLID", widthX: stroke.width, widthY: stroke.width, color: stroke.color },
|
|
196
|
+
brush: NO_BRUSH
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
var H_ALIGN = { left: "LEFT", start: "LEFT", center: "CENTER", right: "RIGHT", end: "RIGHT", justify: "JUSTIFY" };
|
|
200
|
+
var V_ALIGN = { top: "TOP", center: "CENTER", bottom: "BOTTOM" };
|
|
201
|
+
function textObject(cmd, state) {
|
|
202
|
+
const map = axisMap(state.transform);
|
|
203
|
+
const scale = map && uniformScale(map);
|
|
204
|
+
const boxWidth = cmd.width ?? cmd.maxWidth;
|
|
205
|
+
if (!map || scale === void 0 || boxWidth === void 0 || cmd.height === void 0) return void 0;
|
|
206
|
+
if (cmd.direction === "rtl" || cmd.runs.length === 0) return void 0;
|
|
207
|
+
const [first] = cmd.runs;
|
|
208
|
+
let color;
|
|
209
|
+
for (const run of cmd.runs) {
|
|
210
|
+
if (run.fontFamily !== first.fontFamily || run.fontSize !== first.fontSize) return void 0;
|
|
211
|
+
if (run.fontWeight !== first.fontWeight || run.fontStyle !== first.fontStyle) return void 0;
|
|
212
|
+
if (run.underline !== first.underline || run.strikethrough !== first.strikethrough) return void 0;
|
|
213
|
+
if (run.stroke?.paint !== void 0 || run.overline || run.sizeMap || run.rotate) return void 0;
|
|
214
|
+
if (run.letterSpacing !== 0 || run.baselineShift !== 0) return void 0;
|
|
215
|
+
const paint = run.fill?.paint;
|
|
216
|
+
if (!paint || paint.kind !== void 0 && paint.kind !== "solid") return void 0;
|
|
217
|
+
const hex = opaqueHex(paint.color, (paint.opacity ?? 1) * state.alpha);
|
|
218
|
+
if (hex === void 0 || color !== void 0 && hex !== color) return void 0;
|
|
219
|
+
color = hex;
|
|
220
|
+
}
|
|
221
|
+
const align = cmd.justify ? "justify" : cmd.align ?? cmd.style.align ?? "left";
|
|
222
|
+
return {
|
|
223
|
+
type: "text",
|
|
224
|
+
position: { x: cmd.x * map.sx + map.tx, y: cmd.y * map.sy + map.ty, width: boxWidth * scale, height: cmd.height * scale },
|
|
225
|
+
font: { name: first.fontFamily, size: first.fontSize * scale, weight: first.fontWeight, italic: first.fontStyle === "italic" },
|
|
226
|
+
textStyle: { color, underline: first.underline ? 1 : 0, strikeout: first.strikethrough ? 1 : 0 },
|
|
227
|
+
control: "FIXEDFRAME",
|
|
228
|
+
shrink: false,
|
|
229
|
+
autoLF: cmd.maxWidth !== void 0,
|
|
230
|
+
horizontalAlignment: H_ALIGN[align] ?? "LEFT",
|
|
231
|
+
verticalAlignment: V_ALIGN[cmd.verticalAlign ?? "top"] ?? "TOP",
|
|
232
|
+
data: cmd.runs.map((run) => run.text).join("")
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// src/lbx/writeLbx.ts
|
|
237
|
+
function plainGroup(g) {
|
|
238
|
+
const composite = g.composite ?? "source-over";
|
|
239
|
+
return !g.clip && !g.effects?.length && !g.isolate && !g.colorMatrix && composite === "source-over";
|
|
240
|
+
}
|
|
241
|
+
function placed(cmd, state) {
|
|
242
|
+
return { kind: "group", transform: state.transform, alpha: state.alpha, children: [cmd] };
|
|
243
|
+
}
|
|
244
|
+
function collect(commands, state, out, skipped) {
|
|
245
|
+
for (const cmd of commands) {
|
|
246
|
+
const carried = carriedObjects(cmd);
|
|
247
|
+
if (carried) {
|
|
248
|
+
const placed2 = placeCarried(carried, state);
|
|
249
|
+
if (placed2) for (const object of placed2) out.push({ object });
|
|
250
|
+
else skipped.push({ kind: "group", detail: "lbxObjects under a rotation, skew or mirror" });
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (cmd.kind === "group" && plainGroup(cmd)) {
|
|
254
|
+
const transform = cmd.transform ? mat3.multiply(Float64Array.from(state.transform), cmd.transform) : state.transform;
|
|
255
|
+
collect(cmd.children, { transform, alpha: state.alpha * (cmd.alpha ?? 1) }, out, skipped);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
const mapped = cmd.kind === "path" ? rectObject(cmd, state) ?? lineObject(cmd, state) : cmd.kind === "text" ? textObject(cmd, state) : void 0;
|
|
259
|
+
if (mapped === void 0) out.push({ raster: placed(cmd, state) });
|
|
260
|
+
else for (const object of Array.isArray(mapped) ? mapped : [mapped]) out.push({ object });
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function inkBounds(img) {
|
|
264
|
+
let x0 = img.width, y0 = img.height, x1 = -1, y1 = -1;
|
|
265
|
+
for (let y = 0; y < img.height; y++) {
|
|
266
|
+
for (let x = 0; x < img.width; x++) {
|
|
267
|
+
if (img.data[(y * img.width + x) * 4 + 3] === 0) continue;
|
|
268
|
+
if (x < x0) x0 = x;
|
|
269
|
+
if (x > x1) x1 = x;
|
|
270
|
+
if (y < y0) y0 = y;
|
|
271
|
+
if (y > y1) y1 = y;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return x1 < 0 ? void 0 : { x: x0, y: y0, width: x1 - x0 + 1, height: y1 - y0 + 1 };
|
|
275
|
+
}
|
|
276
|
+
function crop(img, box) {
|
|
277
|
+
const out = new Uint8ClampedArray(box.width * box.height * 4);
|
|
278
|
+
for (let y = 0; y < box.height; y++) {
|
|
279
|
+
const from = ((box.y + y) * img.width + box.x) * 4;
|
|
280
|
+
out.set(img.data.subarray(from, from + box.width * 4), y * box.width * 4);
|
|
281
|
+
}
|
|
282
|
+
return out;
|
|
283
|
+
}
|
|
284
|
+
async function rasterObject(commands, page, rasterize, dpi) {
|
|
285
|
+
const scale = dpi / 72;
|
|
286
|
+
const img = await rasterize(commands, { x: 0, y: 0, width: page.length, height: page.tape.widthPt }, scale, page.view);
|
|
287
|
+
const box = inkBounds(img);
|
|
288
|
+
if (!box) return void 0;
|
|
289
|
+
return {
|
|
290
|
+
type: "image",
|
|
291
|
+
position: { x: box.x / scale, y: box.y / scale, width: box.width / scale, height: box.height / scale },
|
|
292
|
+
imageData: encodeBmp32(crop(img, box), box.width, box.height)
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
async function settle(items, page, opts, skipped) {
|
|
296
|
+
const objects = [];
|
|
297
|
+
let run = [];
|
|
298
|
+
const flush = async () => {
|
|
299
|
+
if (run.length === 0) return;
|
|
300
|
+
if (opts.rasterize) {
|
|
301
|
+
const image = await rasterObject(run, page, opts.rasterize, opts.rasterDpi ?? 180);
|
|
302
|
+
if (image) objects.push(image);
|
|
303
|
+
} else {
|
|
304
|
+
for (const cmd of run) skipped.push({ kind: innermost(cmd).kind, detail: "no rasterize hook for what .lbx cannot draw" });
|
|
305
|
+
}
|
|
306
|
+
run = [];
|
|
307
|
+
};
|
|
308
|
+
for (const item of items) {
|
|
309
|
+
if ("raster" in item) run.push(item.raster);
|
|
310
|
+
else {
|
|
311
|
+
await flush();
|
|
312
|
+
objects.push(item.object);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
await flush();
|
|
316
|
+
return objects;
|
|
317
|
+
}
|
|
318
|
+
function innermost(cmd) {
|
|
319
|
+
return cmd.kind === "group" && cmd.children.length === 1 ? innermost(cmd.children[0]) : cmd;
|
|
320
|
+
}
|
|
321
|
+
async function lbxConfig(page, opts = {}) {
|
|
322
|
+
const items = [];
|
|
323
|
+
const skipped = [];
|
|
324
|
+
collect(page.commands, { transform: mat3.identity(), alpha: 1 }, items, skipped);
|
|
325
|
+
const objects = await settle(items, page, opts, skipped);
|
|
326
|
+
const paper = {
|
|
327
|
+
width: page.tape.widthPt,
|
|
328
|
+
format: page.tape.format,
|
|
329
|
+
autoLength: page.autoLength ?? false,
|
|
330
|
+
// Under auto length P-touch parks its 1000mm ceiling here; the length lives in the background band.
|
|
331
|
+
height: page.autoLength ? AUTO_LENGTH_MAX_PT : page.length
|
|
332
|
+
};
|
|
333
|
+
const cuts = page.cuts?.filter((x) => x > 0 && x < page.length) ?? [];
|
|
334
|
+
const config = {
|
|
335
|
+
paper,
|
|
336
|
+
background: backgroundFor(paper, page.length),
|
|
337
|
+
objects,
|
|
338
|
+
...cuts.length > 0 ? { cut: { freeCut: [...cuts].sort((a, b) => a - b) } } : {}
|
|
339
|
+
};
|
|
340
|
+
return { config, skipped };
|
|
341
|
+
}
|
|
342
|
+
async function writeLbx(page, opts = {}) {
|
|
343
|
+
const { config, skipped } = await lbxConfig(page, opts);
|
|
344
|
+
return { bytes: await buildLbx(config), config, skipped };
|
|
345
|
+
}
|
|
346
|
+
async function sceneToLbx(args) {
|
|
347
|
+
const { length, tape, origin = { x: 0, y: 0 }, autoLength, cuts, rasterDpi, ...render } = args;
|
|
348
|
+
const plan = { ...render, sourceRect: { x: origin.x, y: origin.y, width: length, height: tape.widthPt }, scale: { x: 1, y: 1 } };
|
|
349
|
+
await warmRender({ render: plan });
|
|
350
|
+
const { commands, view } = planPixelRender(plan);
|
|
351
|
+
const session = createRasterSession();
|
|
352
|
+
try {
|
|
353
|
+
return await writeLbx(
|
|
354
|
+
{ length, tape, commands, view: viewToMat3(view), autoLength, cuts },
|
|
355
|
+
{ rasterize: commandRasterizer(session), rasterDpi }
|
|
356
|
+
);
|
|
357
|
+
} finally {
|
|
358
|
+
session.dispose();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export { TAPES, labelGrid, labelRaster, labelRenderPlan, lbxConfig, lbxObjects, printLabel, printableBand, renderLabel, sceneToLbx, sliceAtCuts, tapeForWidthMm, tapeNamed, writeLbx };
|
|
363
|
+
//# sourceMappingURL=index.js.map
|
|
364
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tape.ts","../src/render.ts","../src/print.ts","../src/lbx/style.ts","../src/lbx/carried.ts","../src/lbx/shapes.ts","../src/lbx/writeLbx.ts","../src/lbx/sceneLbx.ts"],"names":["x","y","placed","mat3","warmRender"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Tape cassettes: how wide a label is, in the units each side of the pipeline counts in.\n */\nimport { TAPE } from 'bil-lbx';\n\n/** A P-touch tape width, in the units `.lbx` files and printers each name it by. */\nexport interface Tape {\n /** The cassette's name, as P-touch prints it: `'12mm'`. */\n name: string;\n /** Width across the tape in millimeters, what a printer's status reply and media lookup use. */\n widthMm: number;\n /** Width across the tape in points, a label's height in the scene and in an `.lbx` file. */\n widthPt: number;\n /** Brother's format code for the tape, written to an `.lbx` file's paper. */\n format: number;\n}\n\n/** Every tape width `.lbx` files carry, narrowest first. */\nexport const TAPES: readonly Tape[] = Object.entries(TAPE).map(([name, { width, format }]) => ({\n name,\n // From the name, not the point width: 3.5mm does not round-trip through 72/25.4.\n widthMm: Number.parseFloat(name),\n widthPt: width,\n format,\n}));\n\n/** The tape `widthMm` wide, or undefined when no cassette is. */\nexport function tapeForWidthMm(widthMm: number): Tape | undefined {\n return TAPES.find((tape) => tape.widthMm === widthMm);\n}\n\n/** The tape named `name` (`'12mm'`), or undefined when no cassette is. */\nexport function tapeNamed(name: string): Tape | undefined {\n return TAPES.find((tape) => tape.name === name);\n}\n","/**\n * A label's scene to the pixels a printer lays down: one pixel per dot, the printable band only.\n */\nimport { renderSceneToPixels, warmRender, type RasterImage, type RenderSceneToPixelsArgs } from '@weasel-js/core';\nimport type { OutputGrid } from '@weasel-js/geom';\nimport type { MediaSpec } from 'obwat';\nimport type { Tape } from './tape';\n\n/** Where a label sits in its scene and what it prints on. Scene units are points. */\nexport interface LabelGeometry {\n /** Length along the tape, in points: the label's width in the scene. */\n length: number;\n tape: Tape;\n /** The printer's geometry for this tape: `Printers.ptP710bt.media(tape.widthMm)`, or what `queryMedia` reports. */\n media: MediaSpec;\n /** The label's top-left corner in the scene. Default `(0, 0)`. */\n origin?: { x: number; y: number };\n}\n\n/**\n * The strip across the tape the printhead reaches, in points from the tape's top edge:\n * `printableDots` at `dpi`, centered. Content outside it does not print.\n */\nexport function printableBand(tape: Tape, media: MediaSpec): { y: number; height: number } {\n const height = media.printableDots * (72 / media.dpi);\n return { y: (tape.widthPt - height) / 2, height };\n}\n\n/**\n * The source rect and scale that render a label at one pixel per printer dot. The rect is the\n * printable band, so the output is exactly `printableDots` tall and the label's length in dots long.\n */\nexport function labelRenderPlan({ length, tape, media, origin = { x: 0, y: 0 } }: LabelGeometry): {\n sourceRect: { x: number; y: number; width: number; height: number };\n scale: { x: number; y: number };\n} {\n const band = printableBand(tape, media);\n const dotsPerPt = media.dpi / 72;\n return {\n sourceRect: { x: origin.x, y: origin.y + band.y, width: length, height: band.height },\n scale: { x: dotsPerPt, y: dotsPerPt },\n };\n}\n\n/**\n * The printer's dot grid in scene units: one dot's size, and the corner of the dot that prints\n * first, at the top of the printable band. `protectRects` from `@weasel-js/dither` takes it to place\n * rects a dither must leave at a plain threshold, such as a barcode and its quiet zone.\n */\nexport function labelGrid({ tape, media, origin = { x: 0, y: 0 } }: Omit<LabelGeometry, 'length'>): OutputGrid {\n return { pitch: 72 / media.dpi, origin: { x: origin.x, y: origin.y + printableBand(tape, media).y } };\n}\n\n/** What {@link renderLabel} takes: the label's geometry, and what `renderSceneToPixels` takes besides its rect and scale. */\nexport type RenderLabelArgs<TData, TLayer extends string, TPose> = LabelGeometry &\n Omit<RenderSceneToPixelsArgs<TData, TLayer, TPose>, 'sourceRect' | 'scale'>;\n\n/**\n * Render a label to straight RGBA at the printer's resolution, on white unless `background` says\n * otherwise: the image `printLabel` takes. Loads the fonts and paint kinds the scene draws with first.\n */\nexport async function renderLabel<TData, TLayer extends string, TPose>(\n args: RenderLabelArgs<TData, TLayer, TPose>,\n): Promise<RasterImage> {\n const { length, tape, media, origin, ...rest } = args;\n const render = { background: '#ffffff', ...rest, ...labelRenderPlan({ length, tape, media, origin }) };\n await warmRender({ render });\n return renderSceneToPixels(render);\n}\n","/**\n * A rendered label to paper: dots, pages at the cut marks, and the job sent through obwat.\n */\nimport { ditherPixels, maskToRgba, type DitherOptions } from '@weasel-js/dither';\nimport {\n NoGrantedDeviceError,\n rgbaToRaster,\n type BrotherPrinter,\n type MediaSpec,\n type PrinterStatus,\n type Raster1bpp,\n type RgbaImage,\n} from 'obwat';\n\n/**\n * Split a raster into pages at `cutsPt`, positions along the label in points from its leading\n * edge. The printer cuts between pages when auto-cut is on. Marks at or past either end, and marks\n * landing on the same dot, are dropped; with none left the raster is one page.\n */\nexport function sliceAtCuts(raster: Raster1bpp, cutsPt: readonly number[], dpi: number): Raster1bpp[] {\n const cutDots = [...new Set(cutsPt.map((x) => Math.round(x * (dpi / 72))))]\n .filter((d) => d > 0 && d < raster.lineCount)\n .sort((a, b) => a - b);\n const bounds = [0, ...cutDots, raster.lineCount];\n const pages: Raster1bpp[] = [];\n for (let i = 0; i < bounds.length - 1; i++) {\n const rows = raster.rows.slice(bounds[i], bounds[i + 1]);\n pages.push({ lineBytes: raster.lineBytes, lineCount: rows.length, rows });\n }\n return pages;\n}\n\nconst INK = [0, 0, 0, 255] as const;\nconst BLANK = [255, 255, 255, 255] as const;\n\n/**\n * `image` as the raster a printer lays down: dithered by `@weasel-js/dither`, then packed. The dots\n * reach obwat as pure black on white, which its own threshold passes through unchanged.\n */\nexport function labelRaster(image: RgbaImage, media: MediaSpec, dither?: DitherOptions): Raster1bpp {\n const mask = ditherPixels(image, dither);\n const dots = maskToRgba(mask, image.width, image.height, { ink: INK, blank: BLANK });\n return rgbaToRaster({ width: image.width, height: image.height, data: dots }, media);\n}\n\n/** What {@link printLabel} sends, and how. */\nexport interface PrintLabelOptions {\n printer: BrotherPrinter;\n /** The label at one pixel per dot, `media.printableDots` tall: what `renderLabel` returns. */\n image: RgbaImage;\n media: MediaSpec;\n /** Cut after each page and at the end. Default true. */\n autoCut?: boolean;\n /** Where to cut along the label, in points: see {@link sliceAtCuts}. */\n cuts?: readonly number[];\n /** How gray becomes dots, as `ditherPixels` takes it; `labelGrid` places a `protect` mask's rects. Default a plain threshold. */\n dither?: DitherOptions;\n /**\n * When no printer has been granted yet, show the browser's device picker and print to what is\n * chosen, rather than rejecting with obwat's `NoGrantedDeviceError`. Only works inside a user\n * gesture. Default false.\n */\n requestDevice?: boolean;\n}\n\n/**\n * Print `image` as one job. Resolves with the printer's status reply, which reports what it found\n * as the job started (a missing tape, an open cover), not that it finished.\n */\nexport async function printLabel(opts: PrintLabelOptions): Promise<PrinterStatus> {\n const { printer, image, media, autoCut = true, cuts = [], dither, requestDevice = false } = opts;\n const pages = sliceAtCuts(labelRaster(image, media, dither), cuts, media.dpi);\n const job = { tapeWidthMm: media.tapeWidthMm, autoCut, marginDots: 0 };\n try {\n return await printer.print(pages, job);\n } catch (err) {\n if (!requestDevice || !(err instanceof NoGrantedDeviceError)) throw err;\n await printer.requestDevice();\n return await printer.print(pages, job);\n }\n}\n","/**\n * The paint, stroke and transform readings every mapped object shares. Each returns undefined for\n * what an `.lbx` object cannot state, and the command goes to the rasterizer instead.\n */\nimport type { GlMat3 } from '@weasel-js/geom';\nimport { isSolidPaint, parseColor, resolveScreenLength, type CommandFill, type CommandStroke } from '@weasel-js/paint';\n\n/** The transform and opacity a command draws under. */\nexport interface DrawState {\n transform: GlMat3;\n alpha: number;\n}\n\n/** An axis-aligned scale and offset with no flip, or undefined for a rotation, skew or mirror. */\nexport interface AxisMap {\n sx: number;\n sy: number;\n tx: number;\n ty: number;\n}\n\nconst EPS = 1e-6;\n\nexport function axisMap(m: GlMat3): AxisMap | undefined {\n if (Math.abs(m[1]!) > EPS || Math.abs(m[3]!) > EPS) return undefined;\n if (m[0]! <= 0 || m[4]! <= 0) return undefined;\n return { sx: m[0]!, sy: m[4]!, tx: m[6]!, ty: m[7]! };\n}\n\n/** One scale for both axes, which a stroke width or a font size needs. */\nexport function uniformScale(map: AxisMap): number | undefined {\n return Math.abs(map.sx - map.sy) <= EPS * Math.max(map.sx, map.sy) ? map.sx : undefined;\n}\n\nfunction hexByte(v: number): string {\n return Math.round(Math.min(1, Math.max(0, v)) * 255).toString(16).padStart(2, '0');\n}\n\n/** `#rrggbb` for a color painted fully opaque, or undefined: `.lbx` objects have no opacity. */\nexport function opaqueHex(color: string | readonly number[], opacity: number): string | undefined {\n const [r, g, b, a] = typeof color === 'string' ? parseColor(color) : color;\n if (Math.abs(a! * opacity - 1) > 1e-3) return undefined;\n return `#${hexByte(r!)}${hexByte(g!)}${hexByte(b!)}`;\n}\n\n/** A field's solid color: `null` for none, undefined for a paint `.lbx` cannot carry. */\ntype Solid = string | null | undefined;\n\nexport function fillColor(fill: CommandFill | undefined, alpha: number): Solid {\n if (fill?.paint === undefined) return null;\n if (!isSolidPaint(fill.paint)) return undefined;\n return opaqueHex(fill.paint.color, (fill.paint.opacity ?? 1) * alpha);\n}\n\nconst PLAIN_STROKE_KEYS = new Set(['paint', 'width', 'cap', 'join', 'miterLimit', 'align']);\n\n/** A solid stroke with nothing past a width, a cap and a join: its color and width in page units. */\nexport interface PlainStroke {\n color: string;\n width: number;\n cap: 'butt' | 'round' | 'square';\n join: 'miter' | 'round' | 'bevel';\n align: 'center' | 'inner' | 'outer';\n}\n\nexport function plainStroke(stroke: CommandStroke | undefined, alpha: number, scale: number): PlainStroke | null | undefined {\n if (stroke?.paint === undefined) return null;\n for (const [key, value] of Object.entries(stroke)) {\n if (value !== undefined && !PLAIN_STROKE_KEYS.has(key)) return undefined;\n }\n if (!isSolidPaint(stroke.paint)) return undefined;\n const color = opaqueHex(stroke.paint.color, (stroke.paint.opacity ?? 1) * alpha);\n if (color === undefined) return undefined;\n const width = resolveScreenLength(stroke.width ?? 1, scale) * scale;\n if (!(width > 0)) return null;\n return { color, width, cap: stroke.cap ?? 'butt', join: stroke.join ?? 'miter', align: stroke.align ?? 'center' };\n}\n","/**\n * `.lbx` objects carried through a scene's draw commands: how a node the writer would otherwise see\n * as paint (a barcode's bars) reaches the file as the object it is.\n */\nimport type { DrawCommand, GroupDrawCommand } from '@weasel-js/renderer';\nimport type { LabelObject } from 'bil-lbx';\nimport { axisMap, type AxisMap, type DrawState } from './style';\n\nconst CARRIED = Symbol('weasel.lbxObjects');\n\ntype Carrier = GroupDrawCommand & { [CARRIED]: readonly LabelObject[] };\n\n/**\n * A command that draws nothing and puts `objects` in the `.lbx` file where it stands, in its paint\n * order. Positions (and a line's points) are in the frame the command is drawn in, and the writer\n * carries them through the transforms above it; sizes inside an object, such as a font size or a\n * barcode's bar width, are written as given. Return it from the `drawOne` passed to `sceneToLbx` for\n * a node whose object the draw commands cannot say, as a barcode's bars do not say it is a barcode.\n */\nexport function lbxObjects(objects: readonly LabelObject[]): DrawCommand {\n const carrier: Carrier = { kind: 'group', children: [], [CARRIED]: objects };\n return carrier;\n}\n\n/** The objects `cmd` carries, or undefined when it is an ordinary command. */\nexport function carriedObjects(cmd: DrawCommand): readonly LabelObject[] | undefined {\n return (cmd as Partial<Carrier>)[CARRIED];\n}\n\nfunction placeObject(obj: LabelObject, map: AxisMap): LabelObject {\n const { x, y, width, height } = obj.position;\n const position = { ...obj.position, x: x * map.sx + map.tx, y: y * map.sy + map.ty, width: width * map.sx, height: height * map.sy };\n if (obj.type !== 'line') return { ...obj, position };\n const points = obj.points.map((p) => ({ x: p.x * map.sx + map.tx, y: p.y * map.sy + map.ty }));\n return { ...obj, position, points };\n}\n\n/** `objects` in the page's frame under `state`, or undefined under a rotation, skew or mirror. */\nexport function placeCarried(objects: readonly LabelObject[], state: DrawState): LabelObject[] | undefined {\n const map = axisMap(state.transform);\n return map && objects.map((obj) => placeObject(obj, map));\n}\n","/**\n * Draw commands as native `.lbx` objects: rectangles, open polylines, plain text boxes. Each mapper\n * returns undefined for a command it cannot state exactly, and the writer rasterizes that one.\n */\nimport { mat3, PATH_CMD_LENGTHS, PATH_L, PATH_M, type PolygonPath } from '@weasel-js/geom';\nimport type { PathDrawCommand, TextDrawCommand } from '@weasel-js/renderer';\nimport type { ArrowStyle, HorizontalAlignment, LineObject, RectObject, TextObject, VerticalAlignment } from 'bil-lbx';\nimport { axisMap, fillColor, opaqueHex, plainStroke, uniformScale, type DrawState } from './style';\n\nconst NO_PEN = { style: 'NULL' as const };\nconst NO_BRUSH = { style: 'NULL' as const };\n\n/** An axis-aligned rect path, filled and stroked in solid colors, as a `draw:rect`. `[]` draws nothing. */\nexport function rectObject(cmd: PathDrawCommand, state: DrawState): RectObject[] | undefined {\n if (cmd.path.kind !== 'rect' || cmd.vertexColors) return undefined;\n const map = axisMap(state.transform);\n if (!map) return undefined;\n const fill = fillColor(cmd.fill, state.alpha);\n if (fill === undefined) return undefined;\n const scale = cmd.stroke?.paint === undefined ? map.sx : uniformScale(map);\n if (scale === undefined) return undefined;\n const stroke = plainStroke(cmd.stroke, state.alpha, scale);\n if (stroke === undefined || (stroke && stroke.join !== 'miter')) return undefined;\n if (fill === null && stroke === null) return [];\n const { x, y, width, height } = cmd.path;\n // P-touch draws the pen inside the object's frame, so the frame reaches the stroke's outer edge.\n const grow = stroke === null ? 0 : stroke.align === 'center' ? stroke.width / 2 : stroke.align === 'outer' ? stroke.width : 0;\n return [{\n type: 'rect',\n shape: 'RECTANGLE',\n position: {\n x: x * map.sx + map.tx - grow,\n y: y * map.sy + map.ty - grow,\n width: width * map.sx + grow * 2,\n height: height * map.sy + grow * 2,\n },\n pen: stroke === null ? NO_PEN : { style: 'INSIDEFRAME', widthX: stroke.width, widthY: stroke.width, color: stroke.color },\n brush: fill === null ? NO_BRUSH : { style: 'SOLID', color: fill },\n }];\n}\n\n/** The points of a path made of one `M` and its `L`s, or undefined for curves, closes or more subpaths. */\nfunction polylinePoints(path: PolygonPath): { x: number; y: number }[] | undefined {\n if (path.corners?.some((c) => c !== null)) return undefined;\n const ox = path.origin?.x ?? 0;\n const oy = path.origin?.y ?? 0;\n const points: { x: number; y: number }[] = [];\n let at = 0;\n for (let i = 0; i < path.commands.length; i++) {\n const code = path.commands[i]!;\n if (code !== (i === 0 ? PATH_M : PATH_L)) return undefined;\n points.push({ x: path.coords[at]! + ox, y: path.coords[at + 1]! + oy });\n at += PATH_CMD_LENGTHS[code]!;\n }\n return points.length >= 2 ? points : undefined;\n}\n\nconst ARROW_FOR_CAP: Record<'butt' | 'round' | 'square', ArrowStyle> = { butt: 'SQUARE', round: 'ROUND', square: 'SQUARE' };\n\n/** An open polyline stroked in a solid color, unfilled, as a `draw:poly`. */\nexport function lineObject(cmd: PathDrawCommand, state: DrawState): LineObject | undefined {\n if (cmd.path.kind !== 'polygon' || cmd.vertexColors || cmd.fill?.paint !== undefined) return undefined;\n const map = axisMap(state.transform);\n const scale = map && uniformScale(map);\n if (!map || scale === undefined) return undefined;\n const local = polylinePoints(cmd.path);\n const stroke = plainStroke(cmd.stroke, state.alpha, scale);\n if (!local || !stroke || stroke.align !== 'center') return undefined;\n const points = local.map((p) => {\n const [x, y] = mat3.apply(state.transform, p.x, p.y);\n return { x, y };\n });\n const xs = points.map((p) => p.x);\n const ys = points.map((p) => p.y);\n const x = Math.min(...xs);\n const y = Math.min(...ys);\n const arrow = ARROW_FOR_CAP[stroke.cap];\n return {\n type: 'line',\n position: { x, y, width: Math.max(...xs) - x, height: Math.max(...ys) - y },\n points,\n arrowBegin: arrow,\n arrowEnd: arrow,\n pen: { style: 'SOLID', widthX: stroke.width, widthY: stroke.width, color: stroke.color },\n brush: NO_BRUSH,\n };\n}\n\nconst H_ALIGN: Record<string, HorizontalAlignment> = { left: 'LEFT', start: 'LEFT', center: 'CENTER', right: 'RIGHT', end: 'RIGHT', justify: 'JUSTIFY' };\nconst V_ALIGN: Record<string, VerticalAlignment> = { top: 'TOP', center: 'CENTER', bottom: 'BOTTOM' };\n\n/**\n * Text in one face, size and solid color, laid in a box of known size, as a `text:text`. P-touch\n * sets it again with its own copy of the face, so the lines can break where the canvas did not.\n */\nexport function textObject(cmd: TextDrawCommand, state: DrawState): TextObject | undefined {\n const map = axisMap(state.transform);\n const scale = map && uniformScale(map);\n const boxWidth = cmd.width ?? cmd.maxWidth;\n if (!map || scale === undefined || boxWidth === undefined || cmd.height === undefined) return undefined;\n if (cmd.direction === 'rtl' || cmd.runs.length === 0) return undefined;\n const [first] = cmd.runs;\n let color: string | undefined;\n for (const run of cmd.runs) {\n if (run.fontFamily !== first!.fontFamily || run.fontSize !== first!.fontSize) return undefined;\n if (run.fontWeight !== first!.fontWeight || run.fontStyle !== first!.fontStyle) return undefined;\n if (run.underline !== first!.underline || run.strikethrough !== first!.strikethrough) return undefined;\n if (run.stroke?.paint !== undefined || run.overline || run.sizeMap || run.rotate) return undefined;\n if (run.letterSpacing !== 0 || run.baselineShift !== 0) return undefined;\n const paint = run.fill?.paint;\n if (!paint || (paint.kind !== undefined && paint.kind !== 'solid')) return undefined;\n const hex = opaqueHex(paint.color, (paint.opacity ?? 1) * state.alpha);\n if (hex === undefined || (color !== undefined && hex !== color)) return undefined;\n color = hex;\n }\n const align = cmd.justify ? 'justify' : (cmd.align ?? cmd.style.align ?? 'left');\n return {\n type: 'text',\n position: { x: cmd.x * map.sx + map.tx, y: cmd.y * map.sy + map.ty, width: boxWidth * scale, height: cmd.height * scale },\n font: { name: first!.fontFamily, size: first!.fontSize * scale, weight: first!.fontWeight, italic: first!.fontStyle === 'italic' },\n textStyle: { color, underline: first!.underline ? 1 : 0, strikeout: first!.strikethrough ? 1 : 0 },\n control: 'FIXEDFRAME',\n shrink: false,\n autoLF: cmd.maxWidth !== undefined,\n horizontalAlignment: H_ALIGN[align] ?? 'LEFT',\n verticalAlignment: V_ALIGN[cmd.verticalAlign ?? 'top'] ?? 'TOP',\n data: cmd.runs.map((run) => run.text).join(''),\n };\n}\n","/**\n * A label's draw commands as a P-touch `.lbx` file.\n */\nimport type { CommandRasterizer, RasterImage } from '@weasel-js/core';\nimport { mat3, type GlMat3 } from '@weasel-js/geom';\nimport type { DrawCommand, GroupDrawCommand } from '@weasel-js/renderer';\nimport {\n AUTO_LENGTH_MAX_PT,\n backgroundFor,\n buildLbx,\n encodeBmp32,\n type ImageObject,\n type LabelConfig,\n type LabelObject,\n} from 'bil-lbx';\nimport type { Tape } from '../tape';\nimport { carriedObjects, placeCarried } from './carried';\nimport { lineObject, rectObject, textObject } from './shapes';\nimport type { DrawState } from './style';\n\n/** One label: its size and the commands painted on it, in points with the origin at its top left. */\nexport interface LbxPage {\n /** Length along the tape, in points. */\n length: number;\n tape: Tape;\n commands: readonly DrawCommand[];\n /** What a `world`-unit paint in `commands` resolves through, as `PdfPage.view`. */\n view?: GlMat3;\n /** Let P-touch size the label to what is on it. `length` is still written as the fitted length. */\n autoLength?: boolean;\n /** Where the printer cuts, in points along the label: a strip of labels in one file. */\n cuts?: readonly number[];\n}\n\n/** Settings and the host callback for {@link writeLbx}. */\nexport interface WriteLbxOptions {\n /** Draws what an `.lbx` object cannot state; `commandRasterizer` from `@weasel-js/core` is one. Without it those commands are skipped. */\n rasterize?: CommandRasterizer;\n /** Pixels per inch for rasterized content. Default 180, the PT-P710BT's. */\n rasterDpi?: number;\n}\n\n/** A command the writer left out of the file, with the reason. */\nexport interface LbxSkip {\n kind: DrawCommand['kind'];\n detail: string;\n}\n\n/** The finished file, the label it describes, and what it had to leave out. */\nexport interface LbxResult {\n bytes: Uint8Array;\n config: LabelConfig;\n skipped: readonly LbxSkip[];\n}\n\ntype Item = { object: LabelObject } | { raster: DrawCommand };\n\nfunction plainGroup(g: GroupDrawCommand): boolean {\n const composite = g.composite ?? 'source-over';\n return !g.clip && !g.effects?.length && !g.isolate && !g.colorMatrix && composite === 'source-over';\n}\n\n/** `cmd` under `state`, as a command drawn in the page's frame. */\nfunction placed(cmd: DrawCommand, state: DrawState): DrawCommand {\n return { kind: 'group', transform: state.transform, alpha: state.alpha, children: [cmd] };\n}\n\nfunction collect(commands: readonly DrawCommand[], state: DrawState, out: Item[], skipped: LbxSkip[]): void {\n for (const cmd of commands) {\n const carried = carriedObjects(cmd);\n if (carried) {\n const placed = placeCarried(carried, state);\n if (placed) for (const object of placed) out.push({ object });\n else skipped.push({ kind: 'group', detail: 'lbxObjects under a rotation, skew or mirror' });\n continue;\n }\n if (cmd.kind === 'group' && plainGroup(cmd)) {\n const transform = cmd.transform ? mat3.multiply(Float64Array.from(state.transform), cmd.transform) : state.transform;\n collect(cmd.children, { transform, alpha: state.alpha * (cmd.alpha ?? 1) }, out, skipped);\n continue;\n }\n const mapped =\n cmd.kind === 'path' ? (rectObject(cmd, state) ?? lineObject(cmd, state)) :\n cmd.kind === 'text' ? textObject(cmd, state) :\n undefined;\n if (mapped === undefined) out.push({ raster: placed(cmd, state) });\n else for (const object of Array.isArray(mapped) ? mapped : [mapped]) out.push({ object });\n }\n}\n\n/** The smallest box holding every pixel with any alpha, or undefined when none has. */\nfunction inkBounds(img: RasterImage): { x: number; y: number; width: number; height: number } | undefined {\n let x0 = img.width, y0 = img.height, x1 = -1, y1 = -1;\n for (let y = 0; y < img.height; y++) {\n for (let x = 0; x < img.width; x++) {\n if (img.data[(y * img.width + x) * 4 + 3] === 0) continue;\n if (x < x0) x0 = x;\n if (x > x1) x1 = x;\n if (y < y0) y0 = y;\n if (y > y1) y1 = y;\n }\n }\n return x1 < 0 ? undefined : { x: x0, y: y0, width: x1 - x0 + 1, height: y1 - y0 + 1 };\n}\n\nfunction crop(img: RasterImage, box: { x: number; y: number; width: number; height: number }): Uint8ClampedArray {\n const out = new Uint8ClampedArray(box.width * box.height * 4);\n for (let y = 0; y < box.height; y++) {\n const from = ((box.y + y) * img.width + box.x) * 4;\n out.set(img.data.subarray(from, from + box.width * 4), y * box.width * 4);\n }\n return out;\n}\n\nasync function rasterObject(\n commands: DrawCommand[],\n page: LbxPage,\n rasterize: CommandRasterizer,\n dpi: number,\n): Promise<ImageObject | undefined> {\n const scale = dpi / 72;\n const img = await rasterize(commands, { x: 0, y: 0, width: page.length, height: page.tape.widthPt }, scale, page.view);\n const box = inkBounds(img);\n if (!box) return undefined;\n return {\n type: 'image',\n position: { x: box.x / scale, y: box.y / scale, width: box.width / scale, height: box.height / scale },\n imageData: encodeBmp32(crop(img, box), box.width, box.height),\n };\n}\n\n/** Each run of commands an object cannot state, drawn as one image, in paint order with the objects around it. */\nasync function settle(items: Item[], page: LbxPage, opts: WriteLbxOptions, skipped: LbxSkip[]): Promise<LabelObject[]> {\n const objects: LabelObject[] = [];\n let run: DrawCommand[] = [];\n const flush = async () => {\n if (run.length === 0) return;\n if (opts.rasterize) {\n const image = await rasterObject(run, page, opts.rasterize, opts.rasterDpi ?? 180);\n if (image) objects.push(image);\n } else {\n for (const cmd of run) skipped.push({ kind: innermost(cmd).kind, detail: 'no rasterize hook for what .lbx cannot draw' });\n }\n run = [];\n };\n for (const item of items) {\n if ('raster' in item) run.push(item.raster);\n else {\n await flush();\n objects.push(item.object);\n }\n }\n await flush();\n return objects;\n}\n\nfunction innermost(cmd: DrawCommand): DrawCommand {\n return cmd.kind === 'group' && cmd.children.length === 1 ? innermost(cmd.children[0]!) : cmd;\n}\n\n/** The `.lbx` label `page` describes, without the file: its paper, cuts and objects. */\nexport async function lbxConfig(page: LbxPage, opts: WriteLbxOptions = {}): Promise<{ config: LabelConfig; skipped: LbxSkip[] }> {\n const items: Item[] = [];\n const skipped: LbxSkip[] = [];\n collect(page.commands, { transform: mat3.identity(), alpha: 1 }, items, skipped);\n const objects = await settle(items, page, opts, skipped);\n const paper = {\n width: page.tape.widthPt,\n format: page.tape.format,\n autoLength: page.autoLength ?? false,\n // Under auto length P-touch parks its 1000mm ceiling here; the length lives in the background band.\n height: page.autoLength ? AUTO_LENGTH_MAX_PT : page.length,\n };\n const cuts = page.cuts?.filter((x) => x > 0 && x < page.length) ?? [];\n const config: LabelConfig = {\n paper,\n background: backgroundFor(paper, page.length),\n objects,\n ...(cuts.length > 0 ? { cut: { freeCut: [...cuts].sort((a, b) => a - b) } } : {}),\n };\n return { config, skipped };\n}\n\n/**\n * Write `page` as an `.lbx` file. Axis-aligned rects, open polylines and plain text boxes become\n * objects P-touch Editor can edit; everything else is drawn through `rasterize` and placed as an\n * image at `rasterDpi`. Objects keep the commands' paint order.\n */\nexport async function writeLbx(page: LbxPage, opts: WriteLbxOptions = {}): Promise<LbxResult> {\n const { config, skipped } = await lbxConfig(page, opts);\n return { bytes: await buildLbx(config), config, skipped };\n}\n","/**\n * A label's scene as an `.lbx` file.\n */\nimport {\n commandRasterizer,\n createRasterSession,\n planPixelRender,\n viewToMat3,\n warmRender,\n type RenderSceneToPixelsArgs,\n} from '@weasel-js/core';\nimport type { Tape } from '../tape';\nimport { writeLbx, type LbxResult, type WriteLbxOptions } from './writeLbx';\n\n/** What {@link sceneToLbx} takes: the label's place in its scene, and what `renderSceneToPixels` takes besides its rect and scale. */\nexport type SceneToLbxArgs<TData, TLayer extends string, TPose> = Omit<\n RenderSceneToPixelsArgs<TData, TLayer, TPose>,\n 'sourceRect' | 'scale' | 'background' | 'gl' | 'createCanvas'\n> & {\n /** Length along the tape, in points: the label's width in the scene. */\n length: number;\n tape: Tape;\n /** The label's top-left corner in the scene. Default `(0, 0)`. */\n origin?: { x: number; y: number };\n autoLength?: boolean;\n cuts?: readonly number[];\n rasterDpi?: WriteLbxOptions['rasterDpi'];\n};\n\n/**\n * Write the label at `origin` in `scene` as an `.lbx` file, drawn the way the canvas draws it\n * (`drawOne` included). What the file cannot state as an object is drawn through a raster session\n * of its own and placed as an image, so this needs WebGL2 where the scene has any.\n */\nexport async function sceneToLbx<TData, TLayer extends string, TPose>(\n args: SceneToLbxArgs<TData, TLayer, TPose>,\n): Promise<LbxResult> {\n const { length, tape, origin = { x: 0, y: 0 }, autoLength, cuts, rasterDpi, ...render } = args;\n const plan = { ...render, sourceRect: { x: origin.x, y: origin.y, width: length, height: tape.widthPt }, scale: { x: 1, y: 1 } };\n await warmRender({ render: plan });\n const { commands, view } = planPixelRender(plan);\n const session = createRasterSession();\n try {\n return await writeLbx(\n { length, tape, commands, view: viewToMat3(view), autoLength, cuts },\n { rasterize: commandRasterizer(session), rasterDpi },\n );\n } finally {\n session.dispose();\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weasel-js/p-touch",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.16.1",
|
|
4
|
+
"description": "Brother P-touch labels from weasel scenes: .lbx files written from draw commands, and printing over WebUSB or Web Serial.",
|
|
5
|
+
"weasel": {
|
|
6
|
+
"tier": "other"
|
|
7
|
+
},
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"sideEffects": false,
|
|
11
|
+
"main": "./dist/index.js",
|
|
12
|
+
"module": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts"
|
|
18
|
+
},
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
21
|
+
"author": "orochi235",
|
|
22
|
+
"homepage": "https://orochi235.github.io/weasel/",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/orochi235/weasel.git",
|
|
26
|
+
"directory": "packages/p-touch"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/orochi235/weasel/issues"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=24"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"README.md",
|
|
37
|
+
"LICENSE"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsup"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@weasel-js/dither": "1.16.1",
|
|
44
|
+
"@weasel-js/geom": "1.16.1",
|
|
45
|
+
"@weasel-js/paint": "1.16.1",
|
|
46
|
+
"@weasel-js/renderer": "1.16.1",
|
|
47
|
+
"@weasel-js/text": "1.16.1",
|
|
48
|
+
"bil-lbx": "^0.3.1",
|
|
49
|
+
"obwat": "^0.3.1"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"@weasel-js/core": "1.16.1"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public",
|
|
56
|
+
"provenance": true
|
|
57
|
+
}
|
|
58
|
+
}
|