skia-canvas 0.9.27 → 0.9.30
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 +243 -0
- package/README.md +144 -54
- package/lib/browser.js +4 -3
- package/lib/index.d.ts +236 -0
- package/lib/index.js +94 -47
- package/package.json +12 -10
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
<!-- ## 🥚 ⟩ [Unreleased] -->
|
|
4
|
+
|
|
5
|
+
## 📦 ⟩ [v0.9.30] ⟩ Jun 7, 2022
|
|
6
|
+
|
|
7
|
+
### New Features
|
|
8
|
+
- Enhacements to the shared **FontLibrary** object:
|
|
9
|
+
- Added a [`reset()`][FontLibrary.reset] method to FontLibrary which uninstalls any fonts that had been dynamically installed via `FontLibrary.use()`
|
|
10
|
+
- The [`use()`][FontLibrary.use] method now checks for previously installed fonts with the same family name (or alias) and will replace them with the newly added font
|
|
11
|
+
- Added pre-compiled binaries for Alpine Linux on arm64
|
|
12
|
+
|
|
13
|
+
### Bugfixes
|
|
14
|
+
- Calling `clip` with an empty path (or one that does not intersect the current clipping mask) will now prevent drawing altogether
|
|
15
|
+
- Transformation (`translate`, `rotate`, etc.) and line-drawing methods (`moveTo`, `lineTo`, `ellipse`, etc.) are now silently ignored if called with `NaN`, `Infinity`, or non-**Number** values in the arguments rather than throwing an error
|
|
16
|
+
- applies to both the Context and Path2D versions of the drawing methods
|
|
17
|
+
- a **TypeError** is thrown only if the number of arguments is too low (mirroring browser behavior)
|
|
18
|
+
- [`conicCurveTo()`][conicCurveTo] now correctly reflects the canvas's transform state
|
|
19
|
+
- The browser-based version of [`loadImage()`][loadImage] now returns a **Promise** that correctly resolves to an **Image** object
|
|
20
|
+
- SVG exports no longer have an invisible, canvas-sized `<rect/>` as their first element
|
|
21
|
+
- Fixed an incompatibility on Alpine between the version of libstdc++ present on the `node:alpine` docker images and the version used when building the precompiled binaries
|
|
22
|
+
|
|
23
|
+
### Misc. Improvements
|
|
24
|
+
- Upgraded Skia to milestone 101
|
|
25
|
+
|
|
26
|
+
[conicCurveTo]: https://github.com/samizdatco/skia-canvas#coniccurvetocpx-cpy-x-y-weight
|
|
27
|
+
[FontLibrary.reset]: https://github.com/samizdatco/skia-canvas#reset
|
|
28
|
+
[FontLibrary.use]: https://github.com/samizdatco/skia-canvas#usefamilyname-fontpaths
|
|
29
|
+
[loadImage]: https://github.com/samizdatco/skia-canvas/#loadimage
|
|
30
|
+
|
|
31
|
+
## 📦 ⟩ [v0.9.29] ⟩ Feb 7, 2022
|
|
32
|
+
|
|
33
|
+
### New Features
|
|
34
|
+
- PDF exports now support the optional [`matte`][matte] argument.
|
|
35
|
+
|
|
36
|
+
### Breaking Changes
|
|
37
|
+
- When the [`drawImage()`][mdn_drawImage] function is passed a **Canvas** object as its image source it will now rasterize the canvas before drawing. The prior behavior (in which it is drawn as a vector graphic) can now be accessed through the new [`drawCanvas()`][drawCanvas] method which supports the same numerical arguments as `drawImage` but requires that its first argument be a **Canvas**.
|
|
38
|
+
|
|
39
|
+
### Bugfixes
|
|
40
|
+
- Regions erased using [`clearRect()`][mdn_clearRect] are now properly antialiased
|
|
41
|
+
- The [`clip()`][mdn_clip] method now interprets the current translate/scale/rotate state correctly when combining clipping masks
|
|
42
|
+
|
|
43
|
+
### Misc. Improvements
|
|
44
|
+
- Upgraded Skia to milestone 97
|
|
45
|
+
|
|
46
|
+
[drawCanvas]: https://github.com/samizdatco/skia-canvas#drawcanvascanvas-x-y-
|
|
47
|
+
[mdn_clip]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip
|
|
48
|
+
[mdn_clearRect]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clearRect
|
|
49
|
+
|
|
50
|
+
## 📦 ⟩ [v0.9.28] ⟩ Jan 12, 2022
|
|
51
|
+
|
|
52
|
+
### New Features
|
|
53
|
+
- Added TypeScript definitions for extensions to the DOM spec (contributed by [@cprecioso](https://github.com/cprecioso))
|
|
54
|
+
- Added 3D-perspective transformations via the new [createProjection()](https://github.com/samizdatco/skia-canvas#createprojectionquad-basis) context method
|
|
55
|
+
- Colors can now use the [hwb()](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hwb()) model
|
|
56
|
+
|
|
57
|
+
### Breaking Changes
|
|
58
|
+
- The **Canvas** [`.async`](https://github.com/samizdatco/skia-canvas#async) property has been **deprecated** and will be removed in a future release.
|
|
59
|
+
- 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)).
|
|
60
|
+
- Use their synchronous counterparts (`saveAsSync`, `toBufferSync`, and `toDataURLSync`) if you want to block execution while exporting images.
|
|
61
|
+
- 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
|
|
62
|
+
|
|
63
|
+
### Bugfixes
|
|
64
|
+
- 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)
|
|
65
|
+
- 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))
|
|
66
|
+
- Shape primitives now behave consistently with browsers when being added to a non-empty path:
|
|
67
|
+
- `rect()` now issues an initial `moveTo` rather than extending the path, then leaves the ‘current’ point in its upper left corner
|
|
68
|
+
- `ellipse()` extends the current path rather than implicitly closing it (contributed by [@meihuanyu](https://github.com/meihuanyu))
|
|
69
|
+
- `arc()` also extends the current path rather than closing it
|
|
70
|
+
|
|
71
|
+
### Misc. Improvements
|
|
72
|
+
- Upgraded Skia to milestone 96
|
|
73
|
+
- Added workflow for creating docker build environments
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
## 📦 ⟩ [v0.9.27] ⟩ Oct 23, 2021
|
|
77
|
+
|
|
78
|
+
### New Features
|
|
79
|
+
- Added pre-compiled binaries for Alpine Linux using the [musl](https://musl.libc.org) C library
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
## 📦 ⟩ [v0.9.26] ⟩ Oct 18, 2021
|
|
83
|
+
|
|
84
|
+
### New Features
|
|
85
|
+
- Added pre-compiled binaries for 32-bit and 64-bit ARM on Linux (a.k.a. Raspberry Pi)
|
|
86
|
+
|
|
87
|
+
### Bugfixes
|
|
88
|
+
- Windows text rendering has been restored after failing due to changes involving the `icudtl.dat` file
|
|
89
|
+
- `FontLibrary.use` now reports an error if the specified font file doesn't exist
|
|
90
|
+
- Fixed a crash that could result from calling `measureText` with various unicode escapes
|
|
91
|
+
|
|
92
|
+
### Misc. Improvements
|
|
93
|
+
- Upgraded Skia to milestone 94
|
|
94
|
+
- Now embedding a more recent version of the FreeType library on Linux with support for more font formats
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
## 📦 ⟩ [v0.9.25] ⟩ Aug 22, 2021
|
|
98
|
+
|
|
99
|
+
### Bugfixes
|
|
100
|
+
- Improved image scaling when a larger image is being shrunk down to a smaller size via [`drawImage()`][mdn_drawImage]
|
|
101
|
+
- 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`
|
|
102
|
+
- [`measureText()`](https://github.com/samizdatco/skia-canvas#measuretextstr-width) now returns correct metrics regardless of current `textAlign` setting
|
|
103
|
+
- Rolled back `icudtl.dat` changes on Windows (which suppressed the misleading warning message but required running as Administrator)
|
|
104
|
+
|
|
105
|
+
### Misc. Improvements
|
|
106
|
+
- Now using [Neon](https://github.com/neon-bindings/neon) v0.9 (with enhanced async event scheduling)
|
|
107
|
+
|
|
108
|
+
[mdn_drawImage]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
|
|
109
|
+
|
|
110
|
+
## 📦 ⟩ [v0.9.24] ⟩ Aug 18, 2021
|
|
111
|
+
|
|
112
|
+
### New Features
|
|
113
|
+
- **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
|
|
114
|
+
- 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`
|
|
115
|
+
- Textures draw either a parallel-lines pattern or one derived from the provided **Path2D** object and positioning parameters
|
|
116
|
+
- 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`)
|
|
117
|
+
- 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)
|
|
118
|
+
|
|
119
|
+
### Bugfixes
|
|
120
|
+
|
|
121
|
+
- Removed use of the `??` operator which is unavailable prior to Node 14
|
|
122
|
+
- Prevented a spurious warning on windows incorrectly claiming that the `icudtl.dat` file could not be found
|
|
123
|
+
|
|
124
|
+
### Misc. Improvements
|
|
125
|
+
|
|
126
|
+
- The **Path2D** [`simplify()`](https://github.com/samizdatco/skia-canvas/#simplifyrulenonzero) method now takes an optional fill-rule argument
|
|
127
|
+
- Added support for versions of macOS starting with 10.13 (High Sierra)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
## 📦 ⟩ [v0.9.23] ⟩ Jul 12, 2021
|
|
131
|
+
|
|
132
|
+
### New Features
|
|
133
|
+
|
|
134
|
+
- [Conic béziers][conic_bezier] can now be drawn to the context or a Path2D with the [`conicCurveTo()`][conic_curveto] method
|
|
135
|
+
- Text can be converted to a Path2D using the context’s new [`outlineText()`][outline_text] method
|
|
136
|
+
- Path2D objects can now report back on their internal geometry with:
|
|
137
|
+
- the [`edges`][edges] property which contains an array of line-drawing commands describing the path’s individual contours
|
|
138
|
+
- the [`contains()`][contains] method which tests whether a given point is on/within the path
|
|
139
|
+
- the [`points()`][points] method which returns an array of `[x, y]` pairs at the requested spacing along the curve’s periphery
|
|
140
|
+
- A modified copy of a source Path2D can now be created using:
|
|
141
|
+
- [`offset()`][offset] or [`transform()`][transform] to shift position or apply a DOMMatrix respectively
|
|
142
|
+
- [`jitter()`][jitter] to break the path into smaller sections and apply random noise to the segments’ positions
|
|
143
|
+
- [`round()`][round] to round off every sharp corner in a path to a particular radius
|
|
144
|
+
- [`trim()`][trim] to select a percentage-based subsection of the path
|
|
145
|
+
- Two similar paths can be ‘tweened’ into a proportional combination of their coordinates using the [`interpolate()`][interpolate] method
|
|
146
|
+
|
|
147
|
+
### Bugfixes
|
|
148
|
+
|
|
149
|
+
- 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()`)
|
|
150
|
+
- The `filter` property will now accept percentage values greater than 999%
|
|
151
|
+
|
|
152
|
+
### Misc. Improvements
|
|
153
|
+
|
|
154
|
+
- 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.
|
|
155
|
+
- 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
|
|
156
|
+
- Canvas dimensions are no longer rounded-off to integer values (at least until a bitmap needs to be generated for export)
|
|
157
|
+
- Linux builds will now run on some older systems going back to glibc 2.24
|
|
158
|
+
|
|
159
|
+
[conic_bezier]: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/curves/beziers#the-conic-bézier-curve
|
|
160
|
+
[conic_curveto]: https://github.com/samizdatco/skia-canvas#coniccurvetocpx-cpy-x-y-weight
|
|
161
|
+
[outline_text]: https://github.com/samizdatco/skia-canvas#outlinetextstr
|
|
162
|
+
[matte]: https://github.com/samizdatco/skia-canvas#matte
|
|
163
|
+
|
|
164
|
+
[edges]: https://github.com/samizdatco/skia-canvas#edges
|
|
165
|
+
[contains]: https://github.com/samizdatco/skia-canvas#containsx-y
|
|
166
|
+
[points]: https://github.com/samizdatco/skia-canvas#pointsstep1
|
|
167
|
+
[offset]: https://github.com/samizdatco/skia-canvas#offsetdx-dy
|
|
168
|
+
[transform]: https://github.com/samizdatco/skia-canvas#transformmatrix-or-transforma-b-c-d-e-f
|
|
169
|
+
|
|
170
|
+
[interpolate]: https://github.com/samizdatco/skia-canvas#interpolateotherpath-weight
|
|
171
|
+
[jitter]: https://github.com/samizdatco/skia-canvas#jittersegmentlength-amount-seed0
|
|
172
|
+
[round]: https://github.com/samizdatco/skia-canvas#roundradius
|
|
173
|
+
[simplify]: https://github.com/samizdatco/skia-canvas#simplify
|
|
174
|
+
[trim]: https://github.com/samizdatco/skia-canvas#trimstart-end-inverted
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
## 📦 ⟩ [v0.9.22] ⟩ Jun 09, 2021
|
|
178
|
+
|
|
179
|
+
### New Features
|
|
180
|
+
|
|
181
|
+
- 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.
|
|
182
|
+
- 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`.
|
|
183
|
+
- SVG exports can optionally convert text to paths by setting the [`outline`](https://github.com/samizdatco/skia-canvas#outline) argument to `true`.
|
|
184
|
+
|
|
185
|
+
### Breaking Changes
|
|
186
|
+
|
|
187
|
+
- 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`.
|
|
188
|
+
- 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.
|
|
189
|
+
|
|
190
|
+
### Bugfixes
|
|
191
|
+
|
|
192
|
+
- `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.
|
|
193
|
+
- 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.
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
## 📦 ⟩ [v0.9.21] ⟩ May 22, 2021
|
|
197
|
+
|
|
198
|
+
### New Features
|
|
199
|
+
- Now runs on Windows and Apple Silicon Macs.
|
|
200
|
+
- Precompiled binaries support Node 10, 12, 14+.
|
|
201
|
+
- Image objects can be initialized from PNG, JPEG, GIF, BMP, or ICO data.
|
|
202
|
+
- 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).
|
|
203
|
+
- Context objects now support [`createConicGradient()`](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createConicGradient).
|
|
204
|
+
- 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.
|
|
205
|
+
|
|
206
|
+
### Bugfixes
|
|
207
|
+
- 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).
|
|
208
|
+
- 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).
|
|
209
|
+
- `CanvasPattern`s now respect the `imageSmoothingEnabled` setting
|
|
210
|
+
- The `counterclockwise` arg to `ellipse` and `arc` is now correctly treated as optional.
|
|
211
|
+
|
|
212
|
+
### Misc. Improvements
|
|
213
|
+
- Made the `console.log` representations of the canvas-related objects friendlier.
|
|
214
|
+
- Added new test suites for `Path2D`, `Image`, and `Canvas`’s format support.
|
|
215
|
+
- Created [workflows](https://github.com/samizdatco/skia-canvas/tree/master/.github/workflows) to automate precompiled binary builds, testing, and npm package updating.
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
## 📦 ⟩ [v0.9.20] ⟩ Mar 27, 2021
|
|
219
|
+
|
|
220
|
+
### Bugfixes
|
|
221
|
+
- The `loadImage` helper can now handle `Buffer` arguments
|
|
222
|
+
|
|
223
|
+
### Misc. Improvements
|
|
224
|
+
- Improved documentation of compilation steps and use of line height with `ctx.font`
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
## 📦 ⟩ [v0.9.19] ⟩ Aug 30, 2020
|
|
228
|
+
|
|
229
|
+
**Initial public release** 🎉
|
|
230
|
+
|
|
231
|
+
[unreleased]: https://github.com/samizdatco/skia-canvas/compare/v0.9.30...HEAD
|
|
232
|
+
[v0.9.30]: https://github.com/samizdatco/skia-canvas/compare/v0.9.29...v0.9.30
|
|
233
|
+
[v0.9.29]: https://github.com/samizdatco/skia-canvas/compare/v0.9.28...v0.9.29
|
|
234
|
+
[v0.9.28]: https://github.com/samizdatco/skia-canvas/compare/v0.9.27...v0.9.28
|
|
235
|
+
[v0.9.27]: https://github.com/samizdatco/skia-canvas/compare/v0.9.26...v0.9.27
|
|
236
|
+
[v0.9.26]: https://github.com/samizdatco/skia-canvas/compare/v0.9.25...v0.9.26
|
|
237
|
+
[v0.9.25]: https://github.com/samizdatco/skia-canvas/compare/v0.9.24...v0.9.25
|
|
238
|
+
[v0.9.24]: https://github.com/samizdatco/skia-canvas/compare/v0.9.23...v0.9.24
|
|
239
|
+
[v0.9.23]: https://github.com/samizdatco/skia-canvas/compare/v0.9.22...v0.9.23
|
|
240
|
+
[v0.9.22]: https://github.com/samizdatco/skia-canvas/compare/v0.9.21...v0.9.22
|
|
241
|
+
[v0.9.21]: https://github.com/samizdatco/skia-canvas/compare/v0.9.20...v0.9.21
|
|
242
|
+
[v0.9.20]: https://github.com/samizdatco/skia-canvas/compare/v0.9.19...v0.9.20
|
|
243
|
+
[v0.9.19]: https://github.com/samizdatco/skia-canvas/compare/v0.9.15...v0.9.19
|
package/README.md
CHANGED
|
@@ -9,9 +9,10 @@ In particular, Skia Canvas:
|
|
|
9
9
|
- is fast and compact since all the heavy lifting is done by native code written in Rust and C++
|
|
10
10
|
- can generate output in both raster (JPEG & PNG) and vector (PDF & SVG) image formats
|
|
11
11
|
- can save images to [files][saveAs], return them as [Buffers][toBuffer], or encode [dataURL][toDataURL_ext] strings
|
|
12
|
-
- uses native threads and [
|
|
12
|
+
- uses native threads and the Node [worker pool](https://github.com/neon-bindings/rfcs/pull/35) 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:
|
|
@@ -51,9 +52,9 @@ Nearly everything you need is statically linked into the library. A notable exce
|
|
|
51
52
|
|
|
52
53
|
### Running in Docker
|
|
53
54
|
|
|
54
|
-
The library is compatible with Linux systems using [glibc](https://www.gnu.org/software/libc/) 2.24 or later as well as Alpine Linux (x64) and the [musl](https://musl.libc.org) C library it favors. In both cases, Fontconfig must be installed on the system for `skia-canvas` to operate correctly.
|
|
55
|
+
The library is compatible with Linux systems using [glibc](https://www.gnu.org/software/libc/) 2.24 or later as well as Alpine Linux (x64 & arm64) and the [musl](https://musl.libc.org) C library it favors. In both cases, Fontconfig must be installed on the system for `skia-canvas` to operate correctly.
|
|
55
56
|
|
|
56
|
-
If you are setting up a [Dockerfile](https://nodejs.org/en/docs/guides/nodejs-docker-webapp/) that uses [`node`](https://hub.docker.com/_/node) as its basis, the simplest approach is to set your `FROM` image to one of the (Debian-derived) defaults like `node:16`, `node:14`, `node:12`, `node:
|
|
57
|
+
If you are setting up a [Dockerfile](https://nodejs.org/en/docs/guides/nodejs-docker-webapp/) that uses [`node`](https://hub.docker.com/_/node) as its basis, the simplest approach is to set your `FROM` image to one of the (Debian-derived) defaults like `node:16`, `node:14`, `node:12`, `node:bullseye`, `node:buster`, or simply:
|
|
57
58
|
```dockerfile
|
|
58
59
|
FROM node
|
|
59
60
|
```
|
|
@@ -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
|
|
|
@@ -278,33 +287,18 @@ This method accepts the same arguments and behaves similarly to `.toBuffer`. How
|
|
|
278
287
|
|
|
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
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
| [**
|
|
285
|
-
| [
|
|
286
|
-
| [
|
|
287
|
-
| [
|
|
288
|
-
| [
|
|
289
|
-
| [
|
|
290
|
-
| [
|
|
291
|
-
|
|
|
292
|
-
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
| Bezier Paths | Typography | Images | Compositing Effects |
|
|
296
|
-
|------------------------------------------|-------------------------------------------------------------|----------------------------------------------------|----------------------------------------------------------|
|
|
297
|
-
| [moveTo()][moveTo()] | [**direction**][direction] | [**imageSmoothingEnabled**][imageSmoothingEnabled] | [**filter**][filter] |
|
|
298
|
-
| [lineTo()][lineTo()] | [**font**][font] ⧸[⚡](#font) | [**imageSmoothingQuality**][imageSmoothingQuality] | [**globalAlpha**][globalAlpha] |
|
|
299
|
-
| [arcTo()][arcTo()] | [**fontVariant** ⚡](#fontvariant) | [createImageData()][createImageData()] | [**globalCompositeOperation**][globalCompositeOperation] |
|
|
300
|
-
| [bezierCurveTo()][bezierCurveTo()] | [**textAlign**][textAlign] | [getImageData()][getImageData()] | [**shadowBlur**][shadowBlur] |
|
|
301
|
-
| [conicCurveTo() ⚡][conicCurveTo] | [**textBaseline**][textBaseline] | [putImageData()][putImageData()] | [**shadowColor**][shadowColor] |
|
|
302
|
-
| [quadraticCurveTo()][quadraticCurveTo()] | [**textTracking** ⚡](#texttracking) | [drawImage()][drawImage()] | [**shadowOffsetX**][shadowOffsetX] |
|
|
303
|
-
| [closePath()][closePath()] | [**textWrap** ⚡](#textwrap) | | [**shadowOffsetY**][shadowOffsetY] |
|
|
304
|
-
| [arc()][arc()] | [measureText()][measureText()] ⧸[⚡](#measuretextstr-width) | | |
|
|
305
|
-
| [ellipse()][ellipse()] | [outlineText() ⚡][outlineText()] | | |
|
|
306
|
-
| [rect()][rect()] | | |
|
|
307
|
-
|
|
290
|
+
| Canvas State | Drawing | Pattern & Color | Line Style | Transform | Bezier Paths | Typography | Images | Compositing Effects |
|
|
291
|
+
|-----------------------------------------------|---------------------------------------------------|---------------------------------------------------|----------------------------------------------|--------------------------------------------------|------------------------------------------|------------------------------------------------------------------|----------------------------------------------------|----------------------------------------------------------|
|
|
292
|
+
| [**canvas**][canvas_attr] ⧸[⚡](#canvas) | [clearRect()][clearRect()] | [**fillStyle**][fillStyle] | [**lineCap**][lineCap] | [**currentTransform**][currentTransform] | [moveTo()][moveTo()] | [**direction**][direction] | [**imageSmoothingEnabled**][imageSmoothingEnabled] | [**filter**][filter] |
|
|
293
|
+
| [beginPath()][beginPath()] | [fillRect()][fillRect()] | [**strokeStyle**][strokeStyle] | [**lineDashFit** ⚡][lineDashFit] | [createProjection() ⚡][createProjection()] | [lineTo()][lineTo()] | [**font**][font] ⧸[⚡](#font) | [**imageSmoothingQuality**][imageSmoothingQuality] | [**globalAlpha**][globalAlpha] |
|
|
294
|
+
| [isPointInPath()][isPointInPath()] | [strokeRect()][strokeRect()] | [createConicGradient()][createConicGradient()] | [**lineDashMarker** ⚡][lineDashMarker] | [getTransform()][getTransform()] | [arcTo()][arcTo()] | [**fontVariant** ⚡](#fontvariant) | [createImageData()][createImageData()] | [**globalCompositeOperation**][globalCompositeOperation] |
|
|
295
|
+
| [isPointInStroke()][isPointInStroke()] | [fillText()][fillText()] ⧸[⚡][drawText] | [createLinearGradient()][createLinearGradient()] | [**lineDashOffset**][lineDashOffset] | [setTransform()][setTransform()] | [bezierCurveTo()][bezierCurveTo()] | [**textAlign**][textAlign] | [getImageData()][getImageData()] | [**shadowBlur**][shadowBlur] |
|
|
296
|
+
| [save()][save()] | [strokeText()][strokeText()] ⧸[⚡][drawText] | [createRadialGradient()][createRadialGradient()] | [**lineJoin**][lineJoin] | [resetTransform()][resetTransform()] | [conicCurveTo() ⚡][conicCurveTo] | [**textBaseline**][textBaseline] | [putImageData()][putImageData()] | [**shadowColor**][shadowColor] |
|
|
297
|
+
| [restore()][restore()] | [fill()][fill()] | [createPattern()][createPattern()] | [**lineWidth**][lineWidth] | [transform()][transform()] | [quadraticCurveTo()][quadraticCurveTo()] | [**textTracking** ⚡](#texttracking) | [drawCanvas() ⚡](#drawcanvascanvas-x-y-) | [**shadowOffsetX**][shadowOffsetX] |
|
|
298
|
+
| [clip()][clip()] | [stroke()][stroke()] | [createTexture() ⚡][createTexture()] | [**miterLimit**][miterLimit] | [translate()][translate()] | [closePath()][closePath()] | [**textWrap** ⚡](#textwrap) | [drawImage()][drawImage()] | [**shadowOffsetY**][shadowOffsetY] |
|
|
299
|
+
| | | | [getLineDash()][getLineDash()] | [rotate()][rotate()] | [arc()][arc()] | [measureText()][measureText()] ⧸[⚡](#measuretextstr-width) | | |
|
|
300
|
+
| | | | [setLineDash()][setLineDash()] | [scale()][scale()] | [ellipse()][ellipse()] | [outlineText() ⚡][outlineText()] | | |
|
|
301
|
+
| | | | | | [rect()][rect()] | | |
|
|
308
302
|
|
|
309
303
|
##### PROPERTIES
|
|
310
304
|
|
|
@@ -389,6 +383,71 @@ The `lineDashFit` attribute can be set to `"move"`, `"turn"`, or `"follow"` and
|
|
|
389
383
|
|
|
390
384
|
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
385
|
|
|
386
|
+
#### `createProjection(quad, [basis])`
|
|
387
|
+
|
|
388
|
+
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.
|
|
389
|
+
|
|
390
|
+
##### `quad`
|
|
391
|
+
|
|
392
|
+
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’.
|
|
393
|
+
|
|
394
|
+
The geometry of the quadrilateral should be described as an Array of either 8 or 4 numbers specifying an arbitrary polygon or rectangle respectively:
|
|
395
|
+
|
|
396
|
+
```js
|
|
397
|
+
[x1, y1, x2, y2, x3, y3, x4, y4] // four corner points
|
|
398
|
+
[left, top, right, bottom] // four edges of a rectangle
|
|
399
|
+
|
|
400
|
+
// internal arrays for grouping are also allowed
|
|
401
|
+
[[x1, y1], [x2, y2], [x3, y3], [x4, y4]]
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
##### `basis`
|
|
405
|
+
|
|
406
|
+
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.
|
|
407
|
+
|
|
408
|
+
The `basis` polygon can be described using 2, 4, or 8 numbers, using the canvas dimensions to fill in the unspecified coordinates:
|
|
409
|
+
```js
|
|
410
|
+
[width, height] // rectangle from ⟨0, 0⟩ to ⟨width, height⟩
|
|
411
|
+
[left, top, right, bottom] // four edges of a rectangle
|
|
412
|
+
[x1, y1, x2, y2, x3, y3, x4, y4] // four corner points
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
----
|
|
416
|
+
|
|
417
|
+
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.
|
|
418
|
+
|
|
419
|
+
```js
|
|
420
|
+
let canvas = new Canvas(512, 512),
|
|
421
|
+
ctx = canvas.getContext("2d"),
|
|
422
|
+
{width:w, height:h} = canvas;
|
|
423
|
+
ctx.font = '900 480px Times'
|
|
424
|
+
ctx.textAlign = 'center'
|
|
425
|
+
ctx.fillStyle = '#aaa'
|
|
426
|
+
ctx.fillRect(0, 0, w, h)
|
|
427
|
+
|
|
428
|
+
let quad = [
|
|
429
|
+
w*.33, h/2, // upper left
|
|
430
|
+
w*.66, h/2, // upper right
|
|
431
|
+
w, h*.9, // bottom right
|
|
432
|
+
0, h*.9, // bottom left
|
|
433
|
+
]
|
|
434
|
+
|
|
435
|
+
let matrix = ctx.createProjection(quad) // use default basis
|
|
436
|
+
ctx.setTransform(matrix)
|
|
437
|
+
|
|
438
|
+
ctx.fillStyle = 'white'
|
|
439
|
+
ctx.fillRect(10, 10, w-20, h-20)
|
|
440
|
+
|
|
441
|
+
ctx.fillStyle = '#900'
|
|
442
|
+
ctx.fillText("@", w/2, h-40)
|
|
443
|
+
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
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:
|
|
447
|
+
|
|
448
|
+

|
|
449
|
+
|
|
450
|
+
|
|
392
451
|
#### `createTexture(spacing, {path, line, color, angle, offset=0})`
|
|
393
452
|
|
|
394
453
|
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.
|
|
@@ -416,6 +475,21 @@ The rectangle defined by the `spacing` argument will be aligned with the canvas
|
|
|
416
475
|
##### `offset`
|
|
417
476
|
As with `CanvasPattern` objects, textures are positioned globally relative to the upper left corner of the canvas—not the corner of the object currently being filled or stroked. To fine-tune the texture’s alignment with individual objects, set the `offset` argument to an `[x, y]` array with two numbers that will shift the texture relative to its origin.
|
|
418
477
|
|
|
478
|
+
#### `drawCanvas(canvas, x, y, …)`
|
|
479
|
+
This method behaves identically to the standard [`drawImage()`][drawImage()] function with one key difference: if the first argument is a canvas, it will not be converted to a bitmap before being drawn. Instead its contents will be added to the canvas as resolution-independent vector graphics. This is especially useful when scaling or rotating since it preserves the fidelity of text, patterns, and gradients from the source canvas.
|
|
480
|
+
|
|
481
|
+
```js
|
|
482
|
+
let src = new Canvas(10, 10),
|
|
483
|
+
srcCtx = src.getContext("2d");
|
|
484
|
+
srcCtx.font = 'italic 10px Times'
|
|
485
|
+
srcCtx.fillText('¶', 2, 8)
|
|
486
|
+
|
|
487
|
+
let dst = new Canvas(350, 150),
|
|
488
|
+
dstCtx = dst.getContext("2d");
|
|
489
|
+
dstCtx.drawImage(src, 0, 0, 150, 150)
|
|
490
|
+
dstCtx.drawCanvas(src, 200, 0, 150, 150)
|
|
491
|
+
```
|
|
492
|
+

|
|
419
493
|
|
|
420
494
|
#### `fillText(str, x, y, [width])` & `strokeText(str, x, y, [width])`
|
|
421
495
|
|
|
@@ -714,6 +788,13 @@ img.onload = function(){
|
|
|
714
788
|
img.src = 'https://example.com/icon.png'
|
|
715
789
|
```
|
|
716
790
|
|
|
791
|
+
```js
|
|
792
|
+
let img = new Image()
|
|
793
|
+
img.src = 'https://example.com/icon.png'
|
|
794
|
+
await img.decode()
|
|
795
|
+
ctx.drawImage(img, 100, 100)
|
|
796
|
+
```
|
|
797
|
+
|
|
717
798
|
```js
|
|
718
799
|
let img = await loadImage('https://example.com/icon.png')
|
|
719
800
|
ctx.drawImage(img, 100, 100)
|
|
@@ -747,6 +828,10 @@ Asking for details about an unknown family will return `undefined`.
|
|
|
747
828
|
|
|
748
829
|
Returns `true` if the family is installed on the system or has been added via `FontLibrary.use()`.
|
|
749
830
|
|
|
831
|
+
##### `reset()`
|
|
832
|
+
|
|
833
|
+
Uninstalls any dynamically loaded fonts that had been added via `FontLibrary.use()`.
|
|
834
|
+
|
|
750
835
|
##### `use(familyName, [...fontPaths])`
|
|
751
836
|
|
|
752
837
|
The `FontLibrary.use()` method allows you to dynamically load local font files and use them with your canvases. By default it will use whatever family name is in the font metadata, but this can be overridden by an alias you provide. Since font-wrangling can be messy, `use` can be called in a number of different ways:
|
|
@@ -770,6 +855,8 @@ FontLibrary.use("Grizwald", [
|
|
|
770
855
|
|
|
771
856
|
###### with a list of ‘glob’ patterns
|
|
772
857
|
|
|
858
|
+
> Note to Windows users: Due to recent changes to the [glob][glob] module, you must write paths using unix-style forward slashes. Backslashes are now used solely for escaping wildcard characters.
|
|
859
|
+
|
|
773
860
|
```js
|
|
774
861
|
// with default family name
|
|
775
862
|
FontLibrary.use(['fonts/Crimson_Pro/*.ttf'])
|
|
@@ -830,6 +917,7 @@ Many thanks to the [`node-canvas`](https://github.com/Automattic/node-canvas) de
|
|
|
830
917
|
[conicCurveTo]: #coniccurvetocpx-cpy-x-y-weight
|
|
831
918
|
[outlineText()]: #outlinetextstr
|
|
832
919
|
[createTexture()]: #createtexturespacing-path-line-color-angle-offset0
|
|
920
|
+
[createProjection()]: #createprojectionquad-basis
|
|
833
921
|
[lineDashMarker]: #linedashmarker
|
|
834
922
|
[lineDashFit]: #linedashfit
|
|
835
923
|
|
|
@@ -915,4 +1003,6 @@ Many thanks to the [`node-canvas`](https://github.com/Automattic/node-canvas) de
|
|
|
915
1003
|
[translate()]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/translate
|
|
916
1004
|
|
|
917
1005
|
[nonzero]: https://en.wikipedia.org/wiki/Nonzero-rule
|
|
918
|
-
[evenodd]: https://en.wikipedia.org/wiki/Even–odd_rule
|
|
1006
|
+
[evenodd]: https://en.wikipedia.org/wiki/Even–odd_rule
|
|
1007
|
+
|
|
1008
|
+
[glob]: https://github.com/isaacs/node-glob/blob/main/changelog.md#80
|
package/lib/browser.js
CHANGED
|
@@ -9,9 +9,10 @@ const {asBuffer, asDownload, asZipDownload, atScale, options} = require('./io')
|
|
|
9
9
|
|
|
10
10
|
const _toURL_ = Symbol.for("toDataURL")
|
|
11
11
|
|
|
12
|
-
const loadImage = src =>
|
|
13
|
-
Object.assign(new Image(), {crossOrigin:'Anonymous',
|
|
14
|
-
)
|
|
12
|
+
const loadImage = src => {
|
|
13
|
+
let img = Object.assign(new Image(), {crossOrigin:'Anonymous', src})
|
|
14
|
+
return img.decode().then(() => img)
|
|
15
|
+
}
|
|
15
16
|
|
|
16
17
|
class Canvas{
|
|
17
18
|
constructor(width, height){
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
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
|
+
drawCanvas(image: Canvas, dx: number, dy: number): void;
|
|
103
|
+
drawCanvas(image: Canvas, dx: number, dy: number, dw: number, dh: number): void;
|
|
104
|
+
drawCanvas(image: Canvas, sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number): void;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface CanvasFillStrokeStyles {
|
|
108
|
+
fillStyle: string | CanvasGradient | CanvasPattern | CanvasTexture;
|
|
109
|
+
strokeStyle: string | CanvasGradient | CanvasPattern | CanvasTexture;
|
|
110
|
+
createConicGradient(startAngle: number, x: number, y: number): CanvasGradient;
|
|
111
|
+
createLinearGradient(x0: number, y0: number, x1: number, y1: number): CanvasGradient;
|
|
112
|
+
createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): CanvasGradient;
|
|
113
|
+
createPattern(image: CanvasImageSource, repetition: string | null): CanvasPattern | null;
|
|
114
|
+
createTexture(spacing: Offset, options?: CreateTextureOptions): CanvasTexture
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
type QuadOrRect = [x1:number, y1:number, x2:number, y2:number, x3:number, y3:number, x4:number, y4:number] |
|
|
118
|
+
[left:number, top:number, right:number, bottom:number] | [width:number, height:number]
|
|
119
|
+
|
|
120
|
+
export interface CanvasRenderingContext2D extends CanvasCompositing, CanvasDrawImage, CanvasDrawPath, CanvasFillStrokeStyles, CanvasFilters, CanvasImageData, CanvasImageSmoothing, CanvasPath, CanvasPathDrawingStyles, CanvasRect, CanvasShadowStyles, CanvasState, CanvasText, CanvasTextDrawingStyles, CanvasTransform, CanvasUserInterface {
|
|
121
|
+
readonly canvas: Canvas;
|
|
122
|
+
fontVariant: string;
|
|
123
|
+
textTracking: number;
|
|
124
|
+
textWrap: boolean;
|
|
125
|
+
lineDashMarker: Path2D | null;
|
|
126
|
+
lineDashFit: "move" | "turn" | "follow";
|
|
127
|
+
|
|
128
|
+
get currentTransform(): DOMMatrix
|
|
129
|
+
set currentTransform(matrix: DOMMatrix)
|
|
130
|
+
createProjection(quad: QuadOrRect, basis?: QuadOrRect): DOMMatrix
|
|
131
|
+
|
|
132
|
+
conicCurveTo(cpx: number, cpy: number, x: number, y: number, weight: number): void
|
|
133
|
+
// getContextAttributes(): CanvasRenderingContext2DSettings;
|
|
134
|
+
|
|
135
|
+
fillText(text: string, x: number, y:number, maxWidth?: number): void
|
|
136
|
+
strokeText(text: string, x: number, y:number, maxWidth?: number): void
|
|
137
|
+
measureText(text: string, maxWidth?: number): TextMetrics
|
|
138
|
+
outlineText(text: string): Path2D
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
//
|
|
142
|
+
// Bézier Paths
|
|
143
|
+
//
|
|
144
|
+
|
|
145
|
+
export interface Path2DBounds {
|
|
146
|
+
readonly top: number
|
|
147
|
+
readonly left: number
|
|
148
|
+
readonly bottom: number
|
|
149
|
+
readonly right: number
|
|
150
|
+
readonly width: number
|
|
151
|
+
readonly height: number
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export type Path2DEdge = [verb: string, ...args: number[]]
|
|
155
|
+
|
|
156
|
+
export class Path2D extends globalThis.Path2D {
|
|
157
|
+
d: string
|
|
158
|
+
readonly bounds: Path2DBounds
|
|
159
|
+
readonly edges: readonly Path2DEdge[]
|
|
160
|
+
|
|
161
|
+
contains(x: number, y: number): boolean
|
|
162
|
+
conicCurveTo(
|
|
163
|
+
cpx: number,
|
|
164
|
+
cpy: number,
|
|
165
|
+
x: number,
|
|
166
|
+
y: number,
|
|
167
|
+
weight: number
|
|
168
|
+
): void
|
|
169
|
+
|
|
170
|
+
complement(otherPath: Path2D): Path2D
|
|
171
|
+
difference(otherPath: Path2D): Path2D
|
|
172
|
+
intersect(otherPath: Path2D): Path2D
|
|
173
|
+
union(otherPath: Path2D): Path2D
|
|
174
|
+
xor(otherPath: Path2D): Path2D
|
|
175
|
+
interpolate(otherPath: Path2D, weight: number): Path2D
|
|
176
|
+
|
|
177
|
+
jitter(segmentLength: number, amount: number, seed?: number): Path2D
|
|
178
|
+
offset(dx: number, dy: number): Path2D
|
|
179
|
+
points(step?: number): readonly [x: number, y: number][]
|
|
180
|
+
round(radius: number): Path2D
|
|
181
|
+
simplify(rule?: "nonzero" | "evenodd"): Path2D
|
|
182
|
+
transform(...args: [matrix: DOMMatrix] | [a: number, b: number, c: number, d: number, e: number, f: number]): Path2D;
|
|
183
|
+
trim(start: number, end: number, inverted?: boolean): Path2D;
|
|
184
|
+
trim(start: number, inverted?: boolean): Path2D;
|
|
185
|
+
|
|
186
|
+
unwind(): Path2D
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
//
|
|
190
|
+
// Typography
|
|
191
|
+
//
|
|
192
|
+
|
|
193
|
+
export interface TextMetrics extends globalThis.TextMetrics {
|
|
194
|
+
lines: TextMetricsLine[]
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface TextMetricsLine {
|
|
198
|
+
readonly x: number
|
|
199
|
+
readonly y: number
|
|
200
|
+
readonly width: number
|
|
201
|
+
readonly height: number
|
|
202
|
+
readonly baseline: number
|
|
203
|
+
readonly startIndex: number
|
|
204
|
+
readonly endIndex: number
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export interface FontFamily {
|
|
208
|
+
family: string
|
|
209
|
+
weights: number[]
|
|
210
|
+
widths: string[]
|
|
211
|
+
styles: string[]
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export interface Font {
|
|
215
|
+
family: string
|
|
216
|
+
weight: number
|
|
217
|
+
style: string
|
|
218
|
+
width: string
|
|
219
|
+
file: string
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export interface FontLibrary {
|
|
223
|
+
families: readonly string[]
|
|
224
|
+
family(name: string): FontFamily | undefined
|
|
225
|
+
has(familyName: string): boolean
|
|
226
|
+
|
|
227
|
+
use(familyName: string, fontPaths?: string | readonly string[]): Font[]
|
|
228
|
+
use(fontPaths: readonly string[]): Font[]
|
|
229
|
+
use(
|
|
230
|
+
families: Record<string, readonly string[] | string>
|
|
231
|
+
): Record<string, Font[] | Font>
|
|
232
|
+
|
|
233
|
+
reset(): void
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export const FontLibrary: FontLibrary
|
package/lib/index.js
CHANGED
|
@@ -51,7 +51,12 @@ class RustClass{
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
ƒ(fn, ...args){
|
|
54
|
-
|
|
54
|
+
try{
|
|
55
|
+
return this.native[fn](this[ø], ...args)
|
|
56
|
+
}catch(error){
|
|
57
|
+
Error.captureStackTrace(error, this.ƒ)
|
|
58
|
+
throw error
|
|
59
|
+
}
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
62
|
|
|
@@ -76,18 +81,23 @@ const toString = val => typeof val=='string' ? val : new String(val).toString()
|
|
|
76
81
|
//
|
|
77
82
|
|
|
78
83
|
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
|
|
84
|
+
if (Array.isArray(jsMatrix) && jsMatrix.length==6){
|
|
85
|
+
var [a, b, c, d, e, f, m14, m24, m44] = jsMatrix.concat(0, 0, 1)
|
|
86
|
+
}else if (jsMatrix instanceof geometry.DOMMatrix){
|
|
87
|
+
var {a, b, c, d, e, f, m14, m24, m44} = jsMatrix
|
|
83
88
|
}
|
|
84
|
-
return [a, c, e, b, d, f]
|
|
89
|
+
return [a, c, e, b, d, f, m14, m24, m44]
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
function fromSkMatrix(skMatrix){
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
let [a, b, c, d, e, f, p0, p1, p2] = skMatrix
|
|
94
|
+
return new geometry.DOMMatrix([
|
|
95
|
+
a, d, 0, p0,
|
|
96
|
+
b, e, 0, p1,
|
|
97
|
+
0, 0, 1, 0,
|
|
98
|
+
c, f, 0, p2
|
|
99
|
+
])
|
|
100
|
+
|
|
91
101
|
}
|
|
92
102
|
|
|
93
103
|
|
|
@@ -141,44 +151,60 @@ class Canvas extends RustClass{
|
|
|
141
151
|
get svg(){ return this.toBuffer("svg") }
|
|
142
152
|
|
|
143
153
|
get async(){ return this.prop('async') }
|
|
144
|
-
set async(flag){
|
|
154
|
+
set async(flag){
|
|
155
|
+
if (!flag){
|
|
156
|
+
process.emitWarning("Use the saveAsSync, toBufferSync, and toDataURLSync methods instead of setting the Canvas `async` property to false", "DeprecationWarning")
|
|
157
|
+
}
|
|
158
|
+
this.prop('async', flag)
|
|
159
|
+
}
|
|
145
160
|
|
|
146
161
|
saveAs(filename, opts={}){
|
|
162
|
+
if (!this.async) return this.saveAsSync(...arguments) // support while deprecated
|
|
163
|
+
|
|
147
164
|
opts = typeof opts=='number' ? {quality:opts} : opts
|
|
148
165
|
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]
|
|
166
|
+
args = [pages.map(core), pattern, padding, format, quality, density, outline, matte]
|
|
167
|
+
return this.ƒ("save", ...args)
|
|
168
|
+
}
|
|
150
169
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}else{
|
|
156
|
-
this.ƒ("saveSync", ...args)
|
|
157
|
-
}
|
|
170
|
+
saveAsSync(filename, opts={}){
|
|
171
|
+
opts = typeof opts=='number' ? {quality:opts} : opts
|
|
172
|
+
let {format, quality, pages, padding, pattern, density, outline, matte} = io.options(this.pages, {filename, ...opts})
|
|
173
|
+
this.ƒ("saveSync", pages.map(core), pattern, padding, format, quality, density, outline, matte)
|
|
158
174
|
}
|
|
159
175
|
|
|
160
176
|
toBuffer(extension="png", opts={}){
|
|
177
|
+
if (!this.async) return this.toBufferSync(...arguments) // support while deprecated
|
|
178
|
+
|
|
161
179
|
opts = typeof opts=='number' ? {quality:opts} : opts
|
|
162
180
|
let {format, quality, pages, density, outline, matte} = io.options(this.pages, {extension, ...opts}),
|
|
163
181
|
args = [pages.map(core), format, quality, density, outline, matte];
|
|
182
|
+
return this.ƒ("toBuffer", ...args)
|
|
183
|
+
}
|
|
164
184
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}else{
|
|
170
|
-
return this.ƒ("toBufferSync", ...args)
|
|
171
|
-
}
|
|
185
|
+
toBufferSync(extension="png", opts={}){
|
|
186
|
+
opts = typeof opts=='number' ? {quality:opts} : opts
|
|
187
|
+
let {format, quality, pages, density, outline, matte} = io.options(this.pages, {extension, ...opts})
|
|
188
|
+
return this.ƒ("toBufferSync", pages.map(core), format, quality, density, outline, matte)
|
|
172
189
|
}
|
|
173
190
|
|
|
174
191
|
toDataURL(extension="png", opts={}){
|
|
192
|
+
if (!this.async) return this.toDataURLSync(...arguments) // support while deprecated
|
|
193
|
+
|
|
175
194
|
opts = typeof opts=='number' ? {quality:opts} : opts
|
|
176
195
|
let {mime} = io.options(this.pages, {extension, ...opts}),
|
|
177
|
-
urlify = data => `data:${mime};base64,${data.toString('base64')}`,
|
|
178
196
|
buffer = this.toBuffer(extension, opts);
|
|
179
|
-
return
|
|
197
|
+
return buffer.then(data => `data:${mime};base64,${data.toString('base64')}`)
|
|
180
198
|
}
|
|
181
199
|
|
|
200
|
+
toDataURLSync(extension="png", opts={}){
|
|
201
|
+
opts = typeof opts=='number' ? {quality:opts} : opts
|
|
202
|
+
let {mime} = io.options(this.pages, {extension, ...opts}),
|
|
203
|
+
buffer = this.toBufferSync(extension, opts);
|
|
204
|
+
return `data:${mime};base64,${buffer.toString('base64')}`
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
182
208
|
[REPR](depth, options) {
|
|
183
209
|
let {width, height, async, pages} = this
|
|
184
210
|
return `Canvas ${inspect({width, height, async, pages}, options)}`
|
|
@@ -261,30 +287,35 @@ class CanvasRenderingContext2D extends RustClass{
|
|
|
261
287
|
get currentTransform(){ return fromSkMatrix( this.prop('currentTransform') ) }
|
|
262
288
|
set currentTransform(matrix){ this.prop('currentTransform', toSkMatrix(matrix) ) }
|
|
263
289
|
|
|
290
|
+
resetTransform(){ this.ƒ('resetTransform')}
|
|
264
291
|
getTransform(){ return this.currentTransform }
|
|
265
292
|
setTransform(matrix){
|
|
266
293
|
this.currentTransform = arguments.length > 1 ? [...arguments] : matrix
|
|
267
294
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
295
|
+
|
|
296
|
+
transform(a, b, c, d, e, f){ this.ƒ('transform', ...arguments)}
|
|
297
|
+
translate(x, y){ this.ƒ('translate', ...arguments)}
|
|
298
|
+
scale(x, y){ this.ƒ('scale', ...arguments)}
|
|
299
|
+
rotate(angle){ this.ƒ('rotate', ...arguments)}
|
|
300
|
+
|
|
301
|
+
createProjection(quad, basis){
|
|
302
|
+
return fromSkMatrix(this.ƒ("createProjection", [quad].flat(), [basis].flat()))
|
|
303
|
+
}
|
|
273
304
|
|
|
274
305
|
// -- bézier paths ----------------------------------------------------------
|
|
275
306
|
beginPath(){ this.ƒ('beginPath') }
|
|
276
307
|
rect(x, y, width, height){ this.ƒ('rect', ...arguments) }
|
|
277
308
|
arc(x, y, radius, startAngle, endAngle, isCCW){ this.ƒ('arc', ...arguments) }
|
|
278
309
|
ellipse(x, y, xRadius, yRadius, rotation, startAngle, endAngle, isCCW){ this.ƒ('ellipse', ...arguments) }
|
|
279
|
-
moveTo(x, y){ this.ƒ('moveTo',
|
|
280
|
-
lineTo(x, y){ this.ƒ('lineTo',
|
|
310
|
+
moveTo(x, y){ this.ƒ('moveTo', ...arguments) }
|
|
311
|
+
lineTo(x, y){ this.ƒ('lineTo', ...arguments) }
|
|
281
312
|
arcTo(x1, y1, x2, y2, radius){ this.ƒ('arcTo', ...arguments) }
|
|
282
313
|
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y){ this.ƒ('bezierCurveTo', ...arguments) }
|
|
283
314
|
quadraticCurveTo(cpx, cpy, x, y){ this.ƒ('quadraticCurveTo', ...arguments) }
|
|
284
315
|
conicCurveTo(cpx, cpy, x, y, weight){ this.ƒ("conicCurveTo", ...arguments) }
|
|
285
316
|
closePath(){ this.ƒ('closePath') }
|
|
286
|
-
isPointInPath(x, y){ return this.ƒ('isPointInPath',
|
|
287
|
-
isPointInStroke(x, y){ return this.ƒ('isPointInStroke',
|
|
317
|
+
isPointInPath(x, y){ return this.ƒ('isPointInPath', ...arguments) }
|
|
318
|
+
isPointInStroke(x, y){ return this.ƒ('isPointInStroke', ...arguments) }
|
|
288
319
|
|
|
289
320
|
// -- using paths -----------------------------------------------------------
|
|
290
321
|
fill(path, rule){
|
|
@@ -377,19 +408,27 @@ class CanvasRenderingContext2D extends RustClass{
|
|
|
377
408
|
let w = Math.floor(width),
|
|
378
409
|
h = Math.floor(height),
|
|
379
410
|
buffer = this.ƒ('getImageData', x, y, w, h);
|
|
380
|
-
return new ImageData(w, h
|
|
411
|
+
return new ImageData(buffer, w, h)
|
|
381
412
|
}
|
|
382
413
|
|
|
383
414
|
drawImage(image, ...coords){
|
|
384
415
|
if (image instanceof Canvas){
|
|
385
|
-
this.ƒ('
|
|
416
|
+
this.ƒ('drawImage', core(image.getContext('2d')), ...coords)
|
|
386
417
|
}else if (image instanceof Image){
|
|
387
|
-
this.ƒ('
|
|
418
|
+
this.ƒ('drawImage', core(image), ...coords)
|
|
388
419
|
}else{
|
|
389
420
|
throw new Error("Expected an Image or a Canvas argument")
|
|
390
421
|
}
|
|
391
422
|
}
|
|
392
423
|
|
|
424
|
+
drawCanvas(image, ...coords){
|
|
425
|
+
if (image instanceof Canvas){
|
|
426
|
+
this.ƒ('drawCanvas', core(image.getContext('2d')), ...coords)
|
|
427
|
+
}else{
|
|
428
|
+
this.drawImage(image, ...coords)
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
393
432
|
// -- typography ------------------------------------------------------------
|
|
394
433
|
get font(){ return this.prop('font') }
|
|
395
434
|
set font(str){ this.prop('font', css.font(str)) }
|
|
@@ -489,6 +528,8 @@ class FontLibrary extends RustClass {
|
|
|
489
528
|
throw new Error("Expected an array of file paths or an object mapping family names to font files")
|
|
490
529
|
}
|
|
491
530
|
}
|
|
531
|
+
|
|
532
|
+
reset(){ return this.ƒ('reset') }
|
|
492
533
|
}
|
|
493
534
|
|
|
494
535
|
class Image extends RustClass {
|
|
@@ -562,9 +603,17 @@ class Image extends RustClass {
|
|
|
562
603
|
}
|
|
563
604
|
|
|
564
605
|
class ImageData{
|
|
565
|
-
constructor(
|
|
566
|
-
if (
|
|
567
|
-
var {width, height
|
|
606
|
+
constructor(...args){
|
|
607
|
+
if (args[0] instanceof ImageData){
|
|
608
|
+
var {data, width, height} = args[0]
|
|
609
|
+
}else if (args[0] instanceof Uint8ClampedArray || args[0] instanceof Buffer){
|
|
610
|
+
var [data, width, height] = args
|
|
611
|
+
height = height || data.length / width / 4
|
|
612
|
+
if (data.length / 4 != width * height){
|
|
613
|
+
throw new Error("ImageData dimensions must match buffer length")
|
|
614
|
+
}
|
|
615
|
+
}else{
|
|
616
|
+
var [width, height] = args
|
|
568
617
|
}
|
|
569
618
|
|
|
570
619
|
if (!Number.isInteger(width) || !Number.isInteger(height) || width < 0 || height < 0){
|
|
@@ -624,8 +673,8 @@ class Path2D extends RustClass{
|
|
|
624
673
|
}
|
|
625
674
|
|
|
626
675
|
// line segments
|
|
627
|
-
moveTo(x, y){ this.ƒ("moveTo",
|
|
628
|
-
lineTo(x, y){ this.ƒ("lineTo",
|
|
676
|
+
moveTo(x, y){ this.ƒ("moveTo", ...arguments) }
|
|
677
|
+
lineTo(x, y){ this.ƒ("lineTo", ...arguments) }
|
|
629
678
|
closePath(){ this.ƒ("closePath") }
|
|
630
679
|
arcTo(x1, y1, x2, y2, radius){ this.ƒ("arcTo", ...arguments) }
|
|
631
680
|
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y){ this.ƒ("bezierCurveTo", ...arguments) }
|
|
@@ -699,9 +748,7 @@ class TextMetrics{
|
|
|
699
748
|
}
|
|
700
749
|
}
|
|
701
750
|
|
|
702
|
-
const loadImage = src => new
|
|
703
|
-
Object.assign(new Image(), {onload, onerror, src})
|
|
704
|
-
)
|
|
751
|
+
const loadImage = src => Object.assign(new Image(), {src}).decode()
|
|
705
752
|
|
|
706
753
|
module.exports = {
|
|
707
754
|
Canvas, CanvasGradient, CanvasPattern, CanvasRenderingContext2D, CanvasTexture,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skia-canvas",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.30",
|
|
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.
|
|
29
|
-
"
|
|
28
|
+
"@mapbox/node-pre-gyp": "^1.0.9",
|
|
29
|
+
"cargo-cp-artifact": "^0.1",
|
|
30
|
+
"glob": "^8.0.3",
|
|
30
31
|
"path-browserify": "^1.0.1",
|
|
31
|
-
"simple-get": "^4.0.
|
|
32
|
+
"simple-get": "^4.0.1",
|
|
32
33
|
"string-split-by": "^1.0.0"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
36
|
+
"@types/jest": "^27.5.1",
|
|
37
|
+
"@types/node": "^17.0.38",
|
|
38
|
+
"aws-sdk": "^2.1146.0",
|
|
39
|
+
"express": "^4.18.1",
|
|
40
|
+
"jest": "^28.1.0",
|
|
39
41
|
"lodash": "^4.17.21",
|
|
40
|
-
"nodemon": "^2.0.
|
|
42
|
+
"nodemon": "^2.0.16",
|
|
41
43
|
"tmp": "^0.2.1"
|
|
42
44
|
},
|
|
43
|
-
"files":[
|
|
45
|
+
"files": [
|
|
44
46
|
"lib"
|
|
45
47
|
],
|
|
46
48
|
"binary": {
|