text-to-canvas 1.1.2 → 1.2.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/CHANGELOG.md CHANGED
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
5
5
  The format is inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## v1.2.0
9
+
10
+ - New `strokeColor` and `strokeWidth` text formatting options to control the outline of the text ([#292](https://github.com/stefcameron/text-to-canvas/issues/292)).
11
+ - Note that due to how the `strokeText()` and `measureText()` Canvas APIs work, the stroke is __not considered__ in text placement. Setting a large width will result in the stroke "bleeding" outside the text box.
12
+
8
13
  ## v1.1.2
9
14
 
10
15
  - Fixed bug where `drawText()` config `fontColor` option was not being included in the base font format used to render the text ([#64](https://github.com/stefcameron/text-to-canvas/issues/64)).
package/README.md CHANGED
@@ -49,6 +49,8 @@ $ yarn add text-to-canvas
49
49
 
50
50
  This project __optionally__ depends on the [canvas](https://github.com/Automattic/node-canvas) package which enables it to be used in a Node [demo](#node).
51
51
 
52
+ > ❗️ Note this is __optional__ as `text-to-canvas` does not formally support this library. This is purely for casual testing and as an example of how `text-to-canvas` should technically work with any library that supports the `HTMLCanvasElement` API. `text-to-canvas` only officially supports HTML `<canvas>`.
53
+
52
54
  Since this package needs to be compiled for use on the platform on which you intend to install/use it, the author must either include pre-built binaries specific to your OS when they make a [release](https://github.com/Automattic/node-canvas/releases), or a new binary must be compiled by your package manager (i.e. `npm`) upon installation.
53
55
 
54
56
  If you're installing on a newer Apple M1, M2, or M3 computer, or if you're using a version of Node newer than v20 (the latest LTS at time of writing), you may experience a `node-pre-gyp` failure because `canvas` doesn't provide pre-built binaries for the ARM64 architecture, only providing x86-64 (Intel x64) binaries for Node v20.
@@ -100,7 +102,7 @@ const text = 'Lorem ipsum dolor sit amet';
100
102
  // OR with some formatting
101
103
  const text: Word[] = [
102
104
  { text: 'Lorem' },
103
- { text: 'ipsum', format: { fontWeight: 'bold', color: 'red' } },
105
+ { text: 'ipsum', format: { fontWeight: 'bold', fontColor: 'red' } },
104
106
  { text: 'dolor', format: { fontStyle: 'italic' } },
105
107
  { text: 'sit' },
106
108
  { text: 'amet' },
@@ -177,12 +179,14 @@ You can run this demo locally with `npm run node:demo`
177
179
  | `y` | `0` | Y position of the text box. |
178
180
  | `align` | `center` | Text align. Other possible values: `left`, `right`. |
179
181
  | `vAlign` | `middle` | Text vertical align. Other possible values: `top`, `bottom`. |
180
- | `font` | `Arial` | Base font family of the text. |
182
+ | `fontFamily` | `Arial` | Base font family of the text. |
181
183
  | `fontSize` | `14` | Base font size of the text in px. |
182
184
  | `fontStyle` | `''` | Base font style, same as css font-style. Examples: `italic`, `oblique 40deg`. |
183
185
  | `fontVariant` | `''` | Base font variant, same as css font-variant. Examples: `small-caps`. |
184
186
  | `fontWeight` | `'400'` | Base font weight, same as css font-weight. Examples: `bold`, `100`. |
185
187
  | `fontColor` | `'black'` | Base font color, same as css color. Examples: `blue`, `#00ff00`. |
188
+ | `strokeColor` | `'black'` | Base stroke color, same as css color. Examples: `blue`, `#00ff00`. |
189
+ | `strokeWidth` | `0` | Base stroke width. Positive number; `<=0` means none. Can be fractional. ⚠️ Word splitting does not take into account the stroke, which is applied on the __center__ of the edges of the text via the [strokeText()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/strokeText) Canvas API. Setting a thick stroke will cause it to bleed out of the text box. |
186
190
  | `justify` | `false` | Justify text if `true`, it will insert spaces between words when necessary. |
187
191
  | `inferWhitespace` | `true` | If whitespace in the text should be inferred. Only applies if the text given to `drawText()` is a `Word[]`. If the text is a `string`, this config setting is ignored. |
188
192
  | `overflow` | `true` | Allows the text to overflow out of the box if the box is too narrow/short to fit it all. `false` will clip the text to the box's boundaries. |
@@ -303,6 +307,10 @@ const drawWords = (baseFormat: TextFormat, spec: RenderSpec) => {
303
307
  ctx.textBaseline = textBaseline;
304
308
  ctx.font = getTextStyle(baseFormat);
305
309
  ctx.fillStyle = baseFormat.fontColor;
310
+ ctx.strokeStyle = baseFormat.strokeColor;
311
+ ctx.lineJoin = 'round';
312
+
313
+ const baseStrokeWidth = baseFormat.strokeWidth
306
314
 
307
315
  lines.forEach((line) => {
308
316
  line.forEach((pw) => {
@@ -313,8 +321,19 @@ const drawWords = (baseFormat: TextFormat, spec: RenderSpec) => {
313
321
  if (pw.format.fontColor) {
314
322
  ctx.fillStyle = pw.format.fontColor;
315
323
  }
324
+ if (pw.format.strokeColor) {
325
+ ctx.strokeStyle = pw.format.strokeColor;
326
+ }
316
327
  }
317
328
  ctx.fillText(pw.word.text, pw.x, pw.y);
329
+ // stroke AFTER fill so it goes on top
330
+ const lineWidth = typeof pw.format?.strokeWidth === 'number'
331
+ ? pw.format.strokeWidth
332
+ : baseStrokeWidth;
333
+ if (lineWidth > 0) {
334
+ ctx.lineWidth = lineWidth;
335
+ ctx.strokeText(pw.word.text, pw.x, pw.y);
336
+ }
318
337
  if (pw.format) {
319
338
  ctx.restore();
320
339
  }
@@ -325,7 +344,7 @@ const drawWords = (baseFormat: TextFormat, spec: RenderSpec) => {
325
344
 
326
345
  const words: Word[] = [
327
346
  { text: 'Lorem' },
328
- { text: 'ipsum', format: { fontWeight: 'bold', color: 'red' } },
347
+ { text: 'ipsum', format: { fontWeight: 'bold', fontColor: 'red' } },
329
348
  { text: 'dolor', format: { fontStyle: 'italic' } },
330
349
  { text: 'sit' },
331
350
  { text: 'amet' },
@@ -363,3 +382,9 @@ worker.onmessage = (event) => {
363
382
  ```
364
383
 
365
384
  </details>
385
+
386
+ # Help
387
+
388
+ ## Blurry text
389
+
390
+ If you're experiencing an issue where, "the text quality rendered on the canvas appears lower than that rendered in the DOM across various device resolutions," this may be caused by __pixel density__ settings. See the discussion on [issue #69](https://github.com/stefcameron/text-to-canvas/issues/69#issuecomment-2136498781) for a possible solution.
@@ -3,6 +3,9 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const DEFAULT_FONT_FAMILY = "Arial";
4
4
  const DEFAULT_FONT_SIZE = 14;
5
5
  const DEFAULT_FONT_COLOR = "black";
6
+ const DEFAULT_STROKE_COLOR = DEFAULT_FONT_COLOR;
7
+ const DEFAULT_STROKE_WIDTH = 0;
8
+ const DEFAULT_STROKE_JOIN = "round";
6
9
  const getTextFormat = (format, baseFormat) => {
7
10
  return Object.assign(
8
11
  {},
@@ -12,7 +15,9 @@ const getTextFormat = (format, baseFormat) => {
12
15
  fontWeight: "400",
13
16
  fontStyle: "",
14
17
  fontVariant: "",
15
- fontColor: DEFAULT_FONT_COLOR
18
+ fontColor: DEFAULT_FONT_COLOR,
19
+ strokeColor: DEFAULT_STROKE_COLOR,
20
+ strokeWidth: DEFAULT_STROKE_WIDTH
16
21
  },
17
22
  baseFormat,
18
23
  format
@@ -474,7 +479,9 @@ const drawText = (ctx, text, config) => {
474
479
  fontStyle: config.fontStyle,
475
480
  fontVariant: config.fontVariant,
476
481
  fontWeight: config.fontWeight,
477
- fontColor: config.fontColor
482
+ fontColor: config.fontColor,
483
+ strokeColor: config.strokeColor,
484
+ strokeWidth: config.strokeWidth
478
485
  });
479
486
  const {
480
487
  width: boxWidth,
@@ -506,6 +513,9 @@ const drawText = (ctx, text, config) => {
506
513
  ctx.textBaseline = textBaseline;
507
514
  ctx.font = getTextStyle(baseFormat);
508
515
  ctx.fillStyle = baseFormat.fontColor || DEFAULT_FONT_COLOR;
516
+ ctx.strokeStyle = baseFormat.strokeColor || DEFAULT_STROKE_COLOR;
517
+ ctx.lineJoin = DEFAULT_STROKE_JOIN;
518
+ const baseStrokeWidth = baseFormat.strokeWidth ?? DEFAULT_STROKE_WIDTH;
509
519
  if (config.overflow === false) {
510
520
  ctx.beginPath();
511
521
  ctx.rect(boxX, boxY, boxWidth, boxHeight);
@@ -520,8 +530,16 @@ const drawText = (ctx, text, config) => {
520
530
  if (pw.format.fontColor) {
521
531
  ctx.fillStyle = pw.format.fontColor;
522
532
  }
533
+ if (pw.format.strokeColor) {
534
+ ctx.strokeStyle = pw.format.strokeColor;
535
+ }
523
536
  }
524
537
  ctx.fillText(pw.word.text, pw.x, pw.y);
538
+ const lineWidth = typeof pw.format?.strokeWidth === "number" ? pw.format.strokeWidth : baseStrokeWidth;
539
+ if (lineWidth > 0) {
540
+ ctx.lineWidth = lineWidth;
541
+ ctx.strokeText(pw.word.text, pw.x, pw.y);
542
+ }
525
543
  if (pw.format) {
526
544
  ctx.restore();
527
545
  }