@richardmcquiston01/unofficial-xcs-writer 0.8.1 → 0.8.2
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/API.md +32 -0
- package/CHANGELOG.md +43 -0
- package/README.md +110 -104
- package/package.json +1 -1
package/API.md
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
# API Reference
|
|
2
2
|
|
|
3
|
+
[Back to README](./README.md)
|
|
4
|
+
|
|
5
|
+
| [`assertXcsFormat(buffer: ArrayBuffer): void`](#assertxcsformatbuffer-arraybuffer-void) | [`readXcsFile(buffer: ArrayBuffer): string`](#readxcsfilebuffer-arraybuffer-string) | [`extractXcsTokens(buffer: ArrayBuffer): string[]`](#extractxcstokensbuffer-arraybuffer-string) | [`renderXcsFile(buffer, variables, values, fonts?): Uint8Array`](#renderxcsfilebuffer-variables-values-fonts-uint8array) | [`XcsVariable`](#xcsvariable) | [`.xs` (v2 workspace) format](#xs-v2-workspace-format) | [`assertXsFormat(buffer: ArrayBuffer): void`](#assertxsformatbuffer-arraybuffer-void) | [`extractXsTokens(buffer: ArrayBuffer): string[]`](#extractxstokensbuffer-arraybuffer-string) | [`renderXsFile(buffer, variables, values, fonts?): Uint8Array`](#renderxsfilebuffer-variables-values-fonts-uint8array) | [`buildXsArchive(file: XCSFile): Uint8Array`](#buildxsarchivefile-xcsfile-uint8array) | [`loadFont(buffer: ArrayBuffer)` / `loadDefaultFont()` / `layoutText(font, text, originX, originY)`](#loadfontbuffer-arraybuffer--loaddefaultfont--layouttextfont-text-originx-originy) | [Building (`src/builder.ts`, `src/layout.ts`, `src/machines.ts`)](#building-srcbuilderts-srclayoutts-srcmachinests) |
|
|
6
|
+
|
|
3
7
|
## `assertXcsFormat(buffer: ArrayBuffer): void`
|
|
4
8
|
|
|
5
9
|
Validates that `buffer` is a parseable XCS JSON file containing a `canvas` array. Throws a descriptive error if the file is invalid or unrecognized.
|
|
6
10
|
|
|
11
|
+
[Back to Top](#api-reference)
|
|
12
|
+
|
|
7
13
|
## `readXcsFile(buffer: ArrayBuffer): string`
|
|
8
14
|
|
|
9
15
|
Decodes the buffer and returns the raw JSON string. Useful for inspection or pass-through scenarios.
|
|
10
16
|
|
|
17
|
+
[Back to Top](#api-reference)
|
|
18
|
+
|
|
11
19
|
## `extractXcsTokens(buffer: ArrayBuffer): string[]`
|
|
12
20
|
|
|
13
21
|
Scans all `TEXT` display objects across all canvases and returns the unique token names found (without braces). For example, `{{LastName}}` in the file → `"LastName"` in the returned array.
|
|
14
22
|
|
|
23
|
+
[Back to Top](#api-reference)
|
|
24
|
+
|
|
15
25
|
## `renderXcsFile(buffer, variables, values, fonts?): Uint8Array`
|
|
16
26
|
|
|
17
27
|
Applies substitution to every `TEXT` display object and returns the modified file encoded as a UTF-8 `Uint8Array`. Tokens with no matching entry in `values` fall back to `variable.defaultValue`, or an empty string if no default is set.
|
|
@@ -29,6 +39,8 @@ Any `fontFamily` not present in `fonts` falls back to a bundled default (Arimo,
|
|
|
29
39
|
|
|
30
40
|
**Known limitation:** `renderXcsFile` only reproduces straight horizontal-baseline layout when substituting into an *existing* TEXT display. xTool Studio's `style.curveX`/`curveY` curved-text formula is undocumented and can't be reverse-decoded from a file alone, so a curved display's substituted text gets correctly-shaped glyphs laid out straight instead of on the original curve. This doesn't apply to the *building* API below — there, you supply the curve angle yourself (see `layoutCurvedGlyphText`), so there's nothing to decode.
|
|
31
41
|
|
|
42
|
+
[Back to Top](#api-reference)
|
|
43
|
+
|
|
32
44
|
## `XcsVariable`
|
|
33
45
|
|
|
34
46
|
```ts
|
|
@@ -38,28 +50,42 @@ interface XcsVariable {
|
|
|
38
50
|
}
|
|
39
51
|
```
|
|
40
52
|
|
|
53
|
+
[Back to Top](#api-reference)
|
|
54
|
+
|
|
41
55
|
## `.xs` (v2 workspace) format
|
|
42
56
|
|
|
57
|
+
[Back to Top](#api-reference)
|
|
58
|
+
|
|
43
59
|
## `assertXsFormat(buffer: ArrayBuffer): void`
|
|
44
60
|
|
|
45
61
|
Validates that `buffer` is a ZIP archive with a `.format` entry equal to `"v2"` and a `project.json` entry. Throws a descriptive error otherwise.
|
|
46
62
|
|
|
63
|
+
[Back to Top](#api-reference)
|
|
64
|
+
|
|
47
65
|
## `extractXsTokens(buffer: ArrayBuffer): string[]`
|
|
48
66
|
|
|
49
67
|
Same as `extractXcsTokens`, but scans every `canvases/<canvasId>/displays-<chunkIndex>.json` chunk across the archive instead of a single `canvas` array.
|
|
50
68
|
|
|
69
|
+
[Back to Top](#api-reference)
|
|
70
|
+
|
|
51
71
|
## `renderXsFile(buffer, variables, values, fonts?): Uint8Array`
|
|
52
72
|
|
|
53
73
|
Same substitution and glyph-regeneration behavior as `renderXcsFile` (see above — including the same `fonts` parameter and curved-text limitation), applied to every displays chunk. Returns a re-zipped `.xs` archive; every entry other than the modified displays chunks is passed through byte-for-byte unchanged.
|
|
54
74
|
|
|
75
|
+
[Back to Top](#api-reference)
|
|
76
|
+
|
|
55
77
|
## `buildXsArchive(file: XCSFile): Uint8Array`
|
|
56
78
|
|
|
57
79
|
Packages an already-`generate()`d project (see Building, below) into a `.xs` v2 ZIP archive. You won't normally call this directly — use `XCSGenerator.toXsBytes()` instead, which calls it for you.
|
|
58
80
|
|
|
81
|
+
[Back to Top](#api-reference)
|
|
82
|
+
|
|
59
83
|
## `loadFont(buffer: ArrayBuffer)` / `loadDefaultFont()` / `layoutText(font, text, originX, originY)`
|
|
60
84
|
|
|
61
85
|
Lower-level glyph-extraction primitives everything else in this library is built on, exported for direct use. See `src/glyphs.ts` for the exact xTool JSON conventions these were reverse-engineered against.
|
|
62
86
|
|
|
87
|
+
[Back to Top](#api-reference)
|
|
88
|
+
|
|
63
89
|
## Building (`src/builder.ts`, `src/layout.ts`, `src/machines.ts`)
|
|
64
90
|
|
|
65
91
|
`createXCS(deviceId?)` / `new XCSGenerator(options?)` — starts a new project (one canvas, one default layer). `deviceId` (and `options.deviceId`) accepts a `XTOOL_MACHINES` key, a `MachineProfile` object, or a raw device id string — see "Building a project from scratch" in the [README](./README.md). Chainable methods:
|
|
@@ -84,3 +110,9 @@ Text layout — build the `layout` option for `.addText()` with one of:
|
|
|
84
110
|
| `layoutCurvedGlyphText(font, text, emSizeMm, curveDeg, boxWidthMm, boxHeightMm, align?, letterSpacingMm?)` | Single-line, curved along an arc spanning `curveDeg` degrees across a `boxWidthMm`-wide box (positive arches the apex upward, negative bows it downward — same convention as SVG `<textPath>`/canvas curved-text editors). Multi-line curved text isn't supported (matches typical curved-text editors, which also flatten multi-line curved text to one line). **Not yet visually verified against real xTool Studio rendering** (there's no way to check outside the application itself) — the geometry is self-consistent and unit-tested, but treat curved output as best-effort until you've spot-checked it. |
|
|
85
111
|
|
|
86
112
|
All three return `GlyphTextLayout | null` (`null` for empty/whitespace-only text). `translateGlyphLayout(layout, dxMm, dyMm)` cheaply shifts an already-built layout without relaying out the font.
|
|
113
|
+
|
|
114
|
+
[Back to Top](#api-reference)
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
[Back to README](./README.md)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
[Back to README](./README.md)
|
|
4
|
+
|
|
5
|
+
| [Unreleased](#unreleased) | [0.8.2 - 2026-10-13](#082---2026-10-13) | [0.8.1 - 2026-09-21](#081---2026-09-21) | [0.8.0 - 2026-09-21](#080---2026-09-21) | [0.7.0 - 2026-09-21](#070---2026-09-21) | [0.6.0 - 2026-09-21](#060---2026-09-21) | [0.5.0 - 2026-09-20](#050---2026-09-20) | [0.4.1 - 2026-09-19](#041---2026-09-19) | [0.4.0 - 2026-09-19](#040---2026-09-19) | [0.3.1 - 2026-08-17](#031---2026-08-17) | [0.3.0 - 2026-07-14](#030---2026-07-14) | [0.2.0 - 2026-07-14](#020---2026-07-14) | [0.1.0 - 2026-05-19](#010---2026-05-19) |
|
|
6
|
+
|
|
3
7
|
All notable changes to this project will be documented in this file.
|
|
4
8
|
|
|
5
9
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
@@ -7,6 +11,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
11
|
|
|
8
12
|
## [Unreleased]
|
|
9
13
|
|
|
14
|
+
[Back to Top](#changelog)
|
|
15
|
+
|
|
16
|
+
## [0.8.2] - 2026-10-13
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **Documentation only**: no library code changed. Rewrote `README.md` to be shorter, with an
|
|
21
|
+
author block, a Table of Contents, Back to Top links, a Supported Machines table, and the
|
|
22
|
+
License section corrected to MIT. Added a Back to README link, a menu row, and Back to Top
|
|
23
|
+
links to `API.md` and `CHANGELOG.md`.
|
|
24
|
+
|
|
25
|
+
[Back to Top](#changelog)
|
|
26
|
+
|
|
10
27
|
## [0.8.1] - 2026-09-21
|
|
11
28
|
|
|
12
29
|
### Changed
|
|
@@ -22,6 +39,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
22
39
|
registered trademarks of Makeblock Co., Ltd., and that this project isn't affiliated with,
|
|
23
40
|
endorsed by, or sponsored by Makeblock or xTool.
|
|
24
41
|
|
|
42
|
+
[Back to Top](#changelog)
|
|
43
|
+
|
|
25
44
|
## [0.8.0] - 2026-09-21
|
|
26
45
|
|
|
27
46
|
### Added
|
|
@@ -42,6 +61,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
42
61
|
(`file.device.id`, i.e. `"P2S"`). `.xcs` output (`.toBytes()`) is unaffected -- it has no
|
|
43
62
|
`deviceCode` field.
|
|
44
63
|
|
|
64
|
+
[Back to Top](#changelog)
|
|
65
|
+
|
|
45
66
|
## [0.7.0] - 2026-09-21
|
|
46
67
|
|
|
47
68
|
### Added
|
|
@@ -56,6 +77,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
56
77
|
is structurally different and much less understood, so it isn't implemented (see `CLAUDE.md`'s
|
|
57
78
|
Open Issues).
|
|
58
79
|
|
|
80
|
+
[Back to Top](#changelog)
|
|
81
|
+
|
|
59
82
|
## [0.6.0] - 2026-09-21
|
|
60
83
|
|
|
61
84
|
### Added
|
|
@@ -70,6 +93,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
70
93
|
generated `.xs` file's power/speed settings may need to be configured manually in xTool
|
|
71
94
|
Studio before cutting/engraving.
|
|
72
95
|
|
|
96
|
+
[Back to Top](#changelog)
|
|
97
|
+
|
|
73
98
|
## [0.5.0] - 2026-09-20
|
|
74
99
|
|
|
75
100
|
### Added
|
|
@@ -87,6 +112,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
87
112
|
vector store. `.xs` *generation* (building a new project from scratch) isn't implemented yet
|
|
88
113
|
— see `CLAUDE.md`'s "Open Issues".
|
|
89
114
|
|
|
115
|
+
[Back to Top](#changelog)
|
|
116
|
+
|
|
90
117
|
## [0.4.1] - 2026-09-19
|
|
91
118
|
|
|
92
119
|
### Changed
|
|
@@ -97,6 +124,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
97
124
|
from 0.4.0; this is the first published release carrying the generation API
|
|
98
125
|
and curved-text layout described below.
|
|
99
126
|
|
|
127
|
+
[Back to Top](#changelog)
|
|
128
|
+
|
|
100
129
|
## [0.4.0] - 2026-09-19
|
|
101
130
|
|
|
102
131
|
### Added
|
|
@@ -124,6 +153,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
124
153
|
still can't decode an existing curved display's `style.curveX`/`curveY`, but the new building
|
|
125
154
|
API doesn't have that problem (the caller supplies the angle directly).
|
|
126
155
|
|
|
156
|
+
[Back to Top](#changelog)
|
|
157
|
+
|
|
127
158
|
## [0.3.1] - 2026-08-17
|
|
128
159
|
|
|
129
160
|
### Changed
|
|
@@ -134,6 +165,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
134
165
|
- `donate.svg` is included in the published tarball so the README's QR image
|
|
135
166
|
resolves for consumers reading it from the package.
|
|
136
167
|
|
|
168
|
+
[Back to Top](#changelog)
|
|
169
|
+
|
|
137
170
|
## [0.3.0] - 2026-07-14
|
|
138
171
|
|
|
139
172
|
### Added
|
|
@@ -149,6 +182,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
149
182
|
0.2.0 tarball on npm was packed from a stale `dist/` and does not actually
|
|
150
183
|
contain the glyph regeneration code its README describes — use 0.3.0 instead.
|
|
151
184
|
|
|
185
|
+
[Back to Top](#changelog)
|
|
186
|
+
|
|
152
187
|
## [0.2.0] - 2026-07-14
|
|
153
188
|
|
|
154
189
|
> **Warning:** the npm tarball for this version was built from a stale `dist/`
|
|
@@ -174,6 +209,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
174
209
|
substituted text on a curved TEXT display gets correctly-shaped glyphs laid
|
|
175
210
|
out on a straight baseline.
|
|
176
211
|
|
|
212
|
+
[Back to Top](#changelog)
|
|
213
|
+
|
|
177
214
|
## [0.1.0] - 2026-05-19
|
|
178
215
|
|
|
179
216
|
### Added
|
|
@@ -183,3 +220,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
183
220
|
- Core API: `assertXcsFormat`, `readXcsFile`, `extractXcsTokens`,
|
|
184
221
|
`renderXcsFile`, and the `XcsVariable` type.
|
|
185
222
|
- Dual ESM + CJS build with TypeScript declarations via tsup.
|
|
223
|
+
|
|
224
|
+
[Back to Top](#changelog)
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
[Back to README](./README.md)
|
package/README.md
CHANGED
|
@@ -1,23 +1,43 @@
|
|
|
1
1
|
# unofficial-xcs-writer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
- Author: [Richard McQuiston](mailto:richard.mcquiston@gmail.com)
|
|
4
|
+
- Website: https://richardmcquiston.com/
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
> **Disclaimer:** This is an unofficial, community-built library. It is not affiliated with, endorsed by, or sponsored by xTool or Makeblock.
|
|
6
7
|
|
|
7
|
-
##
|
|
8
|
+
## Table of Contents
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
| [Overview](#overview) | [Features](#features) | [Getting Started](#getting-started) | [Supported Machines](#supported-machines) | [Known Limitations](#known-limitations) | [More Information](#more-information) | [Development](#development) | [Buy Me a Coffee](#buy-me-a-coffee) | [License](#license) | [Copyright](#copyright) |
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
## Overview
|
|
12
13
|
|
|
13
|
-
This library
|
|
14
|
+
This is a TypeScript library for xTool project files. It reads, builds, and fills in templates for `.xcs` and `.xs` files.
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
- **Extract** `{{token}}` template placeholders from text objects
|
|
17
|
-
- **Render** a filled-in copy by substituting values for those placeholders
|
|
18
|
-
- **Build** a new project from scratch — text (straight, multi-line, or curved), paths, and embedded images — and export it as either `.xcs` or `.xs`
|
|
16
|
+
An `.xcs` file is plain JSON. An `.xs` file is a ZIP archive of JSON files. xTool Studio 1.7 and newer saves `.xs` by default. It can still open `.xcs` files but can no longer save them.
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
The library uses no framework, no DOM, and no Node.js-only features. It works in browsers and in Node.js.
|
|
19
|
+
|
|
20
|
+
[Back to Top](#table-of-contents)
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- Find `{{token}}` placeholders in text objects.
|
|
25
|
+
- Fill in the placeholders with your own values. The library draws new glyph outlines for the changed text.
|
|
26
|
+
- Build a project from scratch. You can add straight, multi-line, or curved text, paths, and images.
|
|
27
|
+
- Save the project as `.xcs` or `.xs`.
|
|
28
|
+
- Pick a target machine from a list of 13 xTool devices.
|
|
29
|
+
- Set power and speed for each object in `.xs` output.
|
|
30
|
+
|
|
31
|
+
[Back to Top](#table-of-contents)
|
|
32
|
+
|
|
33
|
+
## Getting Started
|
|
34
|
+
|
|
35
|
+
### Prerequisites
|
|
36
|
+
|
|
37
|
+
- Node.js 19 or newer, or a current web browser.
|
|
38
|
+
- A package manager such as npm or pnpm.
|
|
39
|
+
|
|
40
|
+
### Installation
|
|
21
41
|
|
|
22
42
|
```bash
|
|
23
43
|
npm install @richardmcquiston01/unofficial-xcs-writer
|
|
@@ -25,49 +45,46 @@ npm install @richardmcquiston01/unofficial-xcs-writer
|
|
|
25
45
|
pnpm add @richardmcquiston01/unofficial-xcs-writer
|
|
26
46
|
```
|
|
27
47
|
|
|
28
|
-
|
|
48
|
+
### Usage
|
|
49
|
+
|
|
50
|
+
To fill in a template, follow these steps:
|
|
51
|
+
|
|
52
|
+
1. Load the file as an `ArrayBuffer`.
|
|
53
|
+
2. Check the format with `assertXcsFormat`. It throws an error if the file is not valid.
|
|
54
|
+
3. List the placeholders with `extractXcsTokens`.
|
|
55
|
+
4. Call `renderXcsFile` with your values.
|
|
56
|
+
|
|
57
|
+
For `.xs` files, use `assertXsFormat`, `extractXsTokens`, and `renderXsFile`. They work the same way.
|
|
58
|
+
|
|
59
|
+
To build a project, call `createXCS` with a machine name. Add objects with `.addText()`, `.addPath()`, and `.addBitmap()`. Finish with `.toBytes()` for `.xcs` or `.toXsBytes()` for `.xs`.
|
|
60
|
+
|
|
61
|
+
### Examples
|
|
62
|
+
|
|
63
|
+
Fill in a template:
|
|
29
64
|
|
|
30
65
|
```ts
|
|
31
|
-
import {
|
|
66
|
+
import {
|
|
67
|
+
assertXcsFormat,
|
|
68
|
+
extractXcsTokens,
|
|
69
|
+
renderXcsFile,
|
|
70
|
+
} from '@richardmcquiston01/unofficial-xcs-writer';
|
|
32
71
|
import type { XcsVariable } from '@richardmcquiston01/unofficial-xcs-writer';
|
|
33
72
|
|
|
34
|
-
|
|
35
|
-
const buffer = await fetch('/templates/name-tag.xcs').then(r => r.arrayBuffer());
|
|
36
|
-
|
|
37
|
-
// Validate it's a real XCS file
|
|
38
|
-
assertXcsFormat(buffer); // throws if invalid
|
|
73
|
+
const buffer: ArrayBuffer = await fetch('/templates/name-tag.xcs').then((r) => r.arrayBuffer());
|
|
39
74
|
|
|
40
|
-
|
|
41
|
-
const tokens = extractXcsTokens(buffer);
|
|
42
|
-
// e.g. ['FirstName', 'LastName']
|
|
75
|
+
assertXcsFormat(buffer);
|
|
76
|
+
const tokens: string[] = extractXcsTokens(buffer); // ['FirstName', 'LastName']
|
|
43
77
|
|
|
44
|
-
// Define variables and supply values
|
|
45
78
|
const variables: XcsVariable[] = [
|
|
46
79
|
{ token: 'FirstName', defaultValue: 'Guest' },
|
|
47
80
|
{ token: 'LastName' },
|
|
48
81
|
];
|
|
49
|
-
const values = { FirstName: 'Jane', LastName: 'Smith' };
|
|
82
|
+
const values: Record<string, string> = { FirstName: 'Jane', LastName: 'Smith' };
|
|
50
83
|
|
|
51
|
-
// Produce a filled-in .xcs file ready for download
|
|
52
84
|
const output: Uint8Array = renderXcsFile(buffer, variables, values);
|
|
53
85
|
```
|
|
54
86
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Same shape, different functions — `.xs` is a ZIP archive, so there's no `readXsFile` equivalent to `readXcsFile` (there's no single JSON string to return), but token extraction and substitution work the same way:
|
|
58
|
-
|
|
59
|
-
```ts
|
|
60
|
-
import { assertXsFormat, extractXsTokens, renderXsFile } from '@richardmcquiston01/unofficial-xcs-writer';
|
|
61
|
-
|
|
62
|
-
const buffer = await fetch('/templates/name-tag.xs').then(r => r.arrayBuffer());
|
|
63
|
-
|
|
64
|
-
assertXsFormat(buffer); // throws if invalid
|
|
65
|
-
const tokens = extractXsTokens(buffer); // e.g. ['FirstName', 'LastName']
|
|
66
|
-
|
|
67
|
-
const output: Uint8Array = renderXsFile(buffer, variables, values);
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### Building a project from scratch
|
|
87
|
+
Build a project from scratch:
|
|
71
88
|
|
|
72
89
|
```ts
|
|
73
90
|
import {
|
|
@@ -79,107 +96,96 @@ import {
|
|
|
79
96
|
|
|
80
97
|
const font = loadDefaultFont();
|
|
81
98
|
|
|
82
|
-
const
|
|
83
|
-
// Straight text needs a real glyph layout so xTool Studio renders
|
|
84
|
-
// actual outlines instead of placeholder blocks.
|
|
99
|
+
const bytes: Uint8Array = createXCS('P2S')
|
|
85
100
|
.addText('Hello', 10, 10, {
|
|
86
101
|
fontFamily: 'Arial',
|
|
87
102
|
fontSize: 24,
|
|
88
103
|
layout: layoutGlyphText(font, 'Hello', 8, 10, 10),
|
|
89
104
|
})
|
|
90
|
-
// Curved text: same idea, via layoutCurvedGlyphText -- the curve
|
|
91
|
-
// is baked into each character's own glyph shape and position, the
|
|
92
|
-
// way substituted text already bakes straight shapes.
|
|
93
105
|
.addText('Arch Text', 0, 40, {
|
|
94
106
|
fontFamily: 'Arial',
|
|
95
107
|
fontSize: 18,
|
|
96
108
|
layout: layoutCurvedGlyphText(font, 'Arch Text', 6, 90, 60, 20, 'center'),
|
|
97
109
|
})
|
|
98
110
|
.addPath('M0 0L10 0L10 10L0 10Z', 0, 60, 10, 10, {
|
|
99
|
-
// Only affects .toXsBytes() output -- see below.
|
|
100
111
|
processing: { processingType: 'VECTOR_CUTTING', values: { power: 80, speed: 10 } },
|
|
101
112
|
})
|
|
102
|
-
.
|
|
113
|
+
.toXsBytes();
|
|
103
114
|
```
|
|
104
115
|
|
|
105
|
-
|
|
106
|
-
archive — same chainable builder, same display objects, different container.
|
|
107
|
-
|
|
108
|
-
Pass `processing: { processingType, values }` to `.addText()`/`.addPath()`/`.addBitmap()`'s
|
|
109
|
-
options to set that display's power/speed/etc. in the exported `.xs` file's `profiles.json` +
|
|
110
|
-
device bindings — `.xcs` output (`.toBytes()`) ignores this option; its own per-display
|
|
111
|
-
processing format is different and not supported yet. Displays given identical settings share
|
|
112
|
-
one profile/binding automatically. `processingType`/`values`' exact shape is xTool Studio's own
|
|
113
|
-
and undocumented beyond real exports — see `xs_samples/*.xs`'s `profiles.json` for examples
|
|
114
|
-
(`VECTOR_ENGRAVING`, `VECTOR_CUTTING`, `FILL_VECTOR_ENGRAVING`). Displays with no `processing`
|
|
115
|
-
option behave as before: a generated `.xs` file's power/speed may need to be set manually in
|
|
116
|
-
xTool Studio before cutting/engraving.
|
|
117
|
-
|
|
118
|
-
`createXCS('P2S')`'s argument (and `XCSGeneratorOptions.deviceId`) accepts a known machine name
|
|
119
|
-
from `XTOOL_MACHINES` — currently 13 machines verified from real exports in this repo (`"P2S"`,
|
|
120
|
-
`"F2 Ultra UV"`, `"S1"`, `"P3"`, `"M2"`, `"F2"`, `"F2 Ultra (Single)"`, `"F2 Ultra"`,
|
|
121
|
-
`"M1 Ultra"`, `"F1"`, `"F1 Lite"`, `"F1 Ultra"`, `"MetalFab"`) — and resolves the real
|
|
122
|
-
`extId`/`extName`/`deviceCode` (`.xs` only)/default power for that machine, instead of reusing
|
|
123
|
-
whatever string you pass for all of them. Export the same design for multiple machines by
|
|
124
|
-
building it once and calling `.toXsBytes()`/`.toBytes()` once per `createXCS(machineName)`:
|
|
116
|
+
Keep these points in mind:
|
|
125
117
|
|
|
126
|
-
|
|
127
|
-
|
|
118
|
+
- Text needs a glyph `layout`. Without one, xTool Studio shows placeholder blocks instead of real letters.
|
|
119
|
+
- The `processing` option only changes `.xs` output. Objects with the same settings share one profile.
|
|
120
|
+
- The library does not check the processing values. Confirm power and speed in xTool Studio before you cut.
|
|
128
121
|
|
|
129
|
-
|
|
130
|
-
const bytes = createXCS(machine).addPath('M0 0L10 0L10 10L0 10Z', 0, 0, 10, 10).toXsBytes();
|
|
131
|
-
// write `bytes` to e.g. `design-${machine}.xs`
|
|
132
|
-
}
|
|
133
|
-
```
|
|
122
|
+
[Back to Top](#table-of-contents)
|
|
134
123
|
|
|
135
|
-
|
|
136
|
-
`extName`/`device.id` all set to that string, default power `55`); you can also pass a
|
|
137
|
-
`MachineProfile` object directly for a machine not yet in the catalog.
|
|
124
|
+
## Supported Machines
|
|
138
125
|
|
|
139
|
-
|
|
126
|
+
Pass one of these names to `createXCS`. The library then uses the real device ids and default power for that machine.
|
|
140
127
|
|
|
141
|
-
|
|
128
|
+
| | | | |
|
|
129
|
+
|---|---|---|---|
|
|
130
|
+
| P2S | P3 | S1 | M2 |
|
|
131
|
+
| M1 Ultra | F1 | F1 Lite | F1 Ultra |
|
|
132
|
+
| F2 | F2 Ultra | F2 Ultra (Single) | F2 Ultra UV |
|
|
133
|
+
| MetalFab | | | |
|
|
142
134
|
|
|
143
|
-
|
|
135
|
+
You can also pass a `MachineProfile` object or a plain device id string. A plain string still works, but it gets default values. To export one design for every machine, loop over `Object.keys(XTOOL_MACHINES)`.
|
|
144
136
|
|
|
145
|
-
[
|
|
137
|
+
[Back to Top](#table-of-contents)
|
|
138
|
+
|
|
139
|
+
## Known Limitations
|
|
140
|
+
|
|
141
|
+
- New text on an already curved text object is laid out straight.
|
|
142
|
+
- Curved text built from scratch has not been checked against real xTool Studio output.
|
|
143
|
+
- Multi-line curved text is not supported.
|
|
144
|
+
- `.xcs` output has no processing-profile support.
|
|
146
145
|
|
|
147
|
-
|
|
146
|
+
[Back to Top](#table-of-contents)
|
|
148
147
|
|
|
149
|
-
|
|
148
|
+
## More Information
|
|
149
|
+
|
|
150
|
+
- [API.md](./API.md) lists every function, option, and type.
|
|
151
|
+
- [CHANGELOG.md](./CHANGELOG.md) lists changes for each release.
|
|
152
|
+
|
|
153
|
+
[Back to Top](#table-of-contents)
|
|
150
154
|
|
|
151
155
|
## Development
|
|
152
156
|
|
|
153
157
|
```bash
|
|
154
|
-
pnpm install
|
|
155
|
-
pnpm build
|
|
156
|
-
pnpm dev
|
|
157
|
-
pnpm test
|
|
158
|
-
|
|
158
|
+
pnpm install # install dependencies
|
|
159
|
+
pnpm build # build to dist/ (ESM, CJS, and type files)
|
|
160
|
+
pnpm dev # rebuild on every save
|
|
161
|
+
pnpm test # run the tests
|
|
162
|
+
pnpm typecheck # check types without building
|
|
159
163
|
```
|
|
160
164
|
|
|
161
|
-
|
|
165
|
+
The library uses [opentype.js](https://github.com/opentypejs/opentype.js) to read glyph outlines and [fflate](https://github.com/101arrowz/fflate) to read and write ZIP archives. The built-in fallback font is [Arimo](https://fonts.google.com/specimen/Arimo) version 5.2.8, under the Apache 2 license. Its license text is in `third_party/arimo/LICENSE`.
|
|
166
|
+
|
|
167
|
+
[Back to Top](#table-of-contents)
|
|
162
168
|
|
|
163
|
-
|
|
164
|
-
|------|--------|
|
|
165
|
-
| `dist/index.js` | ESM |
|
|
166
|
-
| `dist/index.cjs` | CommonJS |
|
|
167
|
-
| `dist/index.d.ts` | TypeScript declarations |
|
|
169
|
+
## Buy Me a Coffee
|
|
168
170
|
|
|
169
|
-
|
|
171
|
+
If this app, code, or repository has helped you or someone you know, please consider donating. I appreciate any help to offset the costs of development and/or AI Credits.
|
|
170
172
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
[**Donate via Stripe**](https://donate.stripe.com/00w5kD3Gj1Xo9v7gVOcs800), or scan:
|
|
174
|
+
|
|
175
|
+
[](https://donate.stripe.com/00w5kD3Gj1Xo9v7gVOcs800)
|
|
176
|
+
|
|
177
|
+
[Back to Top](#table-of-contents)
|
|
176
178
|
|
|
177
179
|
## License
|
|
178
180
|
|
|
179
|
-
[
|
|
181
|
+
MIT. See [LICENSE](./LICENSE). The bundled Arimo font uses the Apache 2 license.
|
|
182
|
+
|
|
183
|
+
[Back to Top](#table-of-contents)
|
|
180
184
|
|
|
181
185
|
## Copyright
|
|
182
186
|
|
|
183
|
-
(c)2026 Richard McQuiston
|
|
187
|
+
(c)2026 Richard McQuiston. All rights reserved.
|
|
188
|
+
|
|
189
|
+
xTool, xTool Studio, and xTool Creative Space are trademarks of Makeblock Co., Ltd. This project is not affiliated with, endorsed by, or sponsored by Makeblock Co., Ltd. or xTool.
|
|
184
190
|
|
|
185
|
-
|
|
191
|
+
[Back to Top](#table-of-contents)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@richardmcquiston01/unofficial-xcs-writer",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Read, write, build, and apply variable substitution to xTool Creative Space .xcs and .xs files, with real glyph outline extraction for text",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Richard McQuiston <richard.mcquiston01@gmail.com>",
|