skia-canvas 0.9.27 → 0.9.28
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/CHANGELOG.md +195 -0
- package/README.md +124 -34
- package/lib/index.d.ts +231 -0
- package/lib/index.js +70 -32
- package/package.json +10 -8
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
<!-- ## 🥚 ⟩ [Unreleased] -->
|
|
4
|
+
|
|
5
|
+
## 📦 ⟩ [v0.9.28] ⟩ Jan 12, 2022
|
|
6
|
+
|
|
7
|
+
### New Features
|
|
8
|
+
- Added TypeScript definitions for extensions to the DOM spec (contributed by [@cprecioso](https://github.com/cprecioso))
|
|
9
|
+
- Added 3D-perspective transformations via the new [createProjection()](https://github.com/samizdatco/skia-canvas#createprojectionquad-basis) context method
|
|
10
|
+
- Colors can now use the [hwb()](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hwb()) model
|
|
11
|
+
|
|
12
|
+
### Breaking Changes
|
|
13
|
+
- The **Canvas** [`.async`](https://github.com/samizdatco/skia-canvas#async) property has been **deprecated** and will be removed in a future release.
|
|
14
|
+
- The `saveAs`, `toBuffer`, and `toDataURL` methods will now be async-only (likewise the [shorthand properties](https://github.com/samizdatco/skia-canvas#pdf-svg-jpg-and-png)).
|
|
15
|
+
- Use their synchronous counterparts (`saveAsSync`, `toBufferSync`, and `toDataURLSync`) if you want to block execution while exporting images.
|
|
16
|
+
- The [ImageData](https://developer.mozilla.org/en-US/docs/Web/API/ImageData/ImageData) constructor now orders its arguments properly: the optional buffer/array argument now comes first
|
|
17
|
+
|
|
18
|
+
### Bugfixes
|
|
19
|
+
- Fixed a stack overflow that was occurring when images became too deeply nested for the default deallocator to handle (primarily due to many thousands of image exports from the same canvas)
|
|
20
|
+
- The `source-in`, `source-out`, `destination-atop`, and `copy` composite operations now work correctly for paths rather than rendering shapes without color (contributed by [@meihuanyu](https://github.com/meihuanyu))
|
|
21
|
+
- Shape primitives now behave consistently with browsers when being added to a non-empty path:
|
|
22
|
+
- `rect()` now issues an initial `moveTo` rather than extending the path, then leaves the ‘current’ point in its upper left corner
|
|
23
|
+
- `ellipse()` extends the current path rather than implicitly closing it (contributed by [@meihuanyu](https://github.com/meihuanyu))
|
|
24
|
+
- `arc()` also extends the current path rather than closing it
|
|
25
|
+
|
|
26
|
+
### Misc. Improvements
|
|
27
|
+
- Upgraded Skia to milestone 96
|
|
28
|
+
- Added workflow for creating docker build environments
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
## 📦 ⟩ [v0.9.27] ⟩ Oct 23, 2021
|
|
32
|
+
|
|
33
|
+
### New Features
|
|
34
|
+
- Added pre-compiled binaries for Alpine Linux using the [musl](https://musl.libc.org) C library
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## 📦 ⟩ [v0.9.26] ⟩ Oct 18, 2021
|
|
38
|
+
|
|
39
|
+
### New Features
|
|
40
|
+
- Added pre-compiled binaries for 32-bit and 64-bit ARM on Linux (a.k.a. Raspberry Pi)
|
|
41
|
+
|
|
42
|
+
### Bugfixes
|
|
43
|
+
- Windows text rendering has been restored after failing due to changes involving the `icudtl.dat` file
|
|
44
|
+
- `FontLibrary.use` now reports an error if the specified font file doesn't exist
|
|
45
|
+
- Fixed a crash that could result from calling `measureText` with various unicode escapes
|
|
46
|
+
|
|
47
|
+
### Misc. Improvements
|
|
48
|
+
- Upgraded Skia to milestone 94
|
|
49
|
+
- Now embedding a more recent version of the FreeType library on Linux with support for more font formats
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## 📦 ⟩ [v0.9.25] ⟩ Aug 22, 2021
|
|
53
|
+
|
|
54
|
+
### Bugfixes
|
|
55
|
+
- Improved image scaling when a larger image is being shrunk down to a smaller size via [`drawImage()`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage)
|
|
56
|
+
- modified [`imageSmoothingQuality`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingQuality) settings to provide a more meaningful range across `low`, `medium`, and `high`
|
|
57
|
+
- [`measureText()`](https://github.com/samizdatco/skia-canvas#measuretextstr-width) now returns correct metrics regardless of current `textAlign` setting
|
|
58
|
+
- Rolled back `icudtl.dat` changes on Windows (which suppressed the misleading warning message but required running as Administrator)
|
|
59
|
+
|
|
60
|
+
### Misc. Improvements
|
|
61
|
+
- Now using [Neon](https://github.com/neon-bindings/neon) v0.9 (with enhanced async event scheduling)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
## 📦 ⟩ [v0.9.24] ⟩ Aug 18, 2021
|
|
65
|
+
|
|
66
|
+
### New Features
|
|
67
|
+
- **Path2D** objects now have a read/write [`d`](https://github.com/samizdatco/skia-canvas/#d) property with an [SVG representation](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#path_commands) of the path’s contours and an [`unwind()`](https://github.com/samizdatco/skia-canvas/#unwind) method for converting from even-odd to non-zero winding rules
|
|
68
|
+
- The [`createTexture()`](https://github.com/samizdatco/skia-canvas#createtexturespacing-path-line-color-angle-offset0) context method returns **CanvasTexture** objects which can be assigned to `fillStyle` or `strokeStyle`
|
|
69
|
+
- Textures draw either a parallel-lines pattern or one derived from the provided **Path2D** object and positioning parameters
|
|
70
|
+
- The marker used when `setLineDash` is active can now be customized by assigning a **Path2D** to the context’s [`lineDashMarker`](https://github.com/samizdatco/skia-canvas#linedashmarker) property (default dashing can be restored by assigning `null`)
|
|
71
|
+
- The marker’s orientation & shape relative to the path being stroked can be controlled by the [`lineDashFit`](https://github.com/samizdatco/skia-canvas#linedashfit) property which defaults to `"turn"` but can be set to `"move"` (which preserves orientation) or `"follow"` (which distorts the marker’s shape to match the contour)
|
|
72
|
+
|
|
73
|
+
### Bugfixes
|
|
74
|
+
|
|
75
|
+
- Removed use of the `??` operator which is unavailable prior to Node 14
|
|
76
|
+
- Prevented a spurious warning on windows incorrectly claiming that the `icudtl.dat` file could not be found
|
|
77
|
+
|
|
78
|
+
### Misc. Improvements
|
|
79
|
+
|
|
80
|
+
- The **Path2D** [`simplify()`](https://github.com/samizdatco/skia-canvas/#simplifyrulenonzero) method now takes an optional fill-rule argument
|
|
81
|
+
- Added support for versions of macOS starting with 10.13 (High Sierra)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
## 📦 ⟩ [v0.9.23] ⟩ Jul 12, 2021
|
|
85
|
+
|
|
86
|
+
### New Features
|
|
87
|
+
|
|
88
|
+
- [Conic béziers][conic_bezier] can now be drawn to the context or a Path2D with the [`conicCurveTo()`][conic_curveto] method
|
|
89
|
+
- Text can be converted to a Path2D using the context’s new [`outlineText()`][outline_text] method
|
|
90
|
+
- Path2D objects can now report back on their internal geometry with:
|
|
91
|
+
- the [`edges`][edges] property which contains an array of line-drawing commands describing the path’s individual contours
|
|
92
|
+
- the [`contains()`][contains] method which tests whether a given point is on/within the path
|
|
93
|
+
- the [`points()`][points] method which returns an array of `[x, y]` pairs at the requested spacing along the curve’s periphery
|
|
94
|
+
- A modified copy of a source Path2D can now be created using:
|
|
95
|
+
- [`offset()`][offset] or [`transform()`][transform] to shift position or apply a DOMMatrix respectively
|
|
96
|
+
- [`jitter()`][jitter] to break the path into smaller sections and apply random noise to the segments’ positions
|
|
97
|
+
- [`round()`][round] to round off every sharp corner in a path to a particular radius
|
|
98
|
+
- [`trim()`][trim] to select a percentage-based subsection of the path
|
|
99
|
+
- Two similar paths can be ‘tweened’ into a proportional combination of their coordinates using the [`interpolate()`][interpolate] method
|
|
100
|
+
|
|
101
|
+
### Bugfixes
|
|
102
|
+
|
|
103
|
+
- Passing a Path2D argument to the `fill()` or `stroke()` method no longer disturbs the context’s ‘current’ path (if one has been created using `beginPath()`)
|
|
104
|
+
- The `filter` property will now accept percentage values greater than 999%
|
|
105
|
+
|
|
106
|
+
### Misc. Improvements
|
|
107
|
+
|
|
108
|
+
- The `newPage()` and `saveAs()` methods now work in the browser, including the ability to save image sequences to a zip archive. The browser’s canvas is still doing all the drawing however, so file export formats will be limited to PNG and JPEG and none of the other Skia-specific extensions will be available.
|
|
109
|
+
- The file-export methods now accept a [`matte`][matte] value in their options object which can be used to set the background color for any portions of the canvas that were left semi-transparent
|
|
110
|
+
- Canvas dimensions are no longer rounded-off to integer values (at least until a bitmap needs to be generated for export)
|
|
111
|
+
- Linux builds will now run on some older systems going back to glibc 2.24
|
|
112
|
+
|
|
113
|
+
[conic_bezier]: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/curves/beziers#the-conic-bézier-curve
|
|
114
|
+
[conic_curveto]: https://github.com/samizdatco/skia-canvas#coniccurvetocpx-cpy-x-y-weight
|
|
115
|
+
[outline_text]: https://github.com/samizdatco/skia-canvas#outlinetextstr
|
|
116
|
+
[matte]: https://github.com/samizdatco/skia-canvas#matte
|
|
117
|
+
|
|
118
|
+
[edges]: https://github.com/samizdatco/skia-canvas#edges
|
|
119
|
+
[contains]: https://github.com/samizdatco/skia-canvas#containsx-y
|
|
120
|
+
[points]: https://github.com/samizdatco/skia-canvas#pointsstep1
|
|
121
|
+
[offset]: https://github.com/samizdatco/skia-canvas#offsetdx-dy
|
|
122
|
+
[transform]: https://github.com/samizdatco/skia-canvas#transformmatrix-or-transforma-b-c-d-e-f
|
|
123
|
+
|
|
124
|
+
[interpolate]: https://github.com/samizdatco/skia-canvas#interpolateotherpath-weight
|
|
125
|
+
[jitter]: https://github.com/samizdatco/skia-canvas#jittersegmentlength-amount-seed0
|
|
126
|
+
[round]: https://github.com/samizdatco/skia-canvas#roundradius
|
|
127
|
+
[simplify]: https://github.com/samizdatco/skia-canvas#simplify
|
|
128
|
+
[trim]: https://github.com/samizdatco/skia-canvas#trimstart-end-inverted
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
## 📦 ⟩ [v0.9.22] ⟩ Jun 09, 2021
|
|
132
|
+
|
|
133
|
+
### New Features
|
|
134
|
+
|
|
135
|
+
- Rasterization and file i/o are now handled asynchronously in a background thread. See the discussion of Canvas’s new [`async`](https://github.com/samizdatco/skia-canvas#async) property for details.
|
|
136
|
+
- Output files can now be generated at pixel-ratios > 1 for High-DPI screens. `SaveAs` and the other canvas output functions all accept an optional [`density`](https://github.com/samizdatco/skia-canvas#density) argument which is an integer ≥1 and will upscale the image accordingly. The density can also be passed using the `filename` argument by ending the name with an ‘@’ suffix like `some-image@2x.png`.
|
|
137
|
+
- SVG exports can optionally convert text to paths by setting the [`outline`](https://github.com/samizdatco/skia-canvas#outline) argument to `true`.
|
|
138
|
+
|
|
139
|
+
### Breaking Changes
|
|
140
|
+
|
|
141
|
+
- The canvas functions dealing with rasterization (`toBuffer`, `toDataURL`, `png`, `jpg`, `pdf`, and `svg`) and file i/o (`saveAs`) are now asynchronous and return `Promise` objects. The old, synchronous behavior is still available on a canvas-by-canvas basis by setting its `async` property to `false`.
|
|
142
|
+
- The optional `quality` argument accepted by the output methods is now a float in the range 0–1 rather than an integer from 0–100. This is consistent with the [encoderOptions](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL) arg in the spec. Quality now defaults to 0.92 (again, as per the spec) rather than lossless.
|
|
143
|
+
|
|
144
|
+
### Bugfixes
|
|
145
|
+
|
|
146
|
+
- `measureText` was reporting zero when asked to measure a string that was entirely made of whitespace. This is still the case for ‘blank‘ lines when `textWrap` is set to `true` but in the default, single-line mode the metrics will now report the width of the whitespace.
|
|
147
|
+
- Changed the way text rendering was staged so that SVG exports didn’t *entirely omit(!)* text from their output. As a result, `Context2D`s now use an external `Typesetter` struct to manage layout and rendering.
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
## 📦 ⟩ [v0.9.21] ⟩ May 22, 2021
|
|
151
|
+
|
|
152
|
+
### New Features
|
|
153
|
+
- Now runs on Windows and Apple Silicon Macs.
|
|
154
|
+
- Precompiled binaries support Node 10, 12, 14+.
|
|
155
|
+
- Image objects can be initialized from PNG, JPEG, GIF, BMP, or ICO data.
|
|
156
|
+
- Path2D objects can now be combined using [boolean operators](https://github.com/samizdatco/skia-canvas/#complement-difference-intersect-union-and-xor) and can measure their own [bounding boxes](https://github.com/samizdatco/skia-canvas/#bounds).
|
|
157
|
+
- Context objects now support [`createConicGradient()`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createConicGradient).
|
|
158
|
+
- Image objects now return a promise from their [`decode()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode) method allowing for async loading without the [`loadImage`](https://github.com/samizdatco/skia-canvas/#loadimage) helper.
|
|
159
|
+
|
|
160
|
+
### Bugfixes
|
|
161
|
+
- Calling `drawImage` with a `Canvas` object as the argument now uses a Skia `Pict` rather than a `Drawable` as the interchange format, meaning it can actually respect the canvas's current `globalAlpha` and `globalCompositeOperation` state (fixed #6).
|
|
162
|
+
- Improved some spurious error messages when trying to generate a graphics file from a canvas whose width and/or height was set to zero (fixed #5).
|
|
163
|
+
- `CanvasPattern`s now respect the `imageSmoothingEnabled` setting
|
|
164
|
+
- The `counterclockwise` arg to `ellipse` and `arc` is now correctly treated as optional.
|
|
165
|
+
|
|
166
|
+
### Misc. Improvements
|
|
167
|
+
- Made the `console.log` representations of the canvas-related objects friendlier.
|
|
168
|
+
- Added new test suites for `Path2D`, `Image`, and `Canvas`’s format support.
|
|
169
|
+
- Created [workflows](https://github.com/samizdatco/skia-canvas/tree/master/.github/workflows) to automate precompiled binary builds, testing, and npm package updating.
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
## 📦 ⟩ [v0.9.20] ⟩ Mar 27, 2021
|
|
173
|
+
|
|
174
|
+
### Bugfixes
|
|
175
|
+
- The `loadImage` helper can now handle `Buffer` arguments
|
|
176
|
+
|
|
177
|
+
### Misc. Improvements
|
|
178
|
+
- Improved documentation of compilation steps and use of line height with `ctx.font`
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
## 📦 ⟩ [v0.9.19] ⟩ Aug 30, 2020
|
|
182
|
+
|
|
183
|
+
**Initial public release** 🎉
|
|
184
|
+
|
|
185
|
+
[unreleased]: https://github.com/samizdatco/skia-canvas/compare/v0.9.28...HEAD
|
|
186
|
+
[v0.9.28]: https://github.com/samizdatco/skia-canvas/compare/v0.9.27...v0.9.28
|
|
187
|
+
[v0.9.27]: https://github.com/samizdatco/skia-canvas/compare/v0.9.26...v0.9.27
|
|
188
|
+
[v0.9.26]: https://github.com/samizdatco/skia-canvas/compare/v0.9.25...v0.9.26
|
|
189
|
+
[v0.9.25]: https://github.com/samizdatco/skia-canvas/compare/v0.9.24...v0.9.25
|
|
190
|
+
[v0.9.24]: https://github.com/samizdatco/skia-canvas/compare/v0.9.23...v0.9.24
|
|
191
|
+
[v0.9.23]: https://github.com/samizdatco/skia-canvas/compare/v0.9.22...v0.9.23
|
|
192
|
+
[v0.9.22]: https://github.com/samizdatco/skia-canvas/compare/v0.9.21...v0.9.22
|
|
193
|
+
[v0.9.21]: https://github.com/samizdatco/skia-canvas/compare/v0.9.20...v0.9.21
|
|
194
|
+
[v0.9.20]: https://github.com/samizdatco/skia-canvas/compare/v0.9.19...v0.9.20
|
|
195
|
+
[v0.9.19]: https://github.com/samizdatco/skia-canvas/compare/v0.9.15...v0.9.19
|
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ In particular, Skia Canvas:
|
|
|
12
12
|
- uses native threads and [channels](https://docs.rs/neon/0.9.0/neon/event/struct.Channel.html) for asynchronous rendering and file I/O
|
|
13
13
|
- can create [multiple ‘pages’][newPage] on a given canvas and then [output][saveAs] them as a single, multi-page PDF or an image-sequence saved to multiple files
|
|
14
14
|
- can [simplify][p2d_simplify], [blunt][p2d_round], [combine][bool-ops], [excerpt][p2d_trim], and [atomize][p2d_points] bézier paths using [efficient](https://www.youtube.com/watch?v=OmfliNQsk88) boolean operations or point-by-point [interpolation][p2d_interpolate]
|
|
15
|
+
- can apply [3D perspective][createProjection()] transformations in addition to [scaling][scale()], [rotation][rotate()], and [translation][translate()]
|
|
15
16
|
- can fill shapes with vector-based [Textures][createTexture()] in addition to bitmap-based [Patterns][createPattern()] and supports line-drawing with custom [markers][lineDashMarker]
|
|
16
17
|
- fully supports the [CSS filter effects][filter] image processing operators
|
|
17
18
|
- offers rich typographic control including:
|
|
@@ -122,15 +123,14 @@ async function render(){
|
|
|
122
123
|
// save the graphic...
|
|
123
124
|
await canvas.saveAs("pilcrow.png")
|
|
124
125
|
// ...or use a shorthand for canvas.toBuffer("png")
|
|
125
|
-
|
|
126
|
+
let pngData = await canvas.png
|
|
126
127
|
// ...or embed it in a string
|
|
127
128
|
console.log(`<img src="${await canvas.toDataURL("png")}">`)
|
|
128
129
|
}
|
|
129
130
|
render()
|
|
130
131
|
|
|
131
|
-
// ...or
|
|
132
|
-
canvas.
|
|
133
|
-
canvas.saveAs("pilcrow.png")
|
|
132
|
+
// ...or save the file synchronously from the main thread
|
|
133
|
+
canvas.saveAsSync("pilcrow.png")
|
|
134
134
|
```
|
|
135
135
|
|
|
136
136
|
|
|
@@ -163,22 +163,22 @@ The Canvas object is a stand-in for the HTML `<canvas>` element. It defines imag
|
|
|
163
163
|
|
|
164
164
|
| Image Dimensions | Rendering Contexts | Output |
|
|
165
165
|
| -- | -- | -- |
|
|
166
|
-
| [**width**][canvas_width] | [**pages**][canvas_pages] ⚡ | [**async**][canvas_async] ⚡ |
|
|
166
|
+
| [**width**][canvas_width] | [**pages**][canvas_pages] ⚡ | ~~[**async**][canvas_async]~~ ⚡ |
|
|
167
167
|
| [**height**][canvas_height] | [getContext()][getContext] | [**pdf**, **png**, **svg**, **jpg**][shorthands] ⚡ |
|
|
168
|
-
| | [newPage()][newPage] ⚡ | [saveAs()][saveAs] ⚡ |
|
|
169
|
-
| | | [toBuffer()][toBuffer] ⚡ |
|
|
170
|
-
| | | [toDataURL()][
|
|
168
|
+
| | [newPage()][newPage] ⚡ | [saveAs()][saveAs] / [saveAsSync()][saveAs] ⚡ |
|
|
169
|
+
| | | [toBuffer()][toBuffer] / [toBufferSync()][toBuffer] ⚡ |
|
|
170
|
+
| | | [toDataURL()][toDataURL_ext] / [toDataURLSync()][toDataURL_ext] ⚡ |
|
|
171
171
|
|
|
172
172
|
[canvas_width]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/width
|
|
173
173
|
[canvas_height]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/height
|
|
174
174
|
[canvas_async]: #async
|
|
175
175
|
[canvas_pages]: #pages
|
|
176
176
|
[getContext]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext
|
|
177
|
-
[saveAs]: #saveasfilename-page-format-density1-quality092-outlinefalse
|
|
178
|
-
[toBuffer]: #tobufferformat-page-density-quality-outline
|
|
177
|
+
[saveAs]: #saveasfilename-page-format-matte-density1-quality092-outlinefalse
|
|
178
|
+
[toBuffer]: #tobufferformat-page-matte-density-quality-outline
|
|
179
179
|
[newPage]: #newpagewidth-height
|
|
180
180
|
[toDataURL_mdn]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
|
|
181
|
-
[toDataURL_ext]: #todataurlformat-page-density-quality-outline
|
|
181
|
+
[toDataURL_ext]: #todataurlformat-page-matte-density-quality-outline
|
|
182
182
|
[shorthands]: #pdf-svg-jpg-and-png
|
|
183
183
|
|
|
184
184
|
#### Creating new `Canvas` objects
|
|
@@ -190,21 +190,24 @@ let defaultCanvas = new Canvas() // without arguments, defaults to 300 × 150 px
|
|
|
190
190
|
let squareCanvas = new Canvas(512, 512) // creates a 512 px square
|
|
191
191
|
```
|
|
192
192
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
#### `.async`
|
|
193
|
+
#### Saving graphics to files, buffers, and strings
|
|
196
194
|
|
|
197
|
-
When the canvas renders images and writes them to disk, it does so in a background thread so as not to block execution within your script. As a result you’ll generally want to deal with the canvas from within an `async` function and be sure to use the `await` keyword when accessing any of its output methods or shorthand properties:
|
|
195
|
+
When the canvas renders images and writes them to disk, it does so in a background thread so as not to block execution within your script. As a result you’ll generally want to deal with the canvas from within an `async` function and be sure to use the `await` keyword when accessing any of its output methods or shorthand properties (all of which return Promises):
|
|
198
196
|
- [`saveAs()`][saveAs]
|
|
199
197
|
- [`toBuffer()`][toBuffer]
|
|
200
198
|
- [`toDataURL()`][toDataURL_ext]
|
|
201
199
|
- [`.pdf`, `.svg`, `.jpg`, and `.png`][shorthands]
|
|
202
200
|
|
|
203
|
-
In cases where this is not the desired behavior, you can switch these methods into a synchronous mode for a particular canvas by setting its `async` property to `false`. For instance, both of the example functions below will generate PNG & PDF from the canvas, though the first will be more efficient (particularly for parallel contexts like request-handlers in an HTTP server or batch exports):
|
|
204
|
-
```js
|
|
205
201
|
|
|
202
|
+
In cases where this is not the desired behavior, you can use the synchronous equivalents for the primary export functions. They accept identical arguments to their async versions but block execution and return their values synchronously rather than wrapped in Promises. Also note that the [shorthand properties][shorthands] do not have synchronous versions:
|
|
203
|
+
- [`saveAsSync()`][saveAs]
|
|
204
|
+
- [`toBufferSync()`][toBuffer]
|
|
205
|
+
- [`toDataURLSync()`][toDataURL_ext]
|
|
206
|
+
|
|
207
|
+
For instance, both of the example functions below will generate PNG & PDF from the canvas, though the first will be more efficient (particularly for parallel contexts like request-handlers in an HTTP server or batch exports):
|
|
208
|
+
|
|
209
|
+
```js
|
|
206
210
|
let canvas = new Canvas()
|
|
207
|
-
console.log(canvas.async) // -> true by default
|
|
208
211
|
|
|
209
212
|
async function normal(){
|
|
210
213
|
let pngURL = await canvas.toDataURL("png")
|
|
@@ -212,14 +215,16 @@ async function normal(){
|
|
|
212
215
|
}
|
|
213
216
|
|
|
214
217
|
function synchronous(){
|
|
215
|
-
|
|
216
|
-
let
|
|
217
|
-
let pdfBuffer = canvas.pdf
|
|
218
|
+
let pngURL = canvas.toDataURLSync("png")
|
|
219
|
+
let pdfBuffer = canvas.toBufferSync("pdf")
|
|
218
220
|
}
|
|
219
221
|
```
|
|
220
222
|
|
|
223
|
+
##### PROPERTIES
|
|
221
224
|
|
|
225
|
+
#### ~~`.async`~~
|
|
222
226
|
|
|
227
|
+
**The async property has been deprecated** and will be removed in a future release. Use the [`saveAsSync()`][saveAs], [`toBufferSync()`][toBuffer], and [`toDataURLSync()`][toDataURL_ext] methods if the default, asynchronous versions aren't to your liking.
|
|
223
228
|
|
|
224
229
|
#### `.pages`
|
|
225
230
|
|
|
@@ -248,6 +253,10 @@ An integer can optionally be placed between the braces to indicate the number of
|
|
|
248
253
|
##### page
|
|
249
254
|
The optional `page` argument accepts an integer that allows for the individual selection of pages in a multi-page canvas. Note that page indexing starts with page 1 **not** 0. The page value can also be negative, counting from the end of the canvas’s `.pages` array. For instance, `.saveAs("currentPage.png", {page:-1})` is equivalent to omitting `page` since they both yield the canvas’s most recently added page.
|
|
250
255
|
|
|
256
|
+
##### format
|
|
257
|
+
|
|
258
|
+
The image format to generate, specified either as a mime-type string or file extension. The `format` argument will take precedence over the type specified through the `filename` argument’s extension, but is primarily useful when generating a file whose name cannot end with an extension for other reasons.
|
|
259
|
+
|
|
251
260
|
##### matte
|
|
252
261
|
The optional `matte` argument accepts a color-string specifying the background that should be drawn *behind* the canvas in the exported image. Any transparent portions of the image will be filled with the matte color.
|
|
253
262
|
|
|
@@ -265,11 +274,11 @@ The `quality` option is a number between 0 and 1.0 that controls the level of JP
|
|
|
265
274
|
##### outline
|
|
266
275
|
When generating SVG output containing text, you have two options for how to handle the fonts that were used. By default, SVG files will contain `<text>` elements that refer to the fonts by name in the embedded stylesheet. This requires that viewers of the SVG have the same fonts available on their system (or accessible as webfonts). Setting the optional `outline` argument to `true` will trace all the letterforms and ‘burn’ them into the file as bézier paths. This will result in a much larger file (and one in which the original text strings will be unrecoverable), but it will be viewable regardless of the specifics of the system it’s displayed on.
|
|
267
276
|
|
|
268
|
-
#### `toBuffer(format, {page, density, quality, outline})`
|
|
277
|
+
#### `toBuffer(format, {page, matte, density, quality, outline})`
|
|
269
278
|
|
|
270
279
|
Node [`Buffer`][Buffer] objects containing various image formats can be created by passing either a format string like `"svg"` or a mime-type like `"image/svg+xml"`. An ‘@’ suffix can be added to the format string to specify a pixel-density (for instance, `"jpg@2x"`). The optional arguments behave the same as in the `saveAs` method.
|
|
271
280
|
|
|
272
|
-
#### `toDataURL(format, {page, density, quality, outline})`
|
|
281
|
+
#### `toDataURL(format, {page, matte, density, quality, outline})`
|
|
273
282
|
|
|
274
283
|
This method accepts the same arguments and behaves similarly to `.toBuffer`. However instead of returning a Buffer, it returns a string of the form `"data:<mime-type>;base64,<image-data>"` which can be used as a `src` attribute in `<img>` tags, embedded into CSS, etc.
|
|
275
284
|
|
|
@@ -279,17 +288,32 @@ This method accepts the same arguments and behaves similarly to `.toBuffer`. How
|
|
|
279
288
|
Most of your interaction with the canvas will actually be directed toward its ‘rendering context’, a supporting object you can acquire by calling the canvas’s [getContext()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext) and [newPage()][newPage] methods.
|
|
280
289
|
|
|
281
290
|
|
|
282
|
-
| Canvas State | Drawing | Pattern & Color | Line Style | Transform
|
|
283
|
-
|
|
284
|
-
| [**canvas**][canvas_attr] ⧸[⚡](#canvas) | [clearRect()][clearRect()] | [**fillStyle**][fillStyle] | [**lineCap**][lineCap] | [**currentTransform**][currentTransform]
|
|
285
|
-
| [beginPath()][beginPath()] | [fillRect()][fillRect()] | [**strokeStyle**][strokeStyle] | [**lineDashFit** ⚡][lineDashFit] | [
|
|
286
|
-
| [isPointInPath()][isPointInPath()] | [strokeRect()][strokeRect()] | [createConicGradient()][createConicGradient()] | [**lineDashMarker** ⚡][lineDashMarker] | [
|
|
287
|
-
| [isPointInStroke()][isPointInStroke()] | [fillText()][fillText()] ⧸[⚡][drawText] | [createLinearGradient()][createLinearGradient()] | [**lineDashOffset**][lineDashOffset] | [
|
|
288
|
-
| [save()][save()] | [strokeText()][strokeText()] ⧸[⚡][drawText] | [createRadialGradient()][createRadialGradient()] | [**lineJoin**][lineJoin] | [
|
|
289
|
-
| [restore()][restore()] | [fill()][fill()] | [createPattern()][createPattern()] | [**lineWidth**][lineWidth] | [
|
|
290
|
-
| [clip()][clip()] | [stroke()][stroke()] | [createTexture() ⚡][createTexture()] | [**miterLimit**][miterLimit] | [
|
|
291
|
-
| | | | [getLineDash()][getLineDash()] | [
|
|
292
|
-
| | | | [setLineDash()][setLineDash()] |
|
|
291
|
+
| Canvas State | Drawing | Pattern & Color | Line Style | Transform |
|
|
292
|
+
|------------------------------------------|----------------------------------------------|---------------------------------------------------|-----------------------------------------|---------------------------------------------|
|
|
293
|
+
| [**canvas**][canvas_attr] ⧸[⚡](#canvas) | [clearRect()][clearRect()] | [**fillStyle**][fillStyle] | [**lineCap**][lineCap] | [**currentTransform**][currentTransform] |
|
|
294
|
+
| [beginPath()][beginPath()] | [fillRect()][fillRect()] | [**strokeStyle**][strokeStyle] | [**lineDashFit** ⚡][lineDashFit] | [createProjection() ⚡][createProjection()] |
|
|
295
|
+
| [isPointInPath()][isPointInPath()] | [strokeRect()][strokeRect()] | [createConicGradient()][createConicGradient()] | [**lineDashMarker** ⚡][lineDashMarker] | [getTransform()][getTransform()] |
|
|
296
|
+
| [isPointInStroke()][isPointInStroke()] | [fillText()][fillText()] ⧸[⚡][drawText] | [createLinearGradient()][createLinearGradient()] | [**lineDashOffset**][lineDashOffset] | [setTransform()][setTransform()] |
|
|
297
|
+
| [save()][save()] | [strokeText()][strokeText()] ⧸[⚡][drawText] | [createRadialGradient()][createRadialGradient()] | [**lineJoin**][lineJoin] | [resetTransform()][resetTransform()] |
|
|
298
|
+
| [restore()][restore()] | [fill()][fill()] | [createPattern()][createPattern()] | [**lineWidth**][lineWidth] | [transform()][transform()] |
|
|
299
|
+
| [clip()][clip()] | [stroke()][stroke()] | [createTexture() ⚡][createTexture()] | [**miterLimit**][miterLimit] | [translate()][translate()] |
|
|
300
|
+
| | | | [getLineDash()][getLineDash()] | [rotate()][rotate()] |
|
|
301
|
+
| | | | [setLineDash()][setLineDash()] | [scale()][scale()] |
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
|
|
293
317
|
|
|
294
318
|
|
|
295
319
|
| Bezier Paths | Typography | Images | Compositing Effects |
|
|
@@ -389,6 +413,71 @@ The `lineDashFit` attribute can be set to `"move"`, `"turn"`, or `"follow"` and
|
|
|
389
413
|
|
|
390
414
|
Adds a line segment connecting the current point to (*x, y*) but curving toward the control point (*cpx, cpy*) along the way. The `weight` argument controls how close the curve will come to the control point. If the weight is `0`, the result will be a straight line from the current point to (*x, y*). With a weight of `1.0`, the function is equivalent to calling `quadraticCurveTo()`. Weights greater than `1.0` will pull the line segment ever closer to the control point.
|
|
391
415
|
|
|
416
|
+
#### `createProjection(quad, [basis])`
|
|
417
|
+
|
|
418
|
+
This method returns a [DOMMatrix][DOMMatrix] object which can be used to simulate perspective effects or other distortions in which the four corners of the canvas are mapped to an arbitrary quadrilateral (four sided polygon). The matrix must be passed to the context's [setTransform][setTransform()] method for it take effect.
|
|
419
|
+
|
|
420
|
+
##### `quad`
|
|
421
|
+
|
|
422
|
+
The `quad` argument defines the **target** of the transformation. It specifies four points that establish where the four corners of the source coordinate space will be positioned within the viewport. If these points form a polygon other than a rectangle, lines drawn along the x & y axes of the source space will no longer be perpendicular—trapezoids allow for ‘vanishing point’ effects and parallelograms create ‘skew’.
|
|
423
|
+
|
|
424
|
+
The geometry of the quadrilateral should be described as an Array of either 8 or 4 numbers specifying an arbitrary polygon or rectangle respectively:
|
|
425
|
+
|
|
426
|
+
```js
|
|
427
|
+
[x1, y1, x2, y2, x3, y3, x4, y4] // four corner points
|
|
428
|
+
[left, top, right, bottom] // four edges of a rectangle
|
|
429
|
+
|
|
430
|
+
// internal arrays for grouping are also allowed
|
|
431
|
+
[[x1, y1], [x2, y2], [x3, y3], [x4, y4]]
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
##### `basis`
|
|
435
|
+
|
|
436
|
+
The optional `basis` argument defines the **source** quadrilateral whose corners will be mapped to the positions defined by `quad`. If no `basis` is specified, the canvas's bounding box will be used (i.e., the rectangle from ⟨`0`, `0`⟩ to ⟨`canvas.width`, `canvas.height`⟩). Note that drawing commands that go outside of the `basis` region may well be visible—it only establishes the geometry of the projection, not the [clipping][clip()] path.
|
|
437
|
+
|
|
438
|
+
The `basis` polygon can be described using 2, 4, or 8 numbers, using the canvas dimensions to fill in the unspecified coordinates:
|
|
439
|
+
```js
|
|
440
|
+
[width, height] // rectangle from ⟨0, 0⟩ to ⟨width, height⟩
|
|
441
|
+
[left, top, right, bottom] // four edges of a rectangle
|
|
442
|
+
[x1, y1, x2, y2, x3, y3, x4, y4] // four corner points
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
----
|
|
446
|
+
|
|
447
|
+
The projection matrix will apply to all types of drawing: shapes, images, and text. This example transforms a white box and red `"@"` character into a trapezoid bounded by the vertical midline of the canvas and its left and right edges. Since no `basis` argument is provided, it will default to using the current canvas bounds as the rectangle to be mapped onto that trapezoid.
|
|
448
|
+
|
|
449
|
+
```js
|
|
450
|
+
let canvas = new Canvas(512, 512),
|
|
451
|
+
ctx = canvas.getContext("2d"),
|
|
452
|
+
{width:w, height:h} = canvas;
|
|
453
|
+
ctx.font = '900 480px Times'
|
|
454
|
+
ctx.textAlign = 'center'
|
|
455
|
+
ctx.fillStyle = '#aaa'
|
|
456
|
+
ctx.fillRect(0, 0, w, h)
|
|
457
|
+
|
|
458
|
+
let quad = [
|
|
459
|
+
w*.33, h/2, // upper left
|
|
460
|
+
w*.66, h/2, // upper right
|
|
461
|
+
w, h*.9, // bottom right
|
|
462
|
+
0, h*.9, // bottom left
|
|
463
|
+
]
|
|
464
|
+
|
|
465
|
+
let matrix = ctx.createProjection(quad) // use default basis
|
|
466
|
+
ctx.setTransform(matrix)
|
|
467
|
+
|
|
468
|
+
ctx.fillStyle = 'white'
|
|
469
|
+
ctx.fillRect(10, 10, w-20, h-20)
|
|
470
|
+
|
|
471
|
+
ctx.fillStyle = '#900'
|
|
472
|
+
ctx.fillText("@", w/2, h-40)
|
|
473
|
+
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
The results below show the image generated when the `createProjection()` call is omitted entirely, called (as above) with just a `quad` argument, or called with two different values for the optional `basis` argument:
|
|
477
|
+
|
|
478
|
+

|
|
479
|
+
|
|
480
|
+
|
|
392
481
|
#### `createTexture(spacing, {path, line, color, angle, offset=0})`
|
|
393
482
|
|
|
394
483
|
The `createTexture()` method returns a `CanvasTexture` object that can be assigned to the context’s `strokeStyle` or `fillStyle` property. Similar to a `CanvasPattern`, a `CanvasTexture` defines a repeating pattern that will be drawn instead of a flat color, but textures define their content using *vectors* rather than bitmaps.
|
|
@@ -830,6 +919,7 @@ Many thanks to the [`node-canvas`](https://github.com/Automattic/node-canvas) de
|
|
|
830
919
|
[conicCurveTo]: #coniccurvetocpx-cpy-x-y-weight
|
|
831
920
|
[outlineText()]: #outlinetextstr
|
|
832
921
|
[createTexture()]: #createtexturespacing-path-line-color-angle-offset0
|
|
922
|
+
[createProjection()]: #createprojectionquad-basis
|
|
833
923
|
[lineDashMarker]: #linedashmarker
|
|
834
924
|
[lineDashFit]: #linedashfit
|
|
835
925
|
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/// <reference lib="dom"/>
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
|
|
4
|
+
export function loadImage(src: string | Buffer): Promise<Image>
|
|
5
|
+
export class DOMMatrix extends globalThis.DOMMatrix {}
|
|
6
|
+
export class DOMPoint extends globalThis.DOMPoint {}
|
|
7
|
+
export class DOMRect extends globalThis.DOMRect {}
|
|
8
|
+
export class Image extends globalThis.Image {}
|
|
9
|
+
export class ImageData extends globalThis.ImageData {}
|
|
10
|
+
export class CanvasGradient extends globalThis.CanvasGradient {}
|
|
11
|
+
export class CanvasPattern extends globalThis.CanvasPattern {}
|
|
12
|
+
export class CanvasTexture {}
|
|
13
|
+
|
|
14
|
+
//
|
|
15
|
+
// Canvas
|
|
16
|
+
//
|
|
17
|
+
|
|
18
|
+
export type ExportFormat = "png" | "jpg" | "jpeg" | "pdf" | "svg";
|
|
19
|
+
|
|
20
|
+
export interface RenderOptions {
|
|
21
|
+
/** Page to export: Defaults to 1 (i.e., first page) */
|
|
22
|
+
page?: number
|
|
23
|
+
|
|
24
|
+
/** Background color to draw beneath transparent parts of the canvas */
|
|
25
|
+
matte?: string
|
|
26
|
+
|
|
27
|
+
/** Number of pixels per grid ‘point’ (defaults to 1) */
|
|
28
|
+
density?: number
|
|
29
|
+
|
|
30
|
+
/** Quality for lossy encodings like JPEG (0.0–1.0) */
|
|
31
|
+
quality?: number
|
|
32
|
+
|
|
33
|
+
/** Convert text to paths for SVG exports */
|
|
34
|
+
outline?: boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface SaveOptions extends RenderOptions {
|
|
38
|
+
/** Image format to use */
|
|
39
|
+
format?: ExportFormat
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class Canvas {
|
|
43
|
+
/** @internal */
|
|
44
|
+
constructor(width?: number, height?: number)
|
|
45
|
+
static contexts: WeakMap<Canvas, readonly CanvasRenderingContext2D[]>
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @deprecated Use the saveAsSync, toBufferSync, and toDataURLSync methods
|
|
49
|
+
* instead of setting the async property to false
|
|
50
|
+
*/
|
|
51
|
+
async: boolean
|
|
52
|
+
width: number
|
|
53
|
+
height: number
|
|
54
|
+
|
|
55
|
+
getContext(type?: "2d"): CanvasRenderingContext2D
|
|
56
|
+
newPage(width?: number, height?: number): CanvasRenderingContext2D
|
|
57
|
+
readonly pages: CanvasRenderingContext2D[]
|
|
58
|
+
|
|
59
|
+
saveAs(filename: string, options?: SaveOptions): Promise<void>
|
|
60
|
+
toBuffer(format: ExportFormat, options?: RenderOptions): Promise<Buffer>
|
|
61
|
+
toDataURL(format: ExportFormat, options?: RenderOptions): Promise<string>
|
|
62
|
+
|
|
63
|
+
saveAsSync(filename: string, options?: SaveOptions): void
|
|
64
|
+
toBufferSync(format: ExportFormat, options?: RenderOptions): Buffer
|
|
65
|
+
toDataURLSync(format: ExportFormat, options?: RenderOptions): string
|
|
66
|
+
|
|
67
|
+
get pdf(): Promise<Buffer>
|
|
68
|
+
get svg(): Promise<Buffer>
|
|
69
|
+
get jpg(): Promise<Buffer>
|
|
70
|
+
get png(): Promise<Buffer>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
//
|
|
74
|
+
// Context
|
|
75
|
+
//
|
|
76
|
+
|
|
77
|
+
type Offset = [x: number, y: number] | number
|
|
78
|
+
|
|
79
|
+
export interface CreateTextureOptions {
|
|
80
|
+
/** The 2D shape to be drawn in a repeating grid with the specified spacing (if omitted, parallel lines will be used) */
|
|
81
|
+
path?: Path2D
|
|
82
|
+
|
|
83
|
+
/** The lineWidth with which to stroke the path (if omitted, the path will be filled instead) */
|
|
84
|
+
line?: number
|
|
85
|
+
|
|
86
|
+
/** The color to use for stroking/filling the path */
|
|
87
|
+
color?: string
|
|
88
|
+
|
|
89
|
+
/** The orientation of the pattern grid in radians */
|
|
90
|
+
angle?: number
|
|
91
|
+
|
|
92
|
+
/** The amount by which to shift the pattern relative to the canvas origin */
|
|
93
|
+
offset?: Offset
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export type CanvasImageSource = Canvas | Image;
|
|
97
|
+
|
|
98
|
+
interface CanvasDrawImage {
|
|
99
|
+
drawImage(image: CanvasImageSource, dx: number, dy: number): void;
|
|
100
|
+
drawImage(image: CanvasImageSource, dx: number, dy: number, dw: number, dh: number): void;
|
|
101
|
+
drawImage(image: CanvasImageSource, sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number): void;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
interface CanvasFillStrokeStyles {
|
|
105
|
+
fillStyle: string | CanvasGradient | CanvasPattern | CanvasTexture;
|
|
106
|
+
strokeStyle: string | CanvasGradient | CanvasPattern | CanvasTexture;
|
|
107
|
+
createConicGradient(startAngle: number, x: number, y: number): CanvasGradient;
|
|
108
|
+
createLinearGradient(x0: number, y0: number, x1: number, y1: number): CanvasGradient;
|
|
109
|
+
createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): CanvasGradient;
|
|
110
|
+
createPattern(image: CanvasImageSource, repetition: string | null): CanvasPattern | null;
|
|
111
|
+
createTexture(spacing: Offset, options?: CreateTextureOptions): CanvasTexture
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
type QuadOrRect = [x1:number, y1:number, x2:number, y2:number, x3:number, y3:number, x4:number, y4:number] |
|
|
115
|
+
[left:number, top:number, right:number, bottom:number] | [width:number, height:number]
|
|
116
|
+
|
|
117
|
+
export interface CanvasRenderingContext2D extends CanvasCompositing, CanvasDrawImage, CanvasDrawPath, CanvasFillStrokeStyles, CanvasFilters, CanvasImageData, CanvasImageSmoothing, CanvasPath, CanvasPathDrawingStyles, CanvasRect, CanvasShadowStyles, CanvasState, CanvasText, CanvasTextDrawingStyles, CanvasTransform, CanvasUserInterface {
|
|
118
|
+
readonly canvas: Canvas;
|
|
119
|
+
fontVariant: string;
|
|
120
|
+
textTracking: number;
|
|
121
|
+
textWrap: boolean;
|
|
122
|
+
lineDashMarker: Path2D | null;
|
|
123
|
+
lineDashFit: "move" | "turn" | "follow";
|
|
124
|
+
|
|
125
|
+
get currentTransform(): DOMMatrix
|
|
126
|
+
set currentTransform(matrix: DOMMatrix)
|
|
127
|
+
createProjection(quad: QuadOrRect, basis?: QuadOrRect): DOMMatrix
|
|
128
|
+
|
|
129
|
+
conicCurveTo(cpx: number, cpy: number, x: number, y: number, weight: number): void
|
|
130
|
+
// getContextAttributes(): CanvasRenderingContext2DSettings;
|
|
131
|
+
|
|
132
|
+
fillText(text: string, x: number, y:number, maxWidth?: number): void
|
|
133
|
+
strokeText(text: string, x: number, y:number, maxWidth?: number): void
|
|
134
|
+
measureText(text: string, maxWidth?: number): TextMetrics
|
|
135
|
+
outlineText(text: string): Path2D
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
//
|
|
139
|
+
// Bézier Paths
|
|
140
|
+
//
|
|
141
|
+
|
|
142
|
+
export interface Path2DBounds {
|
|
143
|
+
readonly top: number
|
|
144
|
+
readonly left: number
|
|
145
|
+
readonly bottom: number
|
|
146
|
+
readonly right: number
|
|
147
|
+
readonly width: number
|
|
148
|
+
readonly height: number
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export type Path2DEdge = [verb: string, ...args: number[]]
|
|
152
|
+
|
|
153
|
+
export class Path2D extends globalThis.Path2D {
|
|
154
|
+
d: string
|
|
155
|
+
readonly bounds: Path2DBounds
|
|
156
|
+
readonly edges: readonly Path2DEdge[]
|
|
157
|
+
|
|
158
|
+
contains(x: number, y: number): boolean
|
|
159
|
+
conicCurveTo(
|
|
160
|
+
cpx: number,
|
|
161
|
+
cpy: number,
|
|
162
|
+
x: number,
|
|
163
|
+
y: number,
|
|
164
|
+
weight: number
|
|
165
|
+
): void
|
|
166
|
+
|
|
167
|
+
complement(otherPath: Path2D): Path2D
|
|
168
|
+
difference(otherPath: Path2D): Path2D
|
|
169
|
+
intersect(otherPath: Path2D): Path2D
|
|
170
|
+
union(otherPath: Path2D): Path2D
|
|
171
|
+
xor(otherPath: Path2D): Path2D
|
|
172
|
+
interpolate(otherPath: Path2D, weight: number): Path2D
|
|
173
|
+
|
|
174
|
+
jitter(segmentLength: number, amount: number, seed?: number): Path2D
|
|
175
|
+
offset(dx: number, dy: number): Path2D
|
|
176
|
+
points(step?: number): readonly [x: number, y: number][]
|
|
177
|
+
round(radius: number): Path2D
|
|
178
|
+
simplify(rule?: "nonzero" | "evenodd"): Path2D
|
|
179
|
+
transform(...args: [matrix: DOMMatrix] | [a: number, b: number, c: number, d: number, e: number, f: number]): Path2D;
|
|
180
|
+
trim(start: number, end: number, inverted?: boolean): Path2D;
|
|
181
|
+
trim(start: number, inverted?: boolean): Path2D;
|
|
182
|
+
|
|
183
|
+
unwind(): Path2D
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
//
|
|
187
|
+
// Typography
|
|
188
|
+
//
|
|
189
|
+
|
|
190
|
+
export interface TextMetrics extends globalThis.TextMetrics {
|
|
191
|
+
lines: TextMetricsLine[]
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export interface TextMetricsLine {
|
|
195
|
+
readonly x: number
|
|
196
|
+
readonly y: number
|
|
197
|
+
readonly width: number
|
|
198
|
+
readonly height: number
|
|
199
|
+
readonly baseline: number
|
|
200
|
+
readonly startIndex: number
|
|
201
|
+
readonly endIndex: number
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export interface FontFamily {
|
|
205
|
+
family: string
|
|
206
|
+
weights: number[]
|
|
207
|
+
widths: string[]
|
|
208
|
+
styles: string[]
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export interface Font {
|
|
212
|
+
family: string
|
|
213
|
+
weight: number
|
|
214
|
+
style: string
|
|
215
|
+
width: string
|
|
216
|
+
file: string
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export interface FontLibrary {
|
|
220
|
+
families: readonly string[]
|
|
221
|
+
family(name: string): FontFamily | undefined
|
|
222
|
+
has(familyName: string): boolean
|
|
223
|
+
|
|
224
|
+
use(familyName: string, fontPaths?: string | readonly string[]): Font[]
|
|
225
|
+
use(fontPaths: readonly string[]): Font[]
|
|
226
|
+
use(
|
|
227
|
+
families: Record<string, readonly string[] | string>
|
|
228
|
+
): Record<string, Font[] | Font>
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export const FontLibrary: FontLibrary
|
package/lib/index.js
CHANGED
|
@@ -76,18 +76,23 @@ const toString = val => typeof val=='string' ? val : new String(val).toString()
|
|
|
76
76
|
//
|
|
77
77
|
|
|
78
78
|
function toSkMatrix(jsMatrix){
|
|
79
|
-
if (Array.isArray(jsMatrix)){
|
|
80
|
-
var [a, b, c, d, e, f] = jsMatrix
|
|
81
|
-
}else{
|
|
82
|
-
var {a, b, c, d, e, f} = jsMatrix
|
|
79
|
+
if (Array.isArray(jsMatrix) && jsMatrix.length==6){
|
|
80
|
+
var [a, b, c, d, e, f, m14, m24, m44] = jsMatrix.concat(0, 0, 1)
|
|
81
|
+
}else if (jsMatrix instanceof geometry.DOMMatrix){
|
|
82
|
+
var {a, b, c, d, e, f, m14, m24, m44} = jsMatrix
|
|
83
83
|
}
|
|
84
|
-
return [a, c, e, b, d, f]
|
|
84
|
+
return [a, c, e, b, d, f, m14, m24, m44]
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function fromSkMatrix(skMatrix){
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
let [a, b, c, d, e, f, p0, p1, p2] = skMatrix
|
|
89
|
+
return new geometry.DOMMatrix([
|
|
90
|
+
a, d, 0, p0,
|
|
91
|
+
b, e, 0, p1,
|
|
92
|
+
0, 0, 1, 0,
|
|
93
|
+
c, f, 0, p2
|
|
94
|
+
])
|
|
95
|
+
|
|
91
96
|
}
|
|
92
97
|
|
|
93
98
|
|
|
@@ -141,44 +146,64 @@ class Canvas extends RustClass{
|
|
|
141
146
|
get svg(){ return this.toBuffer("svg") }
|
|
142
147
|
|
|
143
148
|
get async(){ return this.prop('async') }
|
|
144
|
-
set async(flag){
|
|
149
|
+
set async(flag){
|
|
150
|
+
if (!flag){
|
|
151
|
+
process.emitWarning("Use the saveAsSync, toBufferSync, and toDataURLSync methods instead of setting the Canvas `async` property to false", "DeprecationWarning")
|
|
152
|
+
}
|
|
153
|
+
this.prop('async', flag)
|
|
154
|
+
}
|
|
145
155
|
|
|
146
156
|
saveAs(filename, opts={}){
|
|
157
|
+
if (!this.async) return this.saveAsSync(...arguments) // support while deprecated
|
|
158
|
+
|
|
147
159
|
opts = typeof opts=='number' ? {quality:opts} : opts
|
|
148
160
|
let {format, quality, pages, padding, pattern, density, outline, matte} = io.options(this.pages, {filename, ...opts}),
|
|
149
|
-
args = [pages.map(core), pattern, padding, format, quality, density, outline, matte]
|
|
161
|
+
args = [pages.map(core), pattern, padding, format, quality, density, outline, matte],
|
|
162
|
+
worker = new EventEmitter();
|
|
163
|
+
this.ƒ("save", (result, msg) => worker.emit(result, msg), ...args)
|
|
164
|
+
return new Promise((res, rej) => worker.once('ok', res).once('err', msg => rej(new Error(msg))) )
|
|
165
|
+
}
|
|
150
166
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}else{
|
|
156
|
-
this.ƒ("saveSync", ...args)
|
|
157
|
-
}
|
|
167
|
+
saveAsSync(filename, opts={}){
|
|
168
|
+
opts = typeof opts=='number' ? {quality:opts} : opts
|
|
169
|
+
let {format, quality, pages, padding, pattern, density, outline, matte} = io.options(this.pages, {filename, ...opts})
|
|
170
|
+
this.ƒ("saveSync", pages.map(core), pattern, padding, format, quality, density, outline, matte)
|
|
158
171
|
}
|
|
159
172
|
|
|
160
173
|
toBuffer(extension="png", opts={}){
|
|
174
|
+
if (!this.async) return this.toBufferSync(...arguments) // support while deprecated
|
|
175
|
+
|
|
161
176
|
opts = typeof opts=='number' ? {quality:opts} : opts
|
|
162
177
|
let {format, quality, pages, density, outline, matte} = io.options(this.pages, {extension, ...opts}),
|
|
163
|
-
args = [pages.map(core), format, quality, density, outline, matte]
|
|
178
|
+
args = [pages.map(core), format, quality, density, outline, matte],
|
|
179
|
+
worker = new EventEmitter();
|
|
180
|
+
this.ƒ("toBuffer", (result, msg) => worker.emit(result, msg), ...args)
|
|
181
|
+
return new Promise((res, rej) => worker.once('ok', res).once('err', msg => rej(new Error(msg))) )
|
|
182
|
+
}
|
|
164
183
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}else{
|
|
170
|
-
return this.ƒ("toBufferSync", ...args)
|
|
171
|
-
}
|
|
184
|
+
toBufferSync(extension="png", opts={}){
|
|
185
|
+
opts = typeof opts=='number' ? {quality:opts} : opts
|
|
186
|
+
let {format, quality, pages, density, outline, matte} = io.options(this.pages, {extension, ...opts})
|
|
187
|
+
return this.ƒ("toBufferSync", pages.map(core), format, quality, density, outline, matte)
|
|
172
188
|
}
|
|
173
189
|
|
|
174
190
|
toDataURL(extension="png", opts={}){
|
|
191
|
+
if (!this.async) return this.toDataURLSync(...arguments) // support while deprecated
|
|
192
|
+
|
|
175
193
|
opts = typeof opts=='number' ? {quality:opts} : opts
|
|
176
194
|
let {mime} = io.options(this.pages, {extension, ...opts}),
|
|
177
|
-
urlify = data => `data:${mime};base64,${data.toString('base64')}`,
|
|
178
195
|
buffer = this.toBuffer(extension, opts);
|
|
179
|
-
return
|
|
196
|
+
return buffer.then(data => `data:${mime};base64,${data.toString('base64')}`)
|
|
180
197
|
}
|
|
181
198
|
|
|
199
|
+
toDataURLSync(extension="png", opts={}){
|
|
200
|
+
opts = typeof opts=='number' ? {quality:opts} : opts
|
|
201
|
+
let {mime} = io.options(this.pages, {extension, ...opts}),
|
|
202
|
+
buffer = this.toBufferSync(extension, opts);
|
|
203
|
+
return `data:${mime};base64,${buffer.toString('base64')}`
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
|
|
182
207
|
[REPR](depth, options) {
|
|
183
208
|
let {width, height, async, pages} = this
|
|
184
209
|
return `Canvas ${inspect({width, height, async, pages}, options)}`
|
|
@@ -261,15 +286,20 @@ class CanvasRenderingContext2D extends RustClass{
|
|
|
261
286
|
get currentTransform(){ return fromSkMatrix( this.prop('currentTransform') ) }
|
|
262
287
|
set currentTransform(matrix){ this.prop('currentTransform', toSkMatrix(matrix) ) }
|
|
263
288
|
|
|
289
|
+
resetTransform(){ this.ƒ('resetTransform')}
|
|
264
290
|
getTransform(){ return this.currentTransform }
|
|
265
291
|
setTransform(matrix){
|
|
266
292
|
this.currentTransform = arguments.length > 1 ? [...arguments] : matrix
|
|
267
293
|
}
|
|
294
|
+
|
|
268
295
|
transform(...terms){ this.ƒ('transform', ...terms)}
|
|
269
296
|
translate(x, y){ this.ƒ('translate', x, y)}
|
|
270
297
|
scale(x, y){ this.ƒ('scale', x, y)}
|
|
271
298
|
rotate(angle){ this.ƒ('rotate', angle)}
|
|
272
|
-
|
|
299
|
+
|
|
300
|
+
createProjection(quad, basis){
|
|
301
|
+
return fromSkMatrix(this.ƒ("createProjection", [quad].flat(), [basis].flat()))
|
|
302
|
+
}
|
|
273
303
|
|
|
274
304
|
// -- bézier paths ----------------------------------------------------------
|
|
275
305
|
beginPath(){ this.ƒ('beginPath') }
|
|
@@ -377,7 +407,7 @@ class CanvasRenderingContext2D extends RustClass{
|
|
|
377
407
|
let w = Math.floor(width),
|
|
378
408
|
h = Math.floor(height),
|
|
379
409
|
buffer = this.ƒ('getImageData', x, y, w, h);
|
|
380
|
-
return new ImageData(w, h
|
|
410
|
+
return new ImageData(buffer, w, h)
|
|
381
411
|
}
|
|
382
412
|
|
|
383
413
|
drawImage(image, ...coords){
|
|
@@ -562,9 +592,17 @@ class Image extends RustClass {
|
|
|
562
592
|
}
|
|
563
593
|
|
|
564
594
|
class ImageData{
|
|
565
|
-
constructor(
|
|
566
|
-
if (
|
|
567
|
-
var {width, height
|
|
595
|
+
constructor(...args){
|
|
596
|
+
if (args[0] instanceof ImageData){
|
|
597
|
+
var {data, width, height} = args[0]
|
|
598
|
+
}else if (args[0] instanceof Uint8ClampedArray || args[0] instanceof Buffer){
|
|
599
|
+
var [data, width, height] = args
|
|
600
|
+
height = height || data.length / width / 4
|
|
601
|
+
if (data.length / 4 != width * height){
|
|
602
|
+
throw new Error("ImageData dimensions must match buffer length")
|
|
603
|
+
}
|
|
604
|
+
}else{
|
|
605
|
+
var [width, height] = args
|
|
568
606
|
}
|
|
569
607
|
|
|
570
608
|
if (!Number.isInteger(width) || !Number.isInteger(height) || width < 0 || height < 0){
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skia-canvas",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.28",
|
|
4
4
|
"description": "A canvas environment for Node",
|
|
5
5
|
"author": "Christian Swinehart <drafting@samizdat.co>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,22 +25,24 @@
|
|
|
25
25
|
"test": "jest"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@mapbox/node-pre-gyp": "^1.0.
|
|
28
|
+
"@mapbox/node-pre-gyp": "^1.0.8",
|
|
29
|
+
"cargo-cp-artifact": "^0.1",
|
|
29
30
|
"glob": "^7.2.0",
|
|
30
31
|
"path-browserify": "^1.0.1",
|
|
31
32
|
"simple-get": "^4.0.0",
|
|
32
33
|
"string-split-by": "^1.0.0"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
36
|
+
"@types/jest": "^27.4.0",
|
|
37
|
+
"@types/node": "^17.0.8",
|
|
38
|
+
"aws-sdk": "^2.1053.0",
|
|
39
|
+
"express": "^4.17.2",
|
|
40
|
+
"jest": "^27.4.7",
|
|
39
41
|
"lodash": "^4.17.21",
|
|
40
|
-
"nodemon": "^2.0.
|
|
42
|
+
"nodemon": "^2.0.15",
|
|
41
43
|
"tmp": "^0.2.1"
|
|
42
44
|
},
|
|
43
|
-
"files":[
|
|
45
|
+
"files": [
|
|
44
46
|
"lib"
|
|
45
47
|
],
|
|
46
48
|
"binary": {
|