@lexmata/micropdf 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +191 -0
- package/README.md +985 -0
- package/binding.gyp +73 -0
- package/dist/annot.d.ts +458 -0
- package/dist/annot.d.ts.map +1 -0
- package/dist/annot.js +697 -0
- package/dist/annot.js.map +1 -0
- package/dist/archive.d.ts +128 -0
- package/dist/archive.d.ts.map +1 -0
- package/dist/archive.js +268 -0
- package/dist/archive.js.map +1 -0
- package/dist/buffer.d.ts +572 -0
- package/dist/buffer.d.ts.map +1 -0
- package/dist/buffer.js +971 -0
- package/dist/buffer.js.map +1 -0
- package/dist/colorspace.d.ts +287 -0
- package/dist/colorspace.d.ts.map +1 -0
- package/dist/colorspace.js +542 -0
- package/dist/colorspace.js.map +1 -0
- package/dist/context.d.ts +184 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +320 -0
- package/dist/context.js.map +1 -0
- package/dist/cookie.d.ts +164 -0
- package/dist/cookie.d.ts.map +1 -0
- package/dist/cookie.js +306 -0
- package/dist/cookie.js.map +1 -0
- package/dist/device.d.ts +169 -0
- package/dist/device.d.ts.map +1 -0
- package/dist/device.js +350 -0
- package/dist/device.js.map +1 -0
- package/dist/display-list.d.ts +202 -0
- package/dist/display-list.d.ts.map +1 -0
- package/dist/display-list.js +410 -0
- package/dist/display-list.js.map +1 -0
- package/dist/document.d.ts +637 -0
- package/dist/document.d.ts.map +1 -0
- package/dist/document.js +902 -0
- package/dist/document.js.map +1 -0
- package/dist/easy.d.ts +423 -0
- package/dist/easy.d.ts.map +1 -0
- package/dist/easy.js +644 -0
- package/dist/easy.js.map +1 -0
- package/dist/enhanced.d.ts +226 -0
- package/dist/enhanced.d.ts.map +1 -0
- package/dist/enhanced.js +368 -0
- package/dist/enhanced.js.map +1 -0
- package/dist/filter.d.ts +51 -0
- package/dist/filter.d.ts.map +1 -0
- package/dist/filter.js +381 -0
- package/dist/filter.js.map +1 -0
- package/dist/font.d.ts +222 -0
- package/dist/font.d.ts.map +1 -0
- package/dist/font.js +381 -0
- package/dist/font.js.map +1 -0
- package/dist/form.d.ts +214 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +497 -0
- package/dist/form.js.map +1 -0
- package/dist/geometry.d.ts +469 -0
- package/dist/geometry.d.ts.map +1 -0
- package/dist/geometry.js +780 -0
- package/dist/geometry.js.map +1 -0
- package/dist/image.d.ts +172 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +348 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +171 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +339 -0
- package/dist/index.js.map +1 -0
- package/dist/link.d.ts +168 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +343 -0
- package/dist/link.js.map +1 -0
- package/dist/micropdf.d.ts +40 -0
- package/dist/micropdf.d.ts.map +1 -0
- package/dist/micropdf.js +45 -0
- package/dist/micropdf.js.map +1 -0
- package/dist/nanopdf.d.ts +40 -0
- package/dist/nanopdf.d.ts.map +1 -0
- package/dist/nanopdf.js +45 -0
- package/dist/nanopdf.js.map +1 -0
- package/dist/native.d.ts +242 -0
- package/dist/native.d.ts.map +1 -0
- package/dist/native.js +509 -0
- package/dist/native.js.map +1 -0
- package/dist/output.d.ts +166 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +365 -0
- package/dist/output.js.map +1 -0
- package/dist/path.d.ts +420 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +687 -0
- package/dist/path.js.map +1 -0
- package/dist/pdf/object.d.ts +489 -0
- package/dist/pdf/object.d.ts.map +1 -0
- package/dist/pdf/object.js +1045 -0
- package/dist/pdf/object.js.map +1 -0
- package/dist/pixmap.d.ts +315 -0
- package/dist/pixmap.d.ts.map +1 -0
- package/dist/pixmap.js +590 -0
- package/dist/pixmap.js.map +1 -0
- package/dist/profiler.d.ts +159 -0
- package/dist/profiler.d.ts.map +1 -0
- package/dist/profiler.js +380 -0
- package/dist/profiler.js.map +1 -0
- package/dist/render-options.d.ts +227 -0
- package/dist/render-options.d.ts.map +1 -0
- package/dist/render-options.js +130 -0
- package/dist/render-options.js.map +1 -0
- package/dist/resource-tracking.d.ts +332 -0
- package/dist/resource-tracking.d.ts.map +1 -0
- package/dist/resource-tracking.js +653 -0
- package/dist/resource-tracking.js.map +1 -0
- package/dist/simple.d.ts +276 -0
- package/dist/simple.d.ts.map +1 -0
- package/dist/simple.js +343 -0
- package/dist/simple.js.map +1 -0
- package/dist/stext.d.ts +290 -0
- package/dist/stext.d.ts.map +1 -0
- package/dist/stext.js +312 -0
- package/dist/stext.js.map +1 -0
- package/dist/stream.d.ts +174 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +476 -0
- package/dist/stream.js.map +1 -0
- package/dist/text.d.ts +337 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +454 -0
- package/dist/text.js.map +1 -0
- package/dist/typed-arrays.d.ts +127 -0
- package/dist/typed-arrays.d.ts.map +1 -0
- package/dist/typed-arrays.js +410 -0
- package/dist/typed-arrays.js.map +1 -0
- package/dist/types.d.ts +358 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +216 -0
- package/dist/types.js.map +1 -0
- package/native/annot.cc +557 -0
- package/native/buffer.cc +204 -0
- package/native/colorspace.cc +166 -0
- package/native/context.cc +84 -0
- package/native/cookie.cc +179 -0
- package/native/device.cc +179 -0
- package/native/display_list.cc +179 -0
- package/native/document.cc +268 -0
- package/native/enhanced.cc +70 -0
- package/native/font.cc +282 -0
- package/native/form.cc +523 -0
- package/native/geometry.cc +255 -0
- package/native/image.cc +216 -0
- package/native/include/micropdf/enhanced.h +38 -0
- package/native/include/micropdf/types.h +36 -0
- package/native/include/micropdf.h +106 -0
- package/native/include/mupdf-ffi.h +39 -0
- package/native/include/mupdf.h +11 -0
- package/native/include/mupdf_minimal.h +381 -0
- package/native/lib/linux-x64/libmicropdf.a +0 -0
- package/native/link.cc +234 -0
- package/native/micropdf.cc +71 -0
- package/native/output.cc +229 -0
- package/native/page.cc +572 -0
- package/native/path.cc +259 -0
- package/native/pixmap.cc +240 -0
- package/native/stext.cc +610 -0
- package/native/stream.cc +239 -0
- package/package.json +120 -0
- package/scripts/build-from-rust.js +97 -0
- package/scripts/install.js +184 -0
package/dist/path.js
ADDED
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path - PDF path construction and vector graphics
|
|
3
|
+
*
|
|
4
|
+
* This module provides comprehensive support for creating and manipulating vector paths
|
|
5
|
+
* used in PDF graphics. Paths are fundamental building blocks for drawing lines, curves,
|
|
6
|
+
* shapes, and complex vector graphics in PDF documents.
|
|
7
|
+
*
|
|
8
|
+
* This module provides 100% API compatibility with MuPDF's path operations.
|
|
9
|
+
*
|
|
10
|
+
* @module path
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { Path, StrokeState, LineCap, LineJoin, Matrix } from 'micropdf';
|
|
14
|
+
*
|
|
15
|
+
* // Create a simple path
|
|
16
|
+
* const path = Path.create();
|
|
17
|
+
* path.moveTo(10, 10);
|
|
18
|
+
* path.lineTo(100, 10);
|
|
19
|
+
* path.lineTo(100, 100);
|
|
20
|
+
* path.close();
|
|
21
|
+
*
|
|
22
|
+
* // Create a rectangle
|
|
23
|
+
* const rect = Path.create();
|
|
24
|
+
* rect.rect(0, 0, 200, 100);
|
|
25
|
+
*
|
|
26
|
+
* // Create a curve
|
|
27
|
+
* const curve = Path.create();
|
|
28
|
+
* curve.moveTo(0, 0);
|
|
29
|
+
* curve.curveTo(50, 100, 100, 100, 150, 0);
|
|
30
|
+
*
|
|
31
|
+
* // Configure stroke
|
|
32
|
+
* const stroke = StrokeState.create();
|
|
33
|
+
* stroke.setLineWidth(2);
|
|
34
|
+
* stroke.setLineCap(LineCap.Round);
|
|
35
|
+
* stroke.setLineJoin(LineJoin.Round);
|
|
36
|
+
* stroke.setDash([5, 3], 0); // 5 on, 3 off
|
|
37
|
+
*
|
|
38
|
+
* // Transform path
|
|
39
|
+
* const matrix = Matrix.scale(2, 2);
|
|
40
|
+
* path.transform(matrix);
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
import { Point, Rect, Matrix } from './geometry.js';
|
|
44
|
+
/**
|
|
45
|
+
* Line cap styles determine how the ends of stroked paths are rendered.
|
|
46
|
+
*
|
|
47
|
+
* @enum {number}
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const stroke = StrokeState.create();
|
|
51
|
+
* stroke.setLineCap(LineCap.Round); // Rounded end caps
|
|
52
|
+
* stroke.setStartCap(LineCap.Square); // Square start cap
|
|
53
|
+
* stroke.setEndCap(LineCap.Butt); // Flat end cap
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export var LineCap;
|
|
57
|
+
(function (LineCap) {
|
|
58
|
+
/**
|
|
59
|
+
* Butt cap - Line ends exactly at the endpoint (no extension).
|
|
60
|
+
* This is the most common and default cap style.
|
|
61
|
+
*/
|
|
62
|
+
LineCap[LineCap["Butt"] = 0] = "Butt";
|
|
63
|
+
/**
|
|
64
|
+
* Round cap - Line extends with a semicircular cap at the endpoint.
|
|
65
|
+
* The diameter equals the line width.
|
|
66
|
+
*/
|
|
67
|
+
LineCap[LineCap["Round"] = 1] = "Round";
|
|
68
|
+
/**
|
|
69
|
+
* Square cap - Line extends with a square cap at the endpoint.
|
|
70
|
+
* The extension equals half the line width.
|
|
71
|
+
*/
|
|
72
|
+
LineCap[LineCap["Square"] = 2] = "Square";
|
|
73
|
+
/**
|
|
74
|
+
* Triangle cap - Line extends with a triangular cap at the endpoint.
|
|
75
|
+
* Less common, used for special effects.
|
|
76
|
+
*/
|
|
77
|
+
LineCap[LineCap["Triangle"] = 3] = "Triangle";
|
|
78
|
+
})(LineCap || (LineCap = {}));
|
|
79
|
+
/**
|
|
80
|
+
* Line join styles determine how path segments connect at corners.
|
|
81
|
+
*
|
|
82
|
+
* @enum {number}
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* const stroke = StrokeState.create();
|
|
86
|
+
* stroke.setLineJoin(LineJoin.Round); // Smooth rounded corners
|
|
87
|
+
* stroke.setLineJoin(LineJoin.Miter); // Sharp pointed corners
|
|
88
|
+
* stroke.setMiterLimit(10); // Limit miter length
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export var LineJoin;
|
|
92
|
+
(function (LineJoin) {
|
|
93
|
+
/**
|
|
94
|
+
* Miter join - Extends the outer edges until they meet at a point.
|
|
95
|
+
* Creates sharp corners but can become very long at acute angles.
|
|
96
|
+
* Use miterLimit to prevent excessive extension.
|
|
97
|
+
*/
|
|
98
|
+
LineJoin[LineJoin["Miter"] = 0] = "Miter";
|
|
99
|
+
/**
|
|
100
|
+
* Round join - Joins segments with a circular arc.
|
|
101
|
+
* Creates smooth, rounded corners with radius equal to half the line width.
|
|
102
|
+
*/
|
|
103
|
+
LineJoin[LineJoin["Round"] = 1] = "Round";
|
|
104
|
+
/**
|
|
105
|
+
* Bevel join - Joins segments with a straight line across the corner.
|
|
106
|
+
* Creates a flat, beveled corner. Safe for all angles.
|
|
107
|
+
*/
|
|
108
|
+
LineJoin[LineJoin["Bevel"] = 2] = "Bevel";
|
|
109
|
+
/**
|
|
110
|
+
* Miter XPS join - Miter join variant used in XPS documents.
|
|
111
|
+
* Similar to standard miter but with slightly different behavior.
|
|
112
|
+
*/
|
|
113
|
+
LineJoin[LineJoin["MiterXPS"] = 3] = "MiterXPS";
|
|
114
|
+
})(LineJoin || (LineJoin = {}));
|
|
115
|
+
/**
|
|
116
|
+
* Type alias for line cap styles (backwards compatibility).
|
|
117
|
+
* @deprecated Use {@link LineCap} instead
|
|
118
|
+
* @type {typeof LineCap}
|
|
119
|
+
*/
|
|
120
|
+
export const LineCapStyle = LineCap;
|
|
121
|
+
/**
|
|
122
|
+
* Type alias for line join styles (backwards compatibility).
|
|
123
|
+
* @deprecated Use {@link LineJoin} instead
|
|
124
|
+
* @type {typeof LineJoin}
|
|
125
|
+
*/
|
|
126
|
+
export const LineJoinStyle = LineJoin;
|
|
127
|
+
/**
|
|
128
|
+
* Stroke state configuration for path rendering.
|
|
129
|
+
*
|
|
130
|
+
* StrokeState encapsulates all the properties that control how paths are stroked
|
|
131
|
+
* (drawn with lines). This includes line width, cap styles, join styles, miter limits,
|
|
132
|
+
* and dash patterns.
|
|
133
|
+
*
|
|
134
|
+
* **Reference Counting**: StrokeState uses manual reference counting. Call `keep()` to
|
|
135
|
+
* increment the reference count and `drop()` to decrement it.
|
|
136
|
+
*
|
|
137
|
+
* @class StrokeState
|
|
138
|
+
* @example
|
|
139
|
+
* ```typescript
|
|
140
|
+
* // Create default stroke state
|
|
141
|
+
* const stroke = StrokeState.create();
|
|
142
|
+
*
|
|
143
|
+
* // Configure stroke properties
|
|
144
|
+
* stroke.setLineWidth(2.5);
|
|
145
|
+
* stroke.setStartCap(LineCap.Round);
|
|
146
|
+
* stroke.setLineJoin(LineJoin.Round);
|
|
147
|
+
* stroke.setMiterLimit(10);
|
|
148
|
+
*
|
|
149
|
+
* // Set dash pattern: 5 units on, 3 units off
|
|
150
|
+
* stroke.setDash([5, 3], 0);
|
|
151
|
+
*
|
|
152
|
+
* // Clone for variations
|
|
153
|
+
* const thickStroke = stroke.clone();
|
|
154
|
+
* thickStroke.setLineWidth(5);
|
|
155
|
+
*
|
|
156
|
+
* // Clean up
|
|
157
|
+
* stroke.drop();
|
|
158
|
+
* thickStroke.drop();
|
|
159
|
+
* ```
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```typescript
|
|
163
|
+
* // Create dotted line
|
|
164
|
+
* const dotted = StrokeState.create();
|
|
165
|
+
* dotted.setLineWidth(1);
|
|
166
|
+
* dotted.setLineCap(LineCap.Round);
|
|
167
|
+
* dotted.setDash([2, 2], 0); // 2 on, 2 off
|
|
168
|
+
*
|
|
169
|
+
* // Create dashed line
|
|
170
|
+
* const dashed = StrokeState.create();
|
|
171
|
+
* dashed.setLineWidth(2);
|
|
172
|
+
* dashed.setDash([10, 5], 0); // 10 on, 5 off
|
|
173
|
+
*
|
|
174
|
+
* // Create solid line with rounded corners
|
|
175
|
+
* const rounded = StrokeState.create();
|
|
176
|
+
* rounded.setLineWidth(3);
|
|
177
|
+
* rounded.setLineCap(LineCap.Round);
|
|
178
|
+
* rounded.setLineJoin(LineJoin.Round);
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
export class StrokeState {
|
|
182
|
+
_lineWidth = 1.0;
|
|
183
|
+
_startCap = LineCap.Butt;
|
|
184
|
+
_dashCap = LineCap.Butt;
|
|
185
|
+
_endCap = LineCap.Butt;
|
|
186
|
+
_lineJoin = LineJoin.Miter;
|
|
187
|
+
_miterLimit = 10.0;
|
|
188
|
+
_dashPhase = 0.0;
|
|
189
|
+
_dashPattern = [];
|
|
190
|
+
_refCount = 1;
|
|
191
|
+
constructor() { }
|
|
192
|
+
/**
|
|
193
|
+
* Create a new stroke state
|
|
194
|
+
*/
|
|
195
|
+
static create() {
|
|
196
|
+
return new StrokeState();
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Create stroke state with specific dash pattern length
|
|
200
|
+
*/
|
|
201
|
+
static createWithDashLen(dashLen) {
|
|
202
|
+
const state = new StrokeState();
|
|
203
|
+
state._dashPattern = new Array(dashLen).fill(0);
|
|
204
|
+
return state;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Clone this stroke state
|
|
208
|
+
*/
|
|
209
|
+
clone() {
|
|
210
|
+
const cloned = new StrokeState();
|
|
211
|
+
cloned._lineWidth = this._lineWidth;
|
|
212
|
+
cloned._startCap = this._startCap;
|
|
213
|
+
cloned._dashCap = this._dashCap;
|
|
214
|
+
cloned._endCap = this._endCap;
|
|
215
|
+
cloned._lineJoin = this._lineJoin;
|
|
216
|
+
cloned._miterLimit = this._miterLimit;
|
|
217
|
+
cloned._dashPhase = this._dashPhase;
|
|
218
|
+
cloned._dashPattern = [...this._dashPattern];
|
|
219
|
+
return cloned;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Keep (increment ref count)
|
|
223
|
+
*/
|
|
224
|
+
keep() {
|
|
225
|
+
this._refCount++;
|
|
226
|
+
return this;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Drop (decrement ref count)
|
|
230
|
+
*/
|
|
231
|
+
drop() {
|
|
232
|
+
if (this._refCount > 0) {
|
|
233
|
+
this._refCount--;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Unshare (make a unique copy if shared)
|
|
238
|
+
*/
|
|
239
|
+
unshare() {
|
|
240
|
+
if (this._refCount > 1) {
|
|
241
|
+
this._refCount--;
|
|
242
|
+
return this.clone();
|
|
243
|
+
}
|
|
244
|
+
return this;
|
|
245
|
+
}
|
|
246
|
+
// ============================================================================
|
|
247
|
+
// Line Width
|
|
248
|
+
// ============================================================================
|
|
249
|
+
get lineWidth() {
|
|
250
|
+
return this._lineWidth;
|
|
251
|
+
}
|
|
252
|
+
set lineWidth(width) {
|
|
253
|
+
this._lineWidth = width;
|
|
254
|
+
}
|
|
255
|
+
// ============================================================================
|
|
256
|
+
// Line Caps
|
|
257
|
+
// ============================================================================
|
|
258
|
+
get startCap() {
|
|
259
|
+
return this._startCap;
|
|
260
|
+
}
|
|
261
|
+
set startCap(cap) {
|
|
262
|
+
this._startCap = cap;
|
|
263
|
+
}
|
|
264
|
+
get dashCap() {
|
|
265
|
+
return this._dashCap;
|
|
266
|
+
}
|
|
267
|
+
set dashCap(cap) {
|
|
268
|
+
this._dashCap = cap;
|
|
269
|
+
}
|
|
270
|
+
get endCap() {
|
|
271
|
+
return this._endCap;
|
|
272
|
+
}
|
|
273
|
+
set endCap(cap) {
|
|
274
|
+
this._endCap = cap;
|
|
275
|
+
}
|
|
276
|
+
// ============================================================================
|
|
277
|
+
// Line Join
|
|
278
|
+
// ============================================================================
|
|
279
|
+
get lineJoin() {
|
|
280
|
+
return this._lineJoin;
|
|
281
|
+
}
|
|
282
|
+
set lineJoin(join) {
|
|
283
|
+
this._lineJoin = join;
|
|
284
|
+
}
|
|
285
|
+
get miterLimit() {
|
|
286
|
+
return this._miterLimit;
|
|
287
|
+
}
|
|
288
|
+
set miterLimit(limit) {
|
|
289
|
+
this._miterLimit = limit;
|
|
290
|
+
}
|
|
291
|
+
// ============================================================================
|
|
292
|
+
// Dash Pattern
|
|
293
|
+
// ============================================================================
|
|
294
|
+
get dashPhase() {
|
|
295
|
+
return this._dashPhase;
|
|
296
|
+
}
|
|
297
|
+
get dashLength() {
|
|
298
|
+
return this._dashPattern.length;
|
|
299
|
+
}
|
|
300
|
+
getDashPattern() {
|
|
301
|
+
return [...this._dashPattern];
|
|
302
|
+
}
|
|
303
|
+
setDash(pattern, phase = 0) {
|
|
304
|
+
this._dashPattern = [...pattern];
|
|
305
|
+
this._dashPhase = phase;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Check if stroke state is valid
|
|
309
|
+
*/
|
|
310
|
+
isValid() {
|
|
311
|
+
return this._lineWidth >= 0 && this._miterLimit >= 1 && this._dashPattern.every((v) => v >= 0);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Path command types
|
|
316
|
+
*/
|
|
317
|
+
var PathCmd;
|
|
318
|
+
(function (PathCmd) {
|
|
319
|
+
PathCmd[PathCmd["MoveTo"] = 0] = "MoveTo";
|
|
320
|
+
PathCmd[PathCmd["LineTo"] = 1] = "LineTo";
|
|
321
|
+
PathCmd[PathCmd["CurveTo"] = 2] = "CurveTo";
|
|
322
|
+
PathCmd[PathCmd["Close"] = 3] = "Close";
|
|
323
|
+
PathCmd[PathCmd["QuadTo"] = 4] = "QuadTo";
|
|
324
|
+
PathCmd[PathCmd["RectTo"] = 5] = "RectTo";
|
|
325
|
+
})(PathCmd || (PathCmd = {}));
|
|
326
|
+
/**
|
|
327
|
+
* A graphics path for constructing vector shapes and drawings.
|
|
328
|
+
*
|
|
329
|
+
* Path represents a sequence of drawing commands that define vector graphics.
|
|
330
|
+
* Paths can contain lines, curves, rectangles, and other geometric primitives.
|
|
331
|
+
* They can be stroked (outlined) or filled to create visible graphics in PDFs.
|
|
332
|
+
*
|
|
333
|
+
* **Path Construction**: Paths are built using a sequence of commands:
|
|
334
|
+
* - `moveTo()` - Start a new subpath at a point
|
|
335
|
+
* - `lineTo()` - Draw a straight line to a point
|
|
336
|
+
* - `curveTo()` - Draw a cubic Bézier curve
|
|
337
|
+
* - `quadTo()` - Draw a quadratic Bézier curve
|
|
338
|
+
* - `rect()` - Add a rectangle
|
|
339
|
+
* - `closePath()` / `close()` - Close the current subpath
|
|
340
|
+
*
|
|
341
|
+
* **Reference Counting**: Paths use manual reference counting. Call `keep()` to
|
|
342
|
+
* increment the reference count and `drop()` to decrement it.
|
|
343
|
+
*
|
|
344
|
+
* @class Path
|
|
345
|
+
* @example
|
|
346
|
+
* ```typescript
|
|
347
|
+
* // Draw a triangle
|
|
348
|
+
* const triangle = Path.create();
|
|
349
|
+
* triangle.moveTo(50, 0);
|
|
350
|
+
* triangle.lineTo(100, 100);
|
|
351
|
+
* triangle.lineTo(0, 100);
|
|
352
|
+
* triangle.close();
|
|
353
|
+
*
|
|
354
|
+
* // Draw a rectangle
|
|
355
|
+
* const rect = Path.create();
|
|
356
|
+
* rect.rect(10, 10, 200, 100);
|
|
357
|
+
*
|
|
358
|
+
* // Draw a rounded rectangle
|
|
359
|
+
* const roundedRect = Path.create();
|
|
360
|
+
* const x = 10, y = 10, w = 200, h = 100, r = 10;
|
|
361
|
+
* roundedRect.moveTo(x + r, y);
|
|
362
|
+
* roundedRect.lineTo(x + w - r, y);
|
|
363
|
+
* roundedRect.curveTo(x + w, y, x + w, y, x + w, y + r);
|
|
364
|
+
* roundedRect.lineTo(x + w, y + h - r);
|
|
365
|
+
* roundedRect.curveTo(x + w, y + h, x + w, y + h, x + w - r, y + h);
|
|
366
|
+
* roundedRect.lineTo(x + r, y + h);
|
|
367
|
+
* roundedRect.curveTo(x, y + h, x, y + h, x, y + h - r);
|
|
368
|
+
* roundedRect.lineTo(x, y + r);
|
|
369
|
+
* roundedRect.curveTo(x, y, x, y, x + r, y);
|
|
370
|
+
* roundedRect.close();
|
|
371
|
+
* ```
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* ```typescript
|
|
375
|
+
* // Draw a sine wave
|
|
376
|
+
* const wave = Path.create();
|
|
377
|
+
* wave.moveTo(0, 50);
|
|
378
|
+
* for (let x = 0; x <= 200; x += 5) {
|
|
379
|
+
* const y = 50 + Math.sin(x * 0.1) * 20;
|
|
380
|
+
* wave.lineTo(x, y);
|
|
381
|
+
* }
|
|
382
|
+
*
|
|
383
|
+
* // Transform the path
|
|
384
|
+
* const matrix = Matrix.scale(2, 2);
|
|
385
|
+
* wave.transform(matrix);
|
|
386
|
+
*
|
|
387
|
+
* // Check if empty
|
|
388
|
+
* if (!wave.isEmpty()) {
|
|
389
|
+
* console.log('Path has commands');
|
|
390
|
+
* }
|
|
391
|
+
*
|
|
392
|
+
* // Clean up
|
|
393
|
+
* wave.drop();
|
|
394
|
+
* ```
|
|
395
|
+
*
|
|
396
|
+
* @example
|
|
397
|
+
* ```typescript
|
|
398
|
+
* // Draw a circle approximation with cubic Bézier curves
|
|
399
|
+
* const circle = Path.create();
|
|
400
|
+
* const cx = 100, cy = 100, r = 50;
|
|
401
|
+
* const k = 0.5522847498; // 4/3 * (sqrt(2) - 1)
|
|
402
|
+
* const kr = r * k;
|
|
403
|
+
*
|
|
404
|
+
* circle.moveTo(cx, cy - r);
|
|
405
|
+
* circle.curveTo(cx + kr, cy - r, cx + r, cy - kr, cx + r, cy);
|
|
406
|
+
* circle.curveTo(cx + r, cy + kr, cx + kr, cy + r, cx, cy + r);
|
|
407
|
+
* circle.curveTo(cx - kr, cy + r, cx - r, cy + kr, cx - r, cy);
|
|
408
|
+
* circle.curveTo(cx - r, cy - kr, cx - kr, cy - r, cx, cy - r);
|
|
409
|
+
* circle.close();
|
|
410
|
+
* ```
|
|
411
|
+
*/
|
|
412
|
+
export class Path {
|
|
413
|
+
_commands = [];
|
|
414
|
+
_currentPoint = Point.ORIGIN;
|
|
415
|
+
_refCount = 1;
|
|
416
|
+
constructor() { }
|
|
417
|
+
/**
|
|
418
|
+
* Create a new empty path
|
|
419
|
+
*/
|
|
420
|
+
static create() {
|
|
421
|
+
return new Path();
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Keep (increment ref count)
|
|
425
|
+
*/
|
|
426
|
+
keep() {
|
|
427
|
+
this._refCount++;
|
|
428
|
+
return this;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Drop (decrement ref count)
|
|
432
|
+
*/
|
|
433
|
+
drop() {
|
|
434
|
+
if (this._refCount > 0) {
|
|
435
|
+
this._refCount--;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Clone this path
|
|
440
|
+
*/
|
|
441
|
+
clone() {
|
|
442
|
+
const cloned = new Path();
|
|
443
|
+
cloned._commands = this._commands.map((cmd) => ({
|
|
444
|
+
cmd: cmd.cmd,
|
|
445
|
+
params: [...cmd.params]
|
|
446
|
+
}));
|
|
447
|
+
cloned._currentPoint = this._currentPoint;
|
|
448
|
+
return cloned;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Get the current point
|
|
452
|
+
*/
|
|
453
|
+
get currentPoint() {
|
|
454
|
+
return this._currentPoint;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Check if the path is empty
|
|
458
|
+
*/
|
|
459
|
+
isEmpty() {
|
|
460
|
+
return this._commands.length === 0;
|
|
461
|
+
}
|
|
462
|
+
moveTo(xOrPoint, y) {
|
|
463
|
+
const [x, actualY] = typeof xOrPoint === 'number' ? [xOrPoint, y] : [xOrPoint.x, xOrPoint.y];
|
|
464
|
+
this._commands.push({ cmd: PathCmd.MoveTo, params: [x, actualY] });
|
|
465
|
+
this._currentPoint = new Point(x, actualY);
|
|
466
|
+
return this;
|
|
467
|
+
}
|
|
468
|
+
lineTo(xOrPoint, y) {
|
|
469
|
+
const [x, actualY] = typeof xOrPoint === 'number' ? [xOrPoint, y] : [xOrPoint.x, xOrPoint.y];
|
|
470
|
+
this._commands.push({ cmd: PathCmd.LineTo, params: [x, actualY] });
|
|
471
|
+
this._currentPoint = new Point(x, actualY);
|
|
472
|
+
return this;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Draw a cubic Bézier curve
|
|
476
|
+
*/
|
|
477
|
+
curveTo(cx1, cy1, cx2, cy2, x, y) {
|
|
478
|
+
this._commands.push({
|
|
479
|
+
cmd: PathCmd.CurveTo,
|
|
480
|
+
params: [cx1, cy1, cx2, cy2, x, y]
|
|
481
|
+
});
|
|
482
|
+
this._currentPoint = new Point(x, y);
|
|
483
|
+
return this;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Draw a quadratic Bézier curve
|
|
487
|
+
*/
|
|
488
|
+
quadTo(cx, cy, x, y) {
|
|
489
|
+
this._commands.push({
|
|
490
|
+
cmd: PathCmd.QuadTo,
|
|
491
|
+
params: [cx, cy, x, y]
|
|
492
|
+
});
|
|
493
|
+
this._currentPoint = new Point(x, y);
|
|
494
|
+
return this;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Close the current subpath
|
|
498
|
+
*/
|
|
499
|
+
closePath() {
|
|
500
|
+
this._commands.push({ cmd: PathCmd.Close, params: [] });
|
|
501
|
+
return this;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Close the current subpath (alias for closePath)
|
|
505
|
+
*/
|
|
506
|
+
close() {
|
|
507
|
+
return this.closePath();
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Add a rectangle to the path
|
|
511
|
+
*/
|
|
512
|
+
rectTo(x, y, w, h) {
|
|
513
|
+
this._commands.push({
|
|
514
|
+
cmd: PathCmd.RectTo,
|
|
515
|
+
params: [x, y, w, h]
|
|
516
|
+
});
|
|
517
|
+
// Rectangle starts and ends at (x, y)
|
|
518
|
+
this._currentPoint = new Point(x, y);
|
|
519
|
+
return this;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Add a rectangle from corners
|
|
523
|
+
*/
|
|
524
|
+
rect(x1, y1, x2, y2) {
|
|
525
|
+
return this.rectTo(x1, y1, x2 - x1, y2 - y1);
|
|
526
|
+
}
|
|
527
|
+
// ============================================================================
|
|
528
|
+
// Path Operations
|
|
529
|
+
// ============================================================================
|
|
530
|
+
/**
|
|
531
|
+
* Get the bounding box of the path
|
|
532
|
+
*/
|
|
533
|
+
getBounds() {
|
|
534
|
+
if (this._commands.length === 0) {
|
|
535
|
+
return Rect.EMPTY;
|
|
536
|
+
}
|
|
537
|
+
let minX = Infinity;
|
|
538
|
+
let minY = Infinity;
|
|
539
|
+
let maxX = -Infinity;
|
|
540
|
+
let maxY = -Infinity;
|
|
541
|
+
const updateBounds = (x, y) => {
|
|
542
|
+
minX = Math.min(minX, x);
|
|
543
|
+
minY = Math.min(minY, y);
|
|
544
|
+
maxX = Math.max(maxX, x);
|
|
545
|
+
maxY = Math.max(maxY, y);
|
|
546
|
+
};
|
|
547
|
+
for (const cmd of this._commands) {
|
|
548
|
+
switch (cmd.cmd) {
|
|
549
|
+
case PathCmd.MoveTo:
|
|
550
|
+
case PathCmd.LineTo:
|
|
551
|
+
updateBounds(cmd.params[0], cmd.params[1]);
|
|
552
|
+
break;
|
|
553
|
+
case PathCmd.CurveTo:
|
|
554
|
+
updateBounds(cmd.params[0], cmd.params[1]);
|
|
555
|
+
updateBounds(cmd.params[2], cmd.params[3]);
|
|
556
|
+
updateBounds(cmd.params[4], cmd.params[5]);
|
|
557
|
+
break;
|
|
558
|
+
case PathCmd.QuadTo:
|
|
559
|
+
updateBounds(cmd.params[0], cmd.params[1]);
|
|
560
|
+
updateBounds(cmd.params[2], cmd.params[3]);
|
|
561
|
+
break;
|
|
562
|
+
case PathCmd.RectTo:
|
|
563
|
+
updateBounds(cmd.params[0], cmd.params[1]);
|
|
564
|
+
updateBounds(cmd.params[0] + cmd.params[2], cmd.params[1] + cmd.params[3]);
|
|
565
|
+
break;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
if (!isFinite(minX)) {
|
|
569
|
+
return Rect.EMPTY;
|
|
570
|
+
}
|
|
571
|
+
return new Rect(minX, minY, maxX, maxY);
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Transform the path by a matrix
|
|
575
|
+
*/
|
|
576
|
+
transform(matrix) {
|
|
577
|
+
const m = Matrix.from(matrix);
|
|
578
|
+
for (const cmd of this._commands) {
|
|
579
|
+
switch (cmd.cmd) {
|
|
580
|
+
case PathCmd.MoveTo:
|
|
581
|
+
case PathCmd.LineTo:
|
|
582
|
+
{
|
|
583
|
+
const p = m.transformPoint({ x: cmd.params[0], y: cmd.params[1] });
|
|
584
|
+
cmd.params[0] = p.x;
|
|
585
|
+
cmd.params[1] = p.y;
|
|
586
|
+
}
|
|
587
|
+
break;
|
|
588
|
+
case PathCmd.CurveTo:
|
|
589
|
+
{
|
|
590
|
+
const p1 = m.transformPoint({ x: cmd.params[0], y: cmd.params[1] });
|
|
591
|
+
const p2 = m.transformPoint({ x: cmd.params[2], y: cmd.params[3] });
|
|
592
|
+
const p3 = m.transformPoint({ x: cmd.params[4], y: cmd.params[5] });
|
|
593
|
+
cmd.params[0] = p1.x;
|
|
594
|
+
cmd.params[1] = p1.y;
|
|
595
|
+
cmd.params[2] = p2.x;
|
|
596
|
+
cmd.params[3] = p2.y;
|
|
597
|
+
cmd.params[4] = p3.x;
|
|
598
|
+
cmd.params[5] = p3.y;
|
|
599
|
+
}
|
|
600
|
+
break;
|
|
601
|
+
case PathCmd.QuadTo:
|
|
602
|
+
{
|
|
603
|
+
const p1 = m.transformPoint({ x: cmd.params[0], y: cmd.params[1] });
|
|
604
|
+
const p2 = m.transformPoint({ x: cmd.params[2], y: cmd.params[3] });
|
|
605
|
+
cmd.params[0] = p1.x;
|
|
606
|
+
cmd.params[1] = p1.y;
|
|
607
|
+
cmd.params[2] = p2.x;
|
|
608
|
+
cmd.params[3] = p2.y;
|
|
609
|
+
}
|
|
610
|
+
break;
|
|
611
|
+
case PathCmd.RectTo:
|
|
612
|
+
{
|
|
613
|
+
// Transform rectangle corners
|
|
614
|
+
const x1 = cmd.params[0];
|
|
615
|
+
const y1 = cmd.params[1];
|
|
616
|
+
const x2 = x1 + cmd.params[2];
|
|
617
|
+
const y2 = y1 + cmd.params[3];
|
|
618
|
+
const p1 = m.transformPoint({ x: x1, y: y1 });
|
|
619
|
+
const p2 = m.transformPoint({ x: x2, y: y2 });
|
|
620
|
+
cmd.params[0] = Math.min(p1.x, p2.x);
|
|
621
|
+
cmd.params[1] = Math.min(p1.y, p2.y);
|
|
622
|
+
cmd.params[2] = Math.abs(p2.x - p1.x);
|
|
623
|
+
cmd.params[3] = Math.abs(p2.y - p1.y);
|
|
624
|
+
}
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
// Update current point
|
|
629
|
+
if (this._commands.length > 0) {
|
|
630
|
+
const lastCmd = this._commands[this._commands.length - 1];
|
|
631
|
+
if (lastCmd.cmd === PathCmd.MoveTo || lastCmd.cmd === PathCmd.LineTo) {
|
|
632
|
+
this._currentPoint = new Point(lastCmd.params[0], lastCmd.params[1]);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
return this;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Walk the path commands
|
|
639
|
+
*/
|
|
640
|
+
walk(walker) {
|
|
641
|
+
for (const cmd of this._commands) {
|
|
642
|
+
switch (cmd.cmd) {
|
|
643
|
+
case PathCmd.MoveTo:
|
|
644
|
+
walker.moveTo?.(cmd.params[0], cmd.params[1]);
|
|
645
|
+
break;
|
|
646
|
+
case PathCmd.LineTo:
|
|
647
|
+
walker.lineTo?.(cmd.params[0], cmd.params[1]);
|
|
648
|
+
break;
|
|
649
|
+
case PathCmd.CurveTo:
|
|
650
|
+
walker.curveTo?.(cmd.params[0], cmd.params[1], cmd.params[2], cmd.params[3], cmd.params[4], cmd.params[5]);
|
|
651
|
+
break;
|
|
652
|
+
case PathCmd.QuadTo:
|
|
653
|
+
walker.quadTo?.(cmd.params[0], cmd.params[1], cmd.params[2], cmd.params[3]);
|
|
654
|
+
break;
|
|
655
|
+
case PathCmd.Close:
|
|
656
|
+
walker.closePath?.();
|
|
657
|
+
break;
|
|
658
|
+
case PathCmd.RectTo:
|
|
659
|
+
walker.rectTo?.(cmd.params[0], cmd.params[1], cmd.params[2], cmd.params[3]);
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Check if path is valid
|
|
666
|
+
*/
|
|
667
|
+
isValid() {
|
|
668
|
+
return this._commands.every((cmd) => {
|
|
669
|
+
return cmd.params.every((p) => isFinite(p));
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Get the number of commands in the path
|
|
674
|
+
*/
|
|
675
|
+
get length() {
|
|
676
|
+
return this._commands.length;
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Clear all commands from the path
|
|
680
|
+
*/
|
|
681
|
+
clear() {
|
|
682
|
+
this._commands = [];
|
|
683
|
+
this._currentPoint = Point.ORIGIN;
|
|
684
|
+
return this;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
//# sourceMappingURL=path.js.map
|