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 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
- fs.writeFileSync("pilcrow.png", await canvas.png)
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 switch into synchronous mode and save from the main thread
132
- canvas.async = false
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()][toDataURL_mdn] [⚡][toDataURL_ext] |
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
- ##### PROPERTIES
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
- canvas.async = false // switch into synchronous mode
216
- let pngURL = canvas.toDataURL("png")
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] | [getTransform()][getTransform()] |
286
- | [isPointInPath()][isPointInPath()] | [strokeRect()][strokeRect()] | [createConicGradient()][createConicGradient()] | [**lineDashMarker** ⚡][lineDashMarker] | [setTransform()][setTransform()] |
287
- | [isPointInStroke()][isPointInStroke()] | [fillText()][fillText()] ⧸[⚡][drawText] | [createLinearGradient()][createLinearGradient()] | [**lineDashOffset**][lineDashOffset] | [resetTransform()][resetTransform()] |
288
- | [save()][save()] | [strokeText()][strokeText()] ⧸[⚡][drawText] | [createRadialGradient()][createRadialGradient()] | [**lineJoin**][lineJoin] | [transform()][transform()] |
289
- | [restore()][restore()] | [fill()][fill()] | [createPattern()][createPattern()] | [**lineWidth**][lineWidth] | [translate()][translate()] |
290
- | [clip()][clip()] | [stroke()][stroke()] | [createTexture() ⚡][createTexture()] | [**miterLimit**][miterLimit] | [rotate()][rotate()] |
291
- | | | | [getLineDash()][getLineDash()] | [scale()][scale()] |
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
+ ![Paths and text with a perspective transform](/test/assets/path/projection@2x.png)
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
- // TBD: how/if to map the perspective terms
89
- let [a, c, e, b, d, f, p0, p1, p2] = skMatrix
90
- return new geometry.DOMMatrix([a, b, c, d, e, f])
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){ this.prop('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
- if (this.async){
152
- let worker = new EventEmitter()
153
- this.ƒ("save", (result, msg) => worker.emit(result, msg), ...args)
154
- return new Promise((res, rej) => worker.once('ok', res).once('err', msg => rej(new Error(msg))) )
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
- if (this.async){
166
- let worker = new EventEmitter()
167
- this.ƒ("toBuffer", (result, msg) => worker.emit(result, msg), ...args)
168
- return new Promise((res, rej) => worker.once('ok', res).once('err', msg => rej(new Error(msg))) )
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 this.async ? buffer.then(urlify) : urlify(buffer)
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
- resetTransform(){ this.ƒ('resetTransform')}
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, buffer)
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(width, height, data){
566
- if (arguments[0] instanceof ImageData){
567
- var {width, height, data} = arguments[0]
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.27",
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.6",
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
- "aws-sdk": "^2.1013.0",
36
- "cargo-cp-artifact": "^0.1",
37
- "express": "^4.17.1",
38
- "jest": "^27.3.1",
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.14",
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": {