Socket
Socket
Sign inDemoInstall

skia-canvas

Package Overview
Dependencies
65
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.9.27 to 0.9.28

CHANGELOG.md

102

lib/index.js

@@ -79,14 +79,19 @@ "use strict"

function toSkMatrix(jsMatrix){
if (Array.isArray(jsMatrix)){
var [a, b, c, d, e, f] = jsMatrix
}else{
var {a, b, c, d, e, f} = jsMatrix
if (Array.isArray(jsMatrix) && jsMatrix.length==6){
var [a, b, c, d, e, f, m14, m24, m44] = jsMatrix.concat(0, 0, 1)
}else if (jsMatrix instanceof geometry.DOMMatrix){
var {a, b, c, d, e, f, m14, m24, m44} = jsMatrix
}
return [a, c, e, b, d, f]
return [a, c, e, b, d, f, m14, m24, m44]
}
function fromSkMatrix(skMatrix){
// TBD: how/if to map the perspective terms
let [a, c, e, b, d, f, p0, p1, p2] = skMatrix
return new geometry.DOMMatrix([a, b, c, d, e, f])
let [a, b, c, d, e, f, p0, p1, p2] = skMatrix
return new geometry.DOMMatrix([
a, d, 0, p0,
b, e, 0, p1,
0, 0, 1, 0,
c, f, 0, p2
])
}

@@ -145,40 +150,60 @@

get async(){ return this.prop('async') }
set async(flag){ this.prop('async', flag) }
set async(flag){
if (!flag){
process.emitWarning("Use the saveAsSync, toBufferSync, and toDataURLSync methods instead of setting the Canvas `async` property to false", "DeprecationWarning")
}
this.prop('async', flag)
}
saveAs(filename, opts={}){
if (!this.async) return this.saveAsSync(...arguments) // support while deprecated
opts = typeof opts=='number' ? {quality:opts} : opts
let {format, quality, pages, padding, pattern, density, outline, matte} = io.options(this.pages, {filename, ...opts}),
args = [pages.map(core), pattern, padding, format, quality, density, outline, matte];
args = [pages.map(core), pattern, padding, format, quality, density, outline, matte],
worker = new EventEmitter();
this.ƒ("save", (result, msg) => worker.emit(result, msg), ...args)
return new Promise((res, rej) => worker.once('ok', res).once('err', msg => rej(new Error(msg))) )
}
if (this.async){
let worker = new EventEmitter()
this.ƒ("save", (result, msg) => worker.emit(result, msg), ...args)
return new Promise((res, rej) => worker.once('ok', res).once('err', msg => rej(new Error(msg))) )
}else{
this.ƒ("saveSync", ...args)
}
saveAsSync(filename, opts={}){
opts = typeof opts=='number' ? {quality:opts} : opts
let {format, quality, pages, padding, pattern, density, outline, matte} = io.options(this.pages, {filename, ...opts})
this.ƒ("saveSync", pages.map(core), pattern, padding, format, quality, density, outline, matte)
}
toBuffer(extension="png", opts={}){
if (!this.async) return this.toBufferSync(...arguments) // support while deprecated
opts = typeof opts=='number' ? {quality:opts} : opts
let {format, quality, pages, density, outline, matte} = io.options(this.pages, {extension, ...opts}),
args = [pages.map(core), format, quality, density, outline, matte];
args = [pages.map(core), format, quality, density, outline, matte],
worker = new EventEmitter();
this.ƒ("toBuffer", (result, msg) => worker.emit(result, msg), ...args)
return new Promise((res, rej) => worker.once('ok', res).once('err', msg => rej(new Error(msg))) )
}
if (this.async){
let worker = new EventEmitter()
this.ƒ("toBuffer", (result, msg) => worker.emit(result, msg), ...args)
return new Promise((res, rej) => worker.once('ok', res).once('err', msg => rej(new Error(msg))) )
}else{
return this.ƒ("toBufferSync", ...args)
}
toBufferSync(extension="png", opts={}){
opts = typeof opts=='number' ? {quality:opts} : opts
let {format, quality, pages, density, outline, matte} = io.options(this.pages, {extension, ...opts})
return this.ƒ("toBufferSync", pages.map(core), format, quality, density, outline, matte)
}
toDataURL(extension="png", opts={}){
if (!this.async) return this.toDataURLSync(...arguments) // support while deprecated
opts = typeof opts=='number' ? {quality:opts} : opts
let {mime} = io.options(this.pages, {extension, ...opts}),
urlify = data => `data:${mime};base64,${data.toString('base64')}`,
buffer = this.toBuffer(extension, opts);
return this.async ? buffer.then(urlify) : urlify(buffer)
return buffer.then(data => `data:${mime};base64,${data.toString('base64')}`)
}
toDataURLSync(extension="png", opts={}){
opts = typeof opts=='number' ? {quality:opts} : opts
let {mime} = io.options(this.pages, {extension, ...opts}),
buffer = this.toBufferSync(extension, opts);
return `data:${mime};base64,${buffer.toString('base64')}`
}
[REPR](depth, options) {

@@ -266,2 +291,3 @@ let {width, height, async, pages} = this

resetTransform(){ this.ƒ('resetTransform')}
getTransform(){ return this.currentTransform }

@@ -271,2 +297,3 @@ setTransform(matrix){

}
transform(...terms){ this.ƒ('transform', ...terms)}

@@ -276,4 +303,7 @@ translate(x, y){ this.ƒ('translate', x, y)}

rotate(angle){ this.ƒ('rotate', angle)}
resetTransform(){ this.ƒ('resetTransform')}
createProjection(quad, basis){
return fromSkMatrix(this.ƒ("createProjection", [quad].flat(), [basis].flat()))
}
// -- bézier paths ----------------------------------------------------------

@@ -385,3 +415,3 @@ beginPath(){ this.ƒ('beginPath') }

buffer = this.ƒ('getImageData', x, y, w, h);
return new ImageData(w, h, buffer)
return new ImageData(buffer, w, h)
}

@@ -571,5 +601,13 @@

class ImageData{
constructor(width, height, data){
if (arguments[0] instanceof ImageData){
var {width, height, data} = arguments[0]
constructor(...args){
if (args[0] instanceof ImageData){
var {data, width, height} = args[0]
}else if (args[0] instanceof Uint8ClampedArray || args[0] instanceof Buffer){
var [data, width, height] = args
height = height || data.length / width / 4
if (data.length / 4 != width * height){
throw new Error("ImageData dimensions must match buffer length")
}
}else{
var [width, height] = args
}

@@ -576,0 +614,0 @@

{
"name": "skia-canvas",
"version": "0.9.27",
"version": "0.9.28",
"description": "A canvas environment for Node",

@@ -28,3 +28,4 @@ "author": "Christian Swinehart <drafting@samizdat.co>",

"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.6",
"@mapbox/node-pre-gyp": "^1.0.8",
"cargo-cp-artifact": "^0.1",
"glob": "^7.2.0",

@@ -36,11 +37,12 @@ "path-browserify": "^1.0.1",

"devDependencies": {
"aws-sdk": "^2.1013.0",
"cargo-cp-artifact": "^0.1",
"express": "^4.17.1",
"jest": "^27.3.1",
"@types/jest": "^27.4.0",
"@types/node": "^17.0.8",
"aws-sdk": "^2.1053.0",
"express": "^4.17.2",
"jest": "^27.4.7",
"lodash": "^4.17.21",
"nodemon": "^2.0.14",
"nodemon": "^2.0.15",
"tmp": "^0.2.1"
},
"files":[
"files": [
"lib"

@@ -47,0 +49,0 @@ ],

@@ -15,2 +15,3 @@ # Skia Canvas

- 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]
- can apply [3D perspective][createProjection()] transformations in addition to [scaling][scale()], [rotation][rotate()], and [translation][translate()]
- can fill shapes with vector-based [Textures][createTexture()] in addition to bitmap-based [Patterns][createPattern()] and supports line-drawing with custom [markers][lineDashMarker]

@@ -126,3 +127,3 @@ - fully supports the [CSS filter effects][filter] image processing operators

// ...or use a shorthand for canvas.toBuffer("png")
fs.writeFileSync("pilcrow.png", await canvas.png)
let pngData = await canvas.png
// ...or embed it in a string

@@ -133,5 +134,4 @@ console.log(`<img src="${await canvas.toDataURL("png")}">`)

// ...or switch into synchronous mode and save from the main thread
canvas.async = false
canvas.saveAs("pilcrow.png")
// ...or save the file synchronously from the main thread
canvas.saveAsSync("pilcrow.png")
```

@@ -169,7 +169,7 @@

| -- | -- | -- |
| [**width**][canvas_width] | [**pages**][canvas_pages] ⚡ | [**async**][canvas_async] ⚡ |
| [**width**][canvas_width] | [**pages**][canvas_pages] ⚡ | ~~[**async**][canvas_async]~~ ⚡ |
| [**height**][canvas_height] | [getContext()][getContext] | [**pdf**, **png**, **svg**, **jpg**][shorthands] ⚡ |
| | [newPage()][newPage] ⚡ | [saveAs()][saveAs] ⚡ |
| | | [toBuffer()][toBuffer] ⚡ |
| | | [toDataURL()][toDataURL_mdn] [⚡][toDataURL_ext] |
| | [newPage()][newPage] ⚡ | [saveAs()][saveAs] / [saveAsSync()][saveAs] ⚡ |
| | | [toBuffer()][toBuffer] / [toBufferSync()][toBuffer] ⚡ |
| | | [toDataURL()][toDataURL_ext] / [toDataURLSync()][toDataURL_ext] ⚡ |

@@ -181,7 +181,7 @@ [canvas_width]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/width

[getContext]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext
[saveAs]: #saveasfilename-page-format-density1-quality092-outlinefalse
[toBuffer]: #tobufferformat-page-density-quality-outline
[saveAs]: #saveasfilename-page-format-matte-density1-quality092-outlinefalse
[toBuffer]: #tobufferformat-page-matte-density-quality-outline
[newPage]: #newpagewidth-height
[toDataURL_mdn]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
[toDataURL_ext]: #todataurlformat-page-density-quality-outline
[toDataURL_ext]: #todataurlformat-page-matte-density-quality-outline
[shorthands]: #pdf-svg-jpg-and-png

@@ -198,7 +198,5 @@

##### PROPERTIES
#### Saving graphics to files, buffers, and strings
#### `.async`
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:
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):
- [`saveAs()`][saveAs]

@@ -209,7 +207,12 @@ - [`toBuffer()`][toBuffer]

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):
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:
- [`saveAsSync()`][saveAs]
- [`toBufferSync()`][toBuffer]
- [`toDataURLSync()`][toDataURL_ext]
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):
```js
let canvas = new Canvas()
console.log(canvas.async) // -> true by default

@@ -222,10 +225,12 @@ async function normal(){

function synchronous(){
canvas.async = false // switch into synchronous mode
let pngURL = canvas.toDataURL("png")
let pdfBuffer = canvas.pdf
let pngURL = canvas.toDataURLSync("png")
let pdfBuffer = canvas.toBufferSync("pdf")
}
```
##### PROPERTIES
#### ~~`.async`~~
**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.

@@ -259,2 +264,6 @@ #### `.pages`

##### format
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.
##### matte

@@ -277,7 +286,7 @@ 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.

#### `toBuffer(format, {page, density, quality, outline})`
#### `toBuffer(format, {page, matte, density, quality, outline})`
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.
#### `toDataURL(format, {page, density, quality, outline})`
#### `toDataURL(format, {page, matte, density, quality, outline})`

@@ -292,15 +301,30 @@ 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.

| Canvas State | Drawing | Pattern & Color | Line Style | Transform |
|------------------------------------------|----------------------------------------------|---------------------------------------------------|-----------------------------------------|------------------------------------------|
| [**canvas**][canvas_attr] ⧸[⚡](#canvas) | [clearRect()][clearRect()] | [**fillStyle**][fillStyle] | [**lineCap**][lineCap] | [**currentTransform**][currentTransform] |
| [beginPath()][beginPath()] | [fillRect()][fillRect()] | [**strokeStyle**][strokeStyle] | [**lineDashFit** ⚡][lineDashFit] | [getTransform()][getTransform()] |
| [isPointInPath()][isPointInPath()] | [strokeRect()][strokeRect()] | [createConicGradient()][createConicGradient()] | [**lineDashMarker** ⚡][lineDashMarker] | [setTransform()][setTransform()] |
| [isPointInStroke()][isPointInStroke()] | [fillText()][fillText()] ⧸[⚡][drawText] | [createLinearGradient()][createLinearGradient()] | [**lineDashOffset**][lineDashOffset] | [resetTransform()][resetTransform()] |
| [save()][save()] | [strokeText()][strokeText()] ⧸[⚡][drawText] | [createRadialGradient()][createRadialGradient()] | [**lineJoin**][lineJoin] | [transform()][transform()] |
| [restore()][restore()] | [fill()][fill()] | [createPattern()][createPattern()] | [**lineWidth**][lineWidth] | [translate()][translate()] |
| [clip()][clip()] | [stroke()][stroke()] | [createTexture() ⚡][createTexture()] | [**miterLimit**][miterLimit] | [rotate()][rotate()] |
| | | | [getLineDash()][getLineDash()] | [scale()][scale()] |
| | | | [setLineDash()][setLineDash()] | |
| Canvas State | Drawing | Pattern & Color | Line Style | Transform |
|------------------------------------------|----------------------------------------------|---------------------------------------------------|-----------------------------------------|---------------------------------------------|
| [**canvas**][canvas_attr] ⧸[⚡](#canvas) | [clearRect()][clearRect()] | [**fillStyle**][fillStyle] | [**lineCap**][lineCap] | [**currentTransform**][currentTransform] |
| [beginPath()][beginPath()] | [fillRect()][fillRect()] | [**strokeStyle**][strokeStyle] | [**lineDashFit** ⚡][lineDashFit] | [createProjection() ⚡][createProjection()] |
| [isPointInPath()][isPointInPath()] | [strokeRect()][strokeRect()] | [createConicGradient()][createConicGradient()] | [**lineDashMarker** ⚡][lineDashMarker] | [getTransform()][getTransform()] |
| [isPointInStroke()][isPointInStroke()] | [fillText()][fillText()] ⧸[⚡][drawText] | [createLinearGradient()][createLinearGradient()] | [**lineDashOffset**][lineDashOffset] | [setTransform()][setTransform()] |
| [save()][save()] | [strokeText()][strokeText()] ⧸[⚡][drawText] | [createRadialGradient()][createRadialGradient()] | [**lineJoin**][lineJoin] | [resetTransform()][resetTransform()] |
| [restore()][restore()] | [fill()][fill()] | [createPattern()][createPattern()] | [**lineWidth**][lineWidth] | [transform()][transform()] |
| [clip()][clip()] | [stroke()][stroke()] | [createTexture() ⚡][createTexture()] | [**miterLimit**][miterLimit] | [translate()][translate()] |
| | | | [getLineDash()][getLineDash()] | [rotate()][rotate()] |
| | | | [setLineDash()][setLineDash()] | [scale()][scale()] |
| Bezier Paths | Typography | Images | Compositing Effects |

@@ -403,2 +427,67 @@ |------------------------------------------|-------------------------------------------------------------|----------------------------------------------------|----------------------------------------------------------|

#### `createProjection(quad, [basis])`
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.
##### `quad`
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’.
The geometry of the quadrilateral should be described as an Array of either 8 or 4 numbers specifying an arbitrary polygon or rectangle respectively:
```js
[x1, y1, x2, y2, x3, y3, x4, y4] // four corner points
[left, top, right, bottom] // four edges of a rectangle
// internal arrays for grouping are also allowed
[[x1, y1], [x2, y2], [x3, y3], [x4, y4]]
```
##### `basis`
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.
The `basis` polygon can be described using 2, 4, or 8 numbers, using the canvas dimensions to fill in the unspecified coordinates:
```js
[width, height] // rectangle from ⟨0, 0⟩ to ⟨width, height⟩
[left, top, right, bottom] // four edges of a rectangle
[x1, y1, x2, y2, x3, y3, x4, y4] // four corner points
```
----
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.
```js
let canvas = new Canvas(512, 512),
ctx = canvas.getContext("2d"),
{width:w, height:h} = canvas;
ctx.font = '900 480px Times'
ctx.textAlign = 'center'
ctx.fillStyle = '#aaa'
ctx.fillRect(0, 0, w, h)
let quad = [
w*.33, h/2, // upper left
w*.66, h/2, // upper right
w, h*.9, // bottom right
0, h*.9, // bottom left
]
let matrix = ctx.createProjection(quad) // use default basis
ctx.setTransform(matrix)
ctx.fillStyle = 'white'
ctx.fillRect(10, 10, w-20, h-20)
ctx.fillStyle = '#900'
ctx.fillText("@", w/2, h-40)
```
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:
![Paths and text with a perspective transform](/test/assets/path/projection@2x.png)
#### `createTexture(spacing, {path, line, color, angle, offset=0})`

@@ -845,2 +934,3 @@

[createTexture()]: #createtexturespacing-path-line-color-angle-offset0
[createProjection()]: #createprojectionquad-basis
[lineDashMarker]: #linedashmarker

@@ -847,0 +937,0 @@ [lineDashFit]: #linedashfit

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc