@napi-rs/canvas 1.0.13 → 1.1.0

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
@@ -2,7 +2,6 @@
2
2
 
3
3
  [![CI](https://github.com/Brooooooklyn/canvas/actions/workflows/CI.yaml/badge.svg)](https://github.com/Brooooooklyn/canvas/actions/workflows/CI.yaml)
4
4
  ![Skia Version](https://img.shields.io/badge/Skia-chrome%2Fm157-hotpink)
5
- [![install size](https://packagephobia.com/badge?p=@napi-rs/canvas)](https://packagephobia.com/result?p=@napi-rs/canvas)
6
5
  [![Downloads](https://img.shields.io/npm/dm/@napi-rs/canvas.svg?sanitize=true)](https://npmcharts.com/compare/@napi-rs/canvas?minimal=true)
7
6
 
8
7
  > 🚀 Help me to become a full-time open-source developer by [sponsoring me on Github](https://github.com/sponsors/Brooooooklyn)
@@ -120,6 +119,46 @@ const jpeg = await canvas.encode('jpeg', { quality: 90, resolution: 300, copyrig
120
119
 
121
120
  WebP, AVIF and GIF output ignore these options. `toDataURL`, `toDataURLAsync`, `toBlob`, `convertToBlob`, `encodeStream` and `savePng` never write metadata. To save a file with metadata, write the Buffer from `toBuffer` or `encode`. With an options object, `toBuffer('image/jpeg', ...)` encodes at quality 92; use `encode` or `encodeSync` for another quality.
122
121
 
122
+ ## Text rendering
123
+
124
+ By default text looks the same on every OS. Every font is drawn with FreeType, with Skia's default text gamma, no LCD (subpixel) text and one font family table (the one Linux uses). The same font files give the same pixels and the same `measureText` results on macOS, Linux and Windows.
125
+
126
+ To get the same output everywhere, register your own font files under names no OS installs, and draw only with those names:
127
+
128
+ ```js
129
+ const { GlobalFonts } = require('@napi-rs/canvas')
130
+
131
+ GlobalFonts.registerFromPath('./fonts/Lato-Regular.ttf', 'MyLato')
132
+ ctx.font = '20px MyLato'
133
+ ```
134
+
135
+ These still depend on the OS and the machine:
136
+
137
+ - Installed fonts. Names like `Arial` or `Helvetica`, and the generic families (`sans-serif`, `serif`, `monospace`, `system-ui`, the canvas default `10px sans-serif`) resolve to whatever is installed.
138
+ - Characters your registered fonts lack. They are taken from the installed default fonts when those have them, else they draw as boxes. There is no search through all installed fonts.
139
+ - The `ex`, `ch`, `cap` and line-height units inside SVG images use an installed default font.
140
+ - Lottie (Skottie) text uses installed fonts.
141
+ - PDF font embedding depends on the build target.
142
+ - x64 and arm64 builds can differ by 1 color level on some edge pixels.
143
+ - A `FREETYPE_PROPERTIES` env var changes FreeType's hinting.
144
+
145
+ Set `DISABLE_SYSTEM_FONTS_LOAD=1` to keep installed fonts out of `GlobalFonts.families`. By default they can still be found by name, on every OS.
146
+
147
+ ### `setTextRenderer()`
148
+
149
+ `setTextRenderer('system')` draws text the way Chrome does on each OS: CoreText on macOS (with CoreText's installed fonts and fallback), Fontations on Linux and FreeType on Windows, plus each OS's text gamma, LCD text on opaque macOS canvases, Chrome's per-OS font families and the macOS metric rules. This was the default in 1.0.12 and 1.0.13. `'portable'` is the default.
150
+
151
+ ```js
152
+ const { setTextRenderer, getTextRenderer } = require('@napi-rs/canvas')
153
+
154
+ setTextRenderer('system')
155
+ getTextRenderer() // 'system'
156
+ ```
157
+
158
+ It applies to the whole process. Call it once at startup, before you create canvases. `setTextRenderer('portable')` switches back. Any other value throws a `TypeError`. A canvas created before the switch keeps its old text gamma until you set its width or height. A `ctx.font` value set before the switch keeps its parsed form (sizes in `ex`, `ch` and other font-relative units, and on macOS the `BlinkMacSystemFont` to `system-ui` rewrite), so set `ctx.font` again after the switch.
159
+
160
+ > **Behavior change** ([#1402](https://github.com/Brooooooklyn/canvas/issues/1402)): text on macOS and Linux is drawn the way Windows draws it, so pixels and `measureText` results differ from 1.0.12 and 1.0.13. On macOS, the installed fonts are FreeType faces again (as in 1.0.11), read from `/System/Library/Fonts`, `/Library/Fonts` and `~/Library/Fonts`; CoreText's per-character fallback is off, and `sans-serif`, `serif` and `monospace` map to Arial, Times New Roman and the Linux monospace list. On macOS, `DISABLE_SYSTEM_FONTS_LOAD` no longer hides installed fonts from name lookup, as on Linux and Windows and before 1.0.12; it only keeps them out of `GlobalFonts.families`. Call `setTextRenderer('system')` to get the 1.0.12 / 1.0.13 behavior back.
161
+
123
162
  ## Emoji text
124
163
 
125
164
  ```js
package/index.d.ts CHANGED
@@ -3,6 +3,36 @@ import { ReadableStream } from 'node:stream/web'
3
3
  // Clear all type of caches in Skia
4
4
  export function clearAllCache(): void
5
5
 
6
+ export type TextRenderer = 'portable' | 'system'
7
+
8
+ /**
9
+ * Selects how text is drawn.
10
+ *
11
+ * `'portable'` (the default) draws text the same way on every OS: FreeType,
12
+ * Skia's default text gamma, no LCD text and one font family table. The same
13
+ * registered fonts give the same pixels and TextMetrics on macOS, Linux and
14
+ * Windows.
15
+ *
16
+ * `'system'` draws text with each OS's own font renderer, as Chrome does:
17
+ * CoreText on macOS (Fontations for some color and variable fonts) with
18
+ * CoreText's installed fonts and fallback, Fontations on Linux, FreeType
19
+ * elsewhere; plus each OS's text gamma and contrast, LCD text on opaque macOS
20
+ * canvases, Chrome's per-OS font family settings and macOS metric rules.
21
+ *
22
+ * Applies to the whole process, worker threads included. Call it once at
23
+ * startup, before creating canvases. Setting the mode in use again changes
24
+ * nothing. Any other value throws a TypeError. Registered fonts keep their
25
+ * FontKey. A canvas created before the switch keeps its text gamma until its
26
+ * width or height is set. A `ctx.font` value set before the switch keeps its
27
+ * parsed form: sizes in `ex`/`ch`/... units and, on macOS, the
28
+ * BlinkMacSystemFont to system-ui rewrite. Set `ctx.font` again after the
29
+ * switch.
30
+ */
31
+ export function setTextRenderer(renderer: TextRenderer): void
32
+
33
+ /** The text renderer in use, set by {@link setTextRenderer}. */
34
+ export function getTextRenderer(): TextRenderer
35
+
6
36
  interface CanvasRenderingContext2D
7
37
  extends CanvasCompositing,
8
38
  CanvasDrawPath,
package/index.js CHANGED
@@ -26,6 +26,8 @@ const {
26
26
  _argumentCounts,
27
27
  _imageDataConstructor,
28
28
  _toNumber: toNumber,
29
+ setTextRenderer,
30
+ getTextRenderer,
29
31
  } = require('./js-binding')
30
32
 
31
33
  const { DOMPoint, DOMMatrix, DOMRect } = require('./geometry')
@@ -702,24 +704,24 @@ class Canvas {
702
704
  }
703
705
 
704
706
  if (!process.env.DISABLE_SYSTEM_FONTS_LOAD) {
705
- // macOS: CoreText's installed fonts, user fonts included, listed at the
706
- // first font lookup.
707
- GlobalFonts.enableSystemFonts()
708
- const platformName = platform()
707
+ // Installed fonts and the per-user font folders: FreeType by default;
708
+ // CoreText's on macOS after setTextRenderer('system'). On macOS that is
709
+ // /Library/Fonts (all users) and ~/Library/Fonts, after
710
+ // /System/Library/Fonts, as CoreText lists them.
709
711
  const homedirPath = homedir()
710
- switch (platformName) {
711
- case 'win32':
712
- GlobalFonts.loadFontsFromDir(join(homedirPath, 'AppData', 'Local', 'Microsoft', 'Windows', 'Fonts'))
713
- break
714
- case 'linux':
715
- GlobalFonts.loadFontsFromDir(join('usr', 'local', 'share', 'fonts'))
716
- GlobalFonts.loadFontsFromDir(join(homedirPath, '.fonts'))
717
- break
718
- }
712
+ const userFontDirs =
713
+ {
714
+ win32: [join(homedirPath, 'AppData', 'Local', 'Microsoft', 'Windows', 'Fonts')],
715
+ darwin: ['/Library/Fonts', join(homedirPath, 'Library', 'Fonts')],
716
+ linux: [join('usr', 'local', 'share', 'fonts'), join(homedirPath, '.fonts')],
717
+ }[platform()] ?? []
718
+ GlobalFonts.enableSystemFonts(userFontDirs)
719
719
  }
720
720
 
721
721
  module.exports = {
722
722
  clearAllCache,
723
+ setTextRenderer,
724
+ getTextRenderer,
723
725
  Canvas,
724
726
  createCanvas,
725
727
  Path2D,
package/js-binding.js CHANGED
@@ -455,6 +455,8 @@ module.exports.SVGCanvas = nativeBinding.SVGCanvas
455
455
  module.exports.ChromaSubsampling = nativeBinding.ChromaSubsampling
456
456
  module.exports.clearAllCache = nativeBinding.clearAllCache
457
457
  module.exports.convertSVGTextToPath = nativeBinding.convertSVGTextToPath
458
+ module.exports.setTextRenderer = nativeBinding.setTextRenderer
459
+ module.exports.getTextRenderer = nativeBinding.getTextRenderer
458
460
  module.exports.FillType = nativeBinding.FillType
459
461
  module.exports.PathOp = nativeBinding.PathOp
460
462
  module.exports.StrokeCap = nativeBinding.StrokeCap
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@napi-rs/canvas",
3
- "version": "1.0.13",
3
+ "version": "1.1.0",
4
4
  "description": "Canvas for Node.js with skia backend",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -71,6 +71,7 @@
71
71
  "test:ci": "NODE_OPTIONS='--import=core-js/proposals/iterator-helpers.js' ava -c 1",
72
72
  "test": "NODE_OPTIONS='--import=core-js/proposals/iterator-helpers.js' ava",
73
73
  "e2e": "yarn workspace @napi-rs/canvas-e2e-webpack test",
74
+ "e2e:electron": "yarn workspace @napi-rs/canvas-e2e-electron test",
74
75
  "version": "napi version && conventional-changelog -p angular -i CHANGELOG.md -s && git add ."
75
76
  },
76
77
  "devDependencies": {
@@ -82,11 +83,11 @@
82
83
  "@napi-rs/webcodecs": "^1.1.1",
83
84
  "@node-rs/crc32": "^1.10.6",
84
85
  "@octokit/rest": "^22.0.1",
85
- "@oxc-node/cli": "^0.1.0",
86
- "@oxc-node/core": "^0.1.0",
86
+ "@oxc-node/cli": "^1.0.0",
87
+ "@oxc-node/core": "^1.0.0",
87
88
  "@taplo/cli": "^0.7.0",
88
89
  "@types/lodash": "^4.17.21",
89
- "@types/node": "^25.0.3",
90
+ "@types/node": "^26.0.0",
90
91
  "@types/semver": "^7",
91
92
  "ava": "^8.0.0",
92
93
  "canvas": "^3.2.0",
@@ -95,7 +96,7 @@
95
96
  "conventional-changelog-cli": "^5.0.0",
96
97
  "core-js": "^3.47.0",
97
98
  "echarts": "^6.0.0",
98
- "electron": "^44.0.0",
99
+ "electron": "^45.0.0",
99
100
  "husky": "^9.1.7",
100
101
  "lint-staged": "^17.0.0",
101
102
  "lodash": "^4.17.21",
@@ -161,16 +162,16 @@
161
162
  "@napi-rs/canvas": "workspace:"
162
163
  },
163
164
  "optionalDependencies": {
164
- "@napi-rs/canvas-linux-x64-gnu": "1.0.13",
165
- "@napi-rs/canvas-darwin-x64": "1.0.13",
166
- "@napi-rs/canvas-win32-x64-msvc": "1.0.13",
167
- "@napi-rs/canvas-win32-arm64-msvc": "1.0.13",
168
- "@napi-rs/canvas-linux-arm-gnueabihf": "1.0.13",
169
- "@napi-rs/canvas-linux-x64-musl": "1.0.13",
170
- "@napi-rs/canvas-linux-arm64-gnu": "1.0.13",
171
- "@napi-rs/canvas-linux-arm64-musl": "1.0.13",
172
- "@napi-rs/canvas-darwin-arm64": "1.0.13",
173
- "@napi-rs/canvas-android-arm64": "1.0.13",
174
- "@napi-rs/canvas-linux-riscv64-gnu": "1.0.13"
165
+ "@napi-rs/canvas-linux-x64-gnu": "1.1.0",
166
+ "@napi-rs/canvas-darwin-x64": "1.1.0",
167
+ "@napi-rs/canvas-win32-x64-msvc": "1.1.0",
168
+ "@napi-rs/canvas-win32-arm64-msvc": "1.1.0",
169
+ "@napi-rs/canvas-linux-arm-gnueabihf": "1.1.0",
170
+ "@napi-rs/canvas-linux-x64-musl": "1.1.0",
171
+ "@napi-rs/canvas-linux-arm64-gnu": "1.1.0",
172
+ "@napi-rs/canvas-linux-arm64-musl": "1.1.0",
173
+ "@napi-rs/canvas-darwin-arm64": "1.1.0",
174
+ "@napi-rs/canvas-android-arm64": "1.1.0",
175
+ "@napi-rs/canvas-linux-riscv64-gnu": "1.1.0"
175
176
  }
176
177
  }