@napi-rs/canvas 0.1.23 → 0.1.26

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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # `skr canvas`
2
2
 
3
3
  ![CI](https://github.com/Brooooooklyn/canvas/workflows/CI/badge.svg)
4
- ![Skia Version](https://img.shields.io/badge/Skia-chrome%2Fm103-hotpink)
4
+ ![Skia Version](https://img.shields.io/badge/Skia-chrome%2Fm105-hotpink)
5
5
  [![install size](https://packagephobia.com/badge?p=@napi-rs/canvas)](https://packagephobia.com/result?p=@napi-rs/canvas)
6
6
  [![Downloads](https://img.shields.io/npm/dm/@napi-rs/canvas.svg?sanitize=true)](https://npmcharts.com/compare/@napi-rs/canvas?minimal=true)
7
7
 
package/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ /// <reference lib="dom" />
2
+
1
3
  export interface DOMMatrix2DInit {
2
4
  a: number
3
5
  b: number
@@ -354,7 +356,7 @@ interface IGlobalFonts {
354
356
 
355
357
  export const GlobalFonts: IGlobalFonts
356
358
 
357
- export const enum PathOp {
359
+ export enum PathOp {
358
360
  Difference = 0, // subtract the op path from the first path
359
361
  Intersect = 1, // intersect the two paths
360
362
  Union = 2, // union (inclusive-or) the two paths
@@ -362,29 +364,40 @@ export const enum PathOp {
362
364
  ReverseDifference = 4, // subtract the first path from the op path
363
365
  }
364
366
 
365
- export const enum FillType {
367
+ export enum FillType {
366
368
  Winding = 0,
367
369
  EvenOdd = 1,
368
370
  InverseWinding = 2,
369
371
  InverseEvenOdd = 3,
370
372
  }
371
373
 
372
- export const enum StrokeJoin {
374
+ export enum StrokeJoin {
373
375
  Miter = 0,
374
376
  Round = 1,
375
377
  Bevel = 2,
376
378
  }
377
379
 
378
- export const enum StrokeCap {
380
+ export enum StrokeCap {
379
381
  Butt = 0,
380
382
  Round = 1,
381
383
  Square = 2,
382
384
  }
383
385
 
384
- export const enum SvgExportFlag {
386
+ export enum SvgExportFlag {
385
387
  ConvertTextToPaths = 0x01,
386
388
  NoPrettyXML = 0x02,
387
389
  RelativePathEncoding = 0x04,
388
390
  }
389
391
 
390
392
  export function convertSVGTextToPath(svg: Buffer | string): Buffer
393
+
394
+ export interface LoadImageOptions {
395
+ alt?: string
396
+ maxRedirects?: number
397
+ requestOptions?: import('http').RequestOptions
398
+ }
399
+
400
+ export function loadImage(
401
+ source: string | URL | Buffer | ArrayBufferLike | Uint8Array | Image | import('stream').Readable,
402
+ options?: LoadImageOptions,
403
+ ): Promise<Image>
package/index.js CHANGED
@@ -16,6 +16,8 @@ const {
16
16
 
17
17
  const { DOMPoint, DOMMatrix, DOMRect } = require('./geometry')
18
18
 
19
+ const loadImage = require('./load-image')
20
+
19
21
  const StrokeJoin = {
20
22
  Miter: 0,
21
23
  Round: 1,
@@ -211,87 +213,37 @@ function createCanvas(width, height, flag) {
211
213
  toDataURLAsync: canvasToDataURLAsync,
212
214
  } = Object.getPrototypeOf(canvasElement)
213
215
 
214
- // See also: https://github.com/kornelski/cavif-rs#usage
215
- const avifDefaultQuality = 80
216
- const avifConfig = {
217
- // Quality from 1 (worst) to 100 (best), the default value is 80. The numbers have different meaning than JPEG's quality scale. Beware when comparing codecs. There is no lossless compression support.
218
- quality: avifDefaultQuality,
219
-
220
- // Calculate alphaQuality, this is consistent with cavif.
221
- // https://github.com/kornelski/cavif-rs/blob/37847b95bb81d4cf90e36b7fab2c7fbbcf95abe2/src/main.rs#L97
222
- alphaQuality: Math.min((avifDefaultQuality + 100) / 2, avifDefaultQuality + avifDefaultQuality / 4 + 2),
223
- threads: 0,
224
-
225
- // Encoding speed between 1 (best, but slowest) and 10 (fastest, but a blurry mess), the default value is 4. Speeds 1 and 2 are unbelievably slow, but make files ~3-5% smaller. Speeds 7 and above degrade compression significantly, and are not recommended.
226
- speed: 4,
227
- }
228
-
229
- canvasElement.encode = function encode(type, qualityOrConfig) {
216
+ canvasElement.encode = function encode(type, qualityOrConfig = {}) {
230
217
  if (type === 'avif') {
231
- return canvasEncode.call(
232
- this,
233
- type,
234
- JSON.stringify({
235
- ...avifConfig,
236
- ...(qualityOrConfig || {}),
237
- }),
238
- )
218
+ return canvasEncode.call(this, type, JSON.stringify(qualityOrConfig))
239
219
  }
240
220
  return canvasEncode.call(this, type, qualityOrConfig)
241
221
  }
242
222
 
243
- canvasElement.encodeSync = function encodeSync(type, qualityOrConfig) {
223
+ canvasElement.encodeSync = function encodeSync(type, qualityOrConfig = {}) {
244
224
  if (type === 'avif') {
245
- return canvasEncodeSync.call(
246
- this,
247
- type,
248
- JSON.stringify({
249
- ...avifConfig,
250
- ...(qualityOrConfig || {}),
251
- }),
252
- )
225
+ return canvasEncodeSync.call(this, type, JSON.stringify(qualityOrConfig))
253
226
  }
254
227
  return canvasEncodeSync.call(this, type, qualityOrConfig)
255
228
  }
256
229
 
257
- canvasElement.toBuffer = function toBuffer(type = 'image/png', qualityOrConfig) {
230
+ canvasElement.toBuffer = function toBuffer(type = 'image/png', qualityOrConfig = {}) {
258
231
  if (type === 'avif') {
259
- return canvasToBuffer.call(
260
- this,
261
- type,
262
- JSON.stringify({
263
- ...avifConfig,
264
- ...(qualityOrConfig || {}),
265
- }),
266
- )
232
+ return canvasToBuffer.call(this, type, JSON.stringify(qualityOrConfig))
267
233
  }
268
234
  return canvasToBuffer.call(this, type, qualityOrConfig)
269
235
  }
270
236
 
271
- canvasElement.toDataURL = function toDataURL(type = 'image/png', qualityOrConfig) {
237
+ canvasElement.toDataURL = function toDataURL(type = 'image/png', qualityOrConfig = {}) {
272
238
  if (type === 'avif') {
273
- return canvasToDataURL.call(
274
- this,
275
- type,
276
- JSON.stringify({
277
- ...avifConfig,
278
- ...(qualityOrConfig || {}),
279
- }),
280
- )
239
+ return canvasToDataURL.call(this, type, JSON.stringify(qualityOrConfig))
281
240
  }
282
241
  return canvasToDataURL.call(this, type, qualityOrConfig)
283
242
  }
284
243
 
285
- canvasElement.toDataURLAsync = function toDataURLAsync(type = 'image/png', qualityOrConfig) {
244
+ canvasElement.toDataURLAsync = function toDataURLAsync(type = 'image/png', qualityOrConfig = {}) {
286
245
  if (type === 'avif') {
287
- return canvasToDataURLAsync.call(
288
- this,
289
- type,
290
- JSON.stringify({
291
- ...avifConfig,
292
- ...(qualityOrConfig || {}),
293
- }),
294
- )
246
+ return canvasToDataURLAsync.call(this, type, JSON.stringify(qualityOrConfig))
295
247
  }
296
248
  return canvasToDataURLAsync.call(this, type, qualityOrConfig)
297
249
  }
@@ -344,4 +296,5 @@ module.exports = {
344
296
  DOMPoint,
345
297
  DOMMatrix,
346
298
  DOMRect,
299
+ loadImage,
347
300
  }
package/load-image.js ADDED
@@ -0,0 +1,94 @@
1
+ const fs = require('fs')
2
+ const { Image } = require('./js-binding')
3
+ const { Readable } = require('stream')
4
+
5
+ let http, https
6
+
7
+ const MAX_REDIRECTS = 20,
8
+ REDIRECT_STATUSES = new Set([301, 302]),
9
+ DATA_URI = /^\s*data:/
10
+
11
+ /**
12
+ * Loads the given source into canvas Image
13
+ * @param {string|URL|Image|Buffer} source The image source to be loaded
14
+ * @param {object} options Options passed to the loader
15
+ */
16
+ module.exports = async function loadImage(source, options = {}) {
17
+ // load readable stream as image
18
+ if (source instanceof Readable) return createImage(await consumeStream(source), options.alt)
19
+ // use the same buffer without copying if the source is a buffer
20
+ if (Buffer.isBuffer(source)) return createImage(source, options.alt)
21
+ // construct a buffer if the source is buffer-like
22
+ if (isBufferLike(source)) return createImage(Buffer.from(source), options.alt)
23
+ // if the source is Image instance, copy the image src to new image
24
+ if (source instanceof Image) return createImage(source.src, options.alt)
25
+ // if source is string and in data uri format, construct image using data uri
26
+ if (typeof source === 'string' && DATA_URI.test(source)) {
27
+ const commaIdx = source.indexOf(',')
28
+ const encoding = source.lastIndexOf('base64', commaIdx) < 0 ? 'utf-8' : 'base64'
29
+ const data = Buffer.from(source.slice(commaIdx + 1), encoding)
30
+ return createImage(data, options.alt)
31
+ }
32
+ // if source is a string or URL instance
33
+ if (typeof source === 'string' || source instanceof URL) {
34
+ // if the source exists as a file, construct image from that file
35
+ if (fs.existsSync(source)) {
36
+ return createImage(await fs.promises.readFile(source), options.alt)
37
+ } else {
38
+ // the source is a remote url here
39
+ source = !(source instanceof URL) ? new URL(source) : source
40
+ // attempt to download the remote source and construct image
41
+ const data = await new Promise((resolve, reject) =>
42
+ makeRequest(source, resolve, reject, options.maxRedirects ?? MAX_REDIRECTS, options.requestOptions),
43
+ )
44
+ return createImage(data, options.alt)
45
+ }
46
+ }
47
+
48
+ // throw error as dont support that source
49
+ throw new TypeError('unsupported image source')
50
+ }
51
+
52
+ function makeRequest(url, resolve, reject, redirectCount, requestOptions) {
53
+ const isHttps = url.protocol === 'https:'
54
+ // lazy load the lib
55
+ const lib = isHttps ? (!https ? (https = require('https')) : https) : !http ? (http = require('http')) : http
56
+
57
+ lib.get(url, requestOptions ?? {}, (res) => {
58
+ const shouldRedirect = REDIRECT_STATUSES.has(res.statusCode) && typeof res.headers.location === 'string'
59
+ if (shouldRedirect && redirectCount > 0)
60
+ return makeRequest(new URL(res.headers.location), resolve, reject, redirectCount - 1, requestOptions)
61
+ if (typeof res.statusCode === 'number' && (res.statusCode < 200 || res.statusCode >= 300)) {
62
+ return reject(new Error(`remote source rejected with status code ${res.statusCode}`))
63
+ }
64
+
65
+ consumeStream(res).then(resolve, reject)
66
+ })
67
+ }
68
+
69
+ // use stream/consumers in the future?
70
+ function consumeStream(res) {
71
+ return new Promise((resolve, reject) => {
72
+ const chunks = []
73
+
74
+ res.on('data', (chunk) => chunks.push(chunk))
75
+ res.on('end', () => resolve(Buffer.concat(chunks)))
76
+ res.on('error', reject)
77
+ })
78
+ }
79
+
80
+ function createImage(src, alt) {
81
+ const image = new Image()
82
+ image.src = src
83
+ if (typeof alt === 'string') image.alt = alt
84
+ return image
85
+ }
86
+
87
+ function isBufferLike(src) {
88
+ return (
89
+ Array.isArray(src) ||
90
+ src instanceof ArrayBuffer ||
91
+ src instanceof SharedArrayBuffer ||
92
+ src instanceof Object.getPrototypeOf(Uint8Array)
93
+ )
94
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@napi-rs/canvas",
3
- "version": "0.1.23",
3
+ "version": "0.1.26",
4
4
  "description": "Canvas for Node.js with skia backend",
5
5
  "main": "index.js",
6
6
  "repository": "https://github.com/Brooooooklyn/canvas.git",
@@ -22,7 +22,8 @@
22
22
  "index.d.ts",
23
23
  "index.js",
24
24
  "geometry.js",
25
- "js-binding.js"
25
+ "js-binding.js",
26
+ "load-image.js"
26
27
  ],
27
28
  "napi": {
28
29
  "name": "skia",
@@ -65,7 +66,7 @@
65
66
  "@jimp/jpeg": "^0.16.1",
66
67
  "@jimp/png": "^0.16.1",
67
68
  "@napi-rs/cli": "^2.9.0",
68
- "@octokit/rest": "^18.12.0",
69
+ "@octokit/rest": "^19.0.0",
69
70
  "@swc-node/register": "^1.5.1",
70
71
  "@types/lodash": "^4.14.182",
71
72
  "@types/node": "^17.0.35",
@@ -74,7 +75,7 @@
74
75
  "ava": "^4.2.0",
75
76
  "benny": "^3.7.1",
76
77
  "canvas": "^2.9.1",
77
- "canvaskit-wasm": "^0.34.0",
78
+ "canvaskit-wasm": "^0.35.0",
78
79
  "colorette": "^2.0.16",
79
80
  "conventional-changelog-cli": "^2.2.2",
80
81
  "echarts": "^5.3.2",
@@ -82,7 +83,7 @@
82
83
  "eslint-config-prettier": "^8.5.0",
83
84
  "eslint-plugin-import": "^2.26.0",
84
85
  "eslint-plugin-prettier": "^4.0.0",
85
- "eslint-plugin-sonarjs": "^0.13.0",
86
+ "eslint-plugin-sonarjs": "^0.14.0",
86
87
  "husky": "^8.0.1",
87
88
  "lint-staged": "^13.0.0",
88
89
  "lodash": "^4.17.21",
@@ -90,11 +91,11 @@
90
91
  "pinst": "^3.0.0",
91
92
  "png.js": "^0.2.1",
92
93
  "prettier": "^2.6.2",
93
- "skia-canvas": "^0.9.29",
94
+ "skia-canvas": "^1.0.0",
94
95
  "typescript": "^4.7.2"
95
96
  },
96
97
  "lint-staged": {
97
- "*.@(js|ts|tsx|yml|yaml|md|json)": [
98
+ "*.@(js|ts|tsx|yml|yaml|md|json|html)": [
98
99
  "prettier --write"
99
100
  ],
100
101
  "*.@(js|ts|tsx)": [
@@ -108,6 +109,10 @@
108
109
  "extensions": [
109
110
  "ts"
110
111
  ],
112
+ "files": [
113
+ "__test__/**/*.spec.ts",
114
+ "scripts/__test__/**/*.spec.ts"
115
+ ],
111
116
  "workerThreads": false,
112
117
  "cache": false,
113
118
  "timeout": "3m",
@@ -123,16 +128,16 @@
123
128
  "singleQuote": true,
124
129
  "arrowParens": "always"
125
130
  },
126
- "packageManager": "yarn@3.2.1",
131
+ "packageManager": "yarn@3.2.2",
127
132
  "optionalDependencies": {
128
- "@napi-rs/canvas-win32-x64-msvc": "0.1.23",
129
- "@napi-rs/canvas-darwin-x64": "0.1.23",
130
- "@napi-rs/canvas-linux-x64-gnu": "0.1.23",
131
- "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.23",
132
- "@napi-rs/canvas-linux-x64-musl": "0.1.23",
133
- "@napi-rs/canvas-linux-arm64-gnu": "0.1.23",
134
- "@napi-rs/canvas-linux-arm64-musl": "0.1.23",
135
- "@napi-rs/canvas-darwin-arm64": "0.1.23",
136
- "@napi-rs/canvas-android-arm64": "0.1.23"
133
+ "@napi-rs/canvas-win32-x64-msvc": "0.1.26",
134
+ "@napi-rs/canvas-darwin-x64": "0.1.26",
135
+ "@napi-rs/canvas-linux-x64-gnu": "0.1.26",
136
+ "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.26",
137
+ "@napi-rs/canvas-linux-x64-musl": "0.1.26",
138
+ "@napi-rs/canvas-linux-arm64-gnu": "0.1.26",
139
+ "@napi-rs/canvas-linux-arm64-musl": "0.1.26",
140
+ "@napi-rs/canvas-darwin-arm64": "0.1.26",
141
+ "@napi-rs/canvas-android-arm64": "0.1.26"
137
142
  }
138
143
  }