skia-canvas 0.9.29 → 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 CHANGED
@@ -2,6 +2,32 @@
2
2
 
3
3
  <!-- ## 🥚 ⟩ [Unreleased] -->
4
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
+
5
31
  ## 📦 ⟩ [v0.9.29] ⟩ Feb 7, 2022
6
32
 
7
33
  ### New Features
@@ -202,7 +228,8 @@
202
228
 
203
229
  **Initial public release** 🎉
204
230
 
205
- [unreleased]: https://github.com/samizdatco/skia-canvas/compare/v0.9.29...HEAD
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
206
233
  [v0.9.29]: https://github.com/samizdatco/skia-canvas/compare/v0.9.28...v0.9.29
207
234
  [v0.9.28]: https://github.com/samizdatco/skia-canvas/compare/v0.9.27...v0.9.28
208
235
  [v0.9.27]: https://github.com/samizdatco/skia-canvas/compare/v0.9.26...v0.9.27
package/README.md CHANGED
@@ -9,7 +9,7 @@ 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 [channels](https://docs.rs/neon/0.9.0/neon/event/struct.Channel.html) for asynchronous rendering and file I/O
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
15
  - can apply [3D perspective][createProjection()] transformations in addition to [scaling][scale()], [rotation][rotate()], and [translation][translate()]
@@ -52,9 +52,9 @@ Nearly everything you need is statically linked into the library. A notable exce
52
52
 
53
53
  ### Running in Docker
54
54
 
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) 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.
56
56
 
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:buster`, `node:stretch`, or simply:
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:
58
58
  ```dockerfile
59
59
  FROM node
60
60
  ```
@@ -398,11 +398,11 @@ The geometry of the quadrilateral should be described as an Array of either 8 or
398
398
  [left, top, right, bottom] // four edges of a rectangle
399
399
 
400
400
  // internal arrays for grouping are also allowed
401
- [[x1, y1], [x2, y2], [x3, y3], [x4, y4]]
401
+ [[x1, y1], [x2, y2], [x3, y3], [x4, y4]]
402
402
  ```
403
403
 
404
404
  ##### `basis`
405
-
405
+
406
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
407
 
408
408
  The `basis` polygon can be described using 2, 4, or 8 numbers, using the canvas dimensions to fill in the unspecified coordinates:
@@ -414,7 +414,7 @@ The `basis` polygon can be described using 2, 4, or 8 numbers, using the canvas
414
414
 
415
415
  ----
416
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.
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
418
 
419
419
  ```js
420
420
  let canvas = new Canvas(512, 512),
@@ -788,6 +788,13 @@ img.onload = function(){
788
788
  img.src = 'https://example.com/icon.png'
789
789
  ```
790
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
+
791
798
  ```js
792
799
  let img = await loadImage('https://example.com/icon.png')
793
800
  ctx.drawImage(img, 100, 100)
@@ -821,6 +828,10 @@ Asking for details about an unknown family will return `undefined`.
821
828
 
822
829
  Returns `true` if the family is installed on the system or has been added via `FontLibrary.use()`.
823
830
 
831
+ ##### `reset()`
832
+
833
+ Uninstalls any dynamically loaded fonts that had been added via `FontLibrary.use()`.
834
+
824
835
  ##### `use(familyName, [...fontPaths])`
825
836
 
826
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:
@@ -844,6 +855,8 @@ FontLibrary.use("Grizwald", [
844
855
 
845
856
  ###### with a list of ‘glob’ patterns
846
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
+
847
860
  ```js
848
861
  // with default family name
849
862
  FontLibrary.use(['fonts/Crimson_Pro/*.ttf'])
@@ -990,4 +1003,6 @@ Many thanks to the [`node-canvas`](https://github.com/Automattic/node-canvas) de
990
1003
  [translate()]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/translate
991
1004
 
992
1005
  [nonzero]: https://en.wikipedia.org/wiki/Nonzero-rule
993
- [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 => new Promise((onload, onerror) =>
13
- Object.assign(new Image(), {crossOrigin:'Anonymous', onload, onerror, src})
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 CHANGED
@@ -229,6 +229,8 @@ export interface FontLibrary {
229
229
  use(
230
230
  families: Record<string, readonly string[] | string>
231
231
  ): Record<string, Font[] | Font>
232
+
233
+ reset(): void
232
234
  }
233
235
 
234
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
- return this.native[fn](this[ø], ...args)
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
 
@@ -158,10 +163,8 @@ class Canvas extends RustClass{
158
163
 
159
164
  opts = typeof opts=='number' ? {quality:opts} : opts
160
165
  let {format, quality, pages, padding, pattern, density, outline, matte} = io.options(this.pages, {filename, ...opts}),
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))) )
166
+ args = [pages.map(core), pattern, padding, format, quality, density, outline, matte]
167
+ return this.ƒ("save", ...args)
165
168
  }
166
169
 
167
170
  saveAsSync(filename, opts={}){
@@ -175,10 +178,8 @@ class Canvas extends RustClass{
175
178
 
176
179
  opts = typeof opts=='number' ? {quality:opts} : opts
177
180
  let {format, quality, pages, density, outline, matte} = io.options(this.pages, {extension, ...opts}),
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))) )
181
+ args = [pages.map(core), format, quality, density, outline, matte];
182
+ return this.ƒ("toBuffer", ...args)
182
183
  }
183
184
 
184
185
  toBufferSync(extension="png", opts={}){
@@ -292,10 +293,10 @@ class CanvasRenderingContext2D extends RustClass{
292
293
  this.currentTransform = arguments.length > 1 ? [...arguments] : matrix
293
294
  }
294
295
 
295
- transform(...terms){ this.ƒ('transform', ...terms)}
296
- translate(x, y){ this.ƒ('translate', x, y)}
297
- scale(x, y){ this.ƒ('scale', x, y)}
298
- rotate(angle){ this.ƒ('rotate', angle)}
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)}
299
300
 
300
301
  createProjection(quad, basis){
301
302
  return fromSkMatrix(this.ƒ("createProjection", [quad].flat(), [basis].flat()))
@@ -306,15 +307,15 @@ class CanvasRenderingContext2D extends RustClass{
306
307
  rect(x, y, width, height){ this.ƒ('rect', ...arguments) }
307
308
  arc(x, y, radius, startAngle, endAngle, isCCW){ this.ƒ('arc', ...arguments) }
308
309
  ellipse(x, y, xRadius, yRadius, rotation, startAngle, endAngle, isCCW){ this.ƒ('ellipse', ...arguments) }
309
- moveTo(x, y){ this.ƒ('moveTo', x, y) }
310
- lineTo(x, y){ this.ƒ('lineTo', x, y) }
310
+ moveTo(x, y){ this.ƒ('moveTo', ...arguments) }
311
+ lineTo(x, y){ this.ƒ('lineTo', ...arguments) }
311
312
  arcTo(x1, y1, x2, y2, radius){ this.ƒ('arcTo', ...arguments) }
312
313
  bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y){ this.ƒ('bezierCurveTo', ...arguments) }
313
314
  quadraticCurveTo(cpx, cpy, x, y){ this.ƒ('quadraticCurveTo', ...arguments) }
314
315
  conicCurveTo(cpx, cpy, x, y, weight){ this.ƒ("conicCurveTo", ...arguments) }
315
316
  closePath(){ this.ƒ('closePath') }
316
- isPointInPath(x, y){ return this.ƒ('isPointInPath', x, y) }
317
- isPointInStroke(x, y){ return this.ƒ('isPointInStroke', x, y) }
317
+ isPointInPath(x, y){ return this.ƒ('isPointInPath', ...arguments) }
318
+ isPointInStroke(x, y){ return this.ƒ('isPointInStroke', ...arguments) }
318
319
 
319
320
  // -- using paths -----------------------------------------------------------
320
321
  fill(path, rule){
@@ -527,6 +528,8 @@ class FontLibrary extends RustClass {
527
528
  throw new Error("Expected an array of file paths or an object mapping family names to font files")
528
529
  }
529
530
  }
531
+
532
+ reset(){ return this.ƒ('reset') }
530
533
  }
531
534
 
532
535
  class Image extends RustClass {
@@ -670,8 +673,8 @@ class Path2D extends RustClass{
670
673
  }
671
674
 
672
675
  // line segments
673
- moveTo(x, y){ this.ƒ("moveTo", x, y) }
674
- lineTo(x, y){ this.ƒ("lineTo", x, y) }
676
+ moveTo(x, y){ this.ƒ("moveTo", ...arguments) }
677
+ lineTo(x, y){ this.ƒ("lineTo", ...arguments) }
675
678
  closePath(){ this.ƒ("closePath") }
676
679
  arcTo(x1, y1, x2, y2, radius){ this.ƒ("arcTo", ...arguments) }
677
680
  bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y){ this.ƒ("bezierCurveTo", ...arguments) }
@@ -745,9 +748,7 @@ class TextMetrics{
745
748
  }
746
749
  }
747
750
 
748
- const loadImage = src => new Promise((onload, onerror) =>
749
- Object.assign(new Image(), {onload, onerror, src})
750
- )
751
+ const loadImage = src => Object.assign(new Image(), {src}).decode()
751
752
 
752
753
  module.exports = {
753
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.29",
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,21 +25,21 @@
25
25
  "test": "jest"
26
26
  },
27
27
  "dependencies": {
28
- "@mapbox/node-pre-gyp": "^1.0.8",
28
+ "@mapbox/node-pre-gyp": "^1.0.9",
29
29
  "cargo-cp-artifact": "^0.1",
30
- "glob": "^7.2.0",
30
+ "glob": "^8.0.3",
31
31
  "path-browserify": "^1.0.1",
32
32
  "simple-get": "^4.0.1",
33
33
  "string-split-by": "^1.0.0"
34
34
  },
35
35
  "devDependencies": {
36
- "@types/jest": "^27.4.0",
37
- "@types/node": "^17.0.15",
38
- "aws-sdk": "^2.1069.0",
39
- "express": "^4.17.2",
40
- "jest": "^27.5.0",
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",
41
41
  "lodash": "^4.17.21",
42
- "nodemon": "^2.0.15",
42
+ "nodemon": "^2.0.16",
43
43
  "tmp": "^0.2.1"
44
44
  },
45
45
  "files": [