node-canvas README

The project's README from github.com/Automattic/node-canvas. As one markdown file: README.md.

node-canvas

Test NPM version

node-canvas is a Cairo-backed Canvas implementation for Node.js.

Installation

The latest version of node-canvas is v4. It fixes a plethora of font related issues, adds support for worker threads, reduces binary size, removes the post install script, expands the platforms that binaries support, and more. It is still in the pre-release phase. We appreciate any feedback.

$ npm install canvas@next

To get v3:

$ npm install canvas

The minimum version of Node.js required is 22.

Binaries will be installed for the following platforms:

If you want to install for a more exotic system, CD into the package and run zig build. You may need to tweak build.zig. PRs are welcome.

Quick Example

const { createCanvas, loadImage } = require('canvas')
const canvas = createCanvas(200, 200)
const ctx = canvas.getContext('2d')

// Write "Awesome!"
ctx.font = '30px Impact'
ctx.rotate(0.1)
ctx.fillText('Awesome!', 50, 100)

// Draw line under text
var text = ctx.measureText('Awesome!')
ctx.strokeStyle = 'rgba(0,0,0,0.5)'
ctx.beginPath()
ctx.lineTo(50, 102)
ctx.lineTo(50 + text.width, 102)
ctx.stroke()

// Draw cat with lime helmet
loadImage('examples/images/lime-cat.jpg').then((image) => {
  ctx.drawImage(image, 50, 0, 70, 70)

  console.log('<img src="' + canvas.toDataURL() + '" />')
})

Documentation

This project is an implementation of the Web Canvas API and implements that API as closely as possible. For API documentation, please visit Mozilla Web Canvas API. (See Compatibility Status for the current API compliance.) All utility methods and non-standard APIs are documented below.

Utility methods

Font API

Non-standard APIs

createCanvas()

createCanvas(width: number, height: number, type?: 'PDF'|'SVG') => Canvas

Creates a Canvas instance. This method works in both Node.js and Web browsers, where there is no Canvas constructor. (See browser.js for the implementation that runs in browsers.)

const { createCanvas } = require('canvas')
const mycanvas = createCanvas(200, 200)
const myPDFcanvas = createCanvas(600, 800, 'pdf') // see "PDF Support" section

createImageData()

createImageData(width: number, height: number) => ImageData
createImageData(data: Uint8ClampedArray, width: number, height?: number) => ImageData
// for alternative pixel formats:
createImageData(data: Uint16Array, width: number, height?: number) => ImageData

Creates an ImageData instance. This method works in both Node.js and Web browsers.

const { createImageData } = require('canvas')
const width = 20, height = 20
const arraySize = width * height * 4
const mydata = createImageData(new Uint8ClampedArray(arraySize), width)

loadImage()

loadImage() => Promise<Image>

Convenience method for loading images. This method works in both Node.js and Web browsers.

const { loadImage } = require('canvas')
const myimg = loadImage('http://server.com/image.png')

myimg.then(() => {
  // do something with image
}).catch(err => {
  console.log('oh no!', err)
})

// or with async/await:
const myimg = await loadImage('http://server.com/image.png')
// do something with image

FontFace


interface FontFaceDescriptors {
  weight?: string | number;
  style?: string;
}

class FontFace {
  constructor(
    family: string,
    source: string | ArrayBuffer | Uint8Array,
    descriptors?: FontFaceDescriptors
  )

  family: string;
  style: string;
  weight: string;
  status: 'loaded' | 'unloaded' | 'error';
}

To use your own font file, create a FontFace with the file path and add it to fonts. fonts is just like the browser's document.fonts, but with a slightly more limited API.

const { createCanvas, FontFace, fonts } = require('canvas')
fonts.add(new FontFace('Comic Sans', '/home/you/comicsans.ttf'))

const canvas = createCanvas(500, 500)
const ctx = canvas.getContext('2d')

ctx.font = '12px "Comic Sans"'
ctx.fillText('Everyone hates this font :(', 250, 10)

The second argument can either be a path to the font file or a buffer in TrueType format. The third argument is an object with properties that resemble the CSS properties that are specified in @font-face rules. You must specify at least family. weight and style are optional and default to 'normal'.

fonts


class FontFaceSet {
  add(face: FontFace): void;
  has(face: FontFace): boolean;
  clear(): void;
  delete(face: FontFace): boolean;
  [Symbol.iterator](): Iterator<FontFace>;
  size: number;
}

fonts: FontFaceSet

fonts implements a subset of the browser's document.fonts. Use fonts.delete() to unregister an individual font, or fonts.clear() to remove all registered fonts. This is useful when you want to remove all registered fonts, such as when using the canvas in tests.

const { registerFont, createCanvas, fonts, FontFace } = require('canvas')

describe('text rendering', () => {
  afterEach(() => {
    fonts.clear()
  })
  it('should render text with Comic Sans', () => {
    fonts.add(new FontFace('Comic Sans', '/home/you/comicsans.ttf'))

    const canvas = createCanvas(500, 500)
    const ctx = canvas.getContext('2d')

    ctx.font = '12px "Comic Sans"'
    ctx.fillText('Everyone loves this font :)', 250, 10)

    // assertScreenshot()
  })
})

Image#src

img.src: string|Buffer

As in browsers, img.src can be set to a data: URI or a remote URL. In addition, node-canvas allows setting src to a local file path or Buffer instance.

const { Image } = require('canvas')

// From a buffer:
fs.readFile('images/squid.png', (err, squid) => {
  if (err) throw err
  const img = new Image()
  img.onload = () => ctx.drawImage(img, 0, 0)
  img.onerror = err => { throw err }
  img.src = squid
})

// From a local file path:
const img = new Image()
img.onload = () => ctx.drawImage(img, 0, 0)
img.onerror = err => { throw err }
img.src = 'images/squid.png'

// From a remote URL:
img.src = 'http://picsum.photos/200/300'
// ... as above

// From a `data:` URI:
img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='
// ... as above

Note: In some cases, img.src= is currently synchronous. However, you should always use img.onload and img.onerror, as we intend to make img.src= always asynchronous as it is in browsers. See https://github.com/Automattic/node-canvas/issues/1007.

Image#dataMode

img.dataMode: number

Applies to JPEG images drawn to PDF canvases only.

Setting img.dataMode = Image.MODE_MIME or Image.MODE_MIME|Image.MODE_IMAGE enables MIME data tracking of images. When MIME data is tracked, PDF canvases can embed JPEGs directly into the output, rather than re-encoding into PNG. This can drastically reduce filesize and speed up rendering.

const { Image, createCanvas } = require('canvas')
const canvas = createCanvas(w, h, 'pdf')
const img = new Image()
img.dataMode = Image.MODE_IMAGE // Only image data tracked
img.dataMode = Image.MODE_MIME // Only mime data tracked
img.dataMode = Image.MODE_MIME | Image.MODE_IMAGE // Both are tracked

If working with a non-PDF canvas, image data must be tracked; otherwise the output will be junk.

Enabling mime data tracking has no benefits (only a slow down) unless you are generating a PDF.

Canvas#toBuffer()

canvas.toBuffer((err: Error|null, result: Buffer) => void, mimeType?: string, config?: any) => void
canvas.toBuffer(mimeType?: string, config?: any) => Buffer

Creates a Buffer object representing the image contained in the canvas.

Return value

If no callback is provided, a Buffer. If a callback is provided, none.

Examples

// Default: buf contains a PNG-encoded image
const buf = canvas.toBuffer()

// PNG-encoded, zlib compression level 3 for faster compression but bigger files, no filtering
const buf2 = canvas.toBuffer('image/png', { compressionLevel: 3, filters: canvas.PNG_FILTER_NONE })

// JPEG-encoded, 50% quality
const buf3 = canvas.toBuffer('image/jpeg', { quality: 0.5 })

// Asynchronous PNG
canvas.toBuffer((err, buf) => {
  if (err) throw err // encoding failed
  // buf is PNG-encoded image
})

canvas.toBuffer((err, buf) => {
  if (err) throw err // encoding failed
  // buf is JPEG-encoded image at 95% quality
}, 'image/jpeg', { quality: 0.95 })

// BGRA pixel values, native-endian
const buf4 = canvas.toBuffer('raw')
const { stride, width } = canvas
// In memory, this is `canvas.height * canvas.stride` bytes long.
// The top row of pixels, in BGRA order on little-endian hardware,
// left-to-right, is:
const topPixelsBGRALeftToRight = buf4.slice(0, width * 4)
// And the third row is:
const row3 = buf4.slice(2 * stride, 2 * stride + width * 4)

// SVG and PDF canvases
const myCanvas = createCanvas(w, h, 'pdf')
myCanvas.toBuffer() // returns a buffer containing a PDF-encoded canvas
// With optional metadata:
myCanvas.toBuffer('application/pdf', {
  title: 'my picture',
  keywords: 'node.js demo cairo',
  creationDate: new Date()
})

Canvas#createPNGStream()

canvas.createPNGStream(config?: any) => ReadableStream

Creates a ReadableStream that emits PNG-encoded data.

Examples

const fs = require('fs')
const out = fs.createWriteStream(__dirname + '/test.png')
const stream = canvas.createPNGStream()
stream.pipe(out)
out.on('finish', () =>  console.log('The PNG file was created.'))

To encode indexed PNGs from canvases with pixelFormat: 'A8' or 'A1', provide an options object:

const palette = new Uint8ClampedArray([
  //r    g    b    a
    0,  50,  50, 255, // index 1
   10,  90,  90, 255, // index 2
  127, 127, 255, 255
  // ...
])
canvas.createPNGStream({
  palette: palette,
  backgroundIndex: 0 // optional, defaults to 0
})

Canvas#createJPEGStream()

canvas.createJPEGStream(config?: any) => ReadableStream

Creates a ReadableStream that emits JPEG-encoded data.

Note: At the moment, createJPEGStream() is synchronous under the hood. That is, it runs in the main thread, not in the libuv threadpool.

Examples

const fs = require('fs')
const out = fs.createWriteStream(__dirname + '/test.jpeg')
const stream = canvas.createJPEGStream()
stream.pipe(out)
out.on('finish', () =>  console.log('The JPEG file was created.'))

// Disable 2x2 chromaSubsampling for deeper colors and use a higher quality
const stream = canvas.createJPEGStream({
  quality: 0.95,
  chromaSubsampling: false
})

Canvas#createPDFStream()

canvas.createPDFStream(config?: any) => ReadableStream

Applies to PDF canvases only. Creates a ReadableStream that emits the encoded PDF. canvas.toBuffer() also produces an encoded PDF, but createPDFStream() can be used to reduce memory usage.

Canvas#toDataURL()

This is a standard API, but several non-standard calls are supported. The full list of supported calls is:

dataUrl = canvas.toDataURL() // defaults to PNG
dataUrl = canvas.toDataURL('image/png')
dataUrl = canvas.toDataURL('image/jpeg')
dataUrl = canvas.toDataURL('image/jpeg', quality) // quality from 0 to 1
canvas.toDataURL((err, png) => { }) // defaults to PNG
canvas.toDataURL('image/png', (err, png) => { })
canvas.toDataURL('image/jpeg', (err, jpeg) => { }) // sync JPEG is not supported
canvas.toDataURL('image/jpeg', {...opts}, (err, jpeg) => { }) // see Canvas#createJPEGStream for valid options
canvas.toDataURL('image/jpeg', quality, (err, jpeg) => { }) // spec-following; quality from 0 to 1

CanvasRenderingContext2D#patternQuality

context.patternQuality: 'fast'|'good'|'best'|'nearest'|'bilinear'

Defaults to 'good'. Affects pattern (gradient, image, etc.) rendering quality.

CanvasRenderingContext2D#quality

context.quality: 'fast'|'good'|'best'|'nearest'|'bilinear'

Defaults to 'good'. Like patternQuality, but applies to transformations affecting more than just patterns.

CanvasRenderingContext2D#textDrawingMode

context.textDrawingMode: 'path'|'glyph'

Defaults to 'path'. The effect depends on the canvas type:

In glyph mode, ctx.strokeText() and ctx.fillText() behave the same (aside from using the stroke and fill style, respectively).

This property is tracked as part of the canvas state in save/restore.

CanvasRenderingContext2D#globalCompositeOperation = 'saturate'

In addition to all of the standard global composite operations defined by the Canvas specification, the 'saturate' operation is also available.

CanvasRenderingContext2D#antialias

context.antialias: 'default'|'none'|'gray'|'subpixel'

Sets the anti-aliasing mode.

PDF Output Support

node-canvas can create PDF documents instead of images. The canvas type must be set when creating the canvas as follows:

const canvas = createCanvas(200, 500, 'pdf')

An additional method .addPage() is then available to create multiple page PDFs:

// On first page
ctx.font = '22px Helvetica'
ctx.fillText('Hello World', 50, 80)

ctx.addPage()
// Now on second page
ctx.font = '22px Helvetica'
ctx.fillText('Hello World 2', 50, 80)

canvas.toBuffer() // returns a PDF file
canvas.createPDFStream() // returns a ReadableStream that emits a PDF
// With optional document metadata:
canvas.toBuffer('application/pdf', {
  title: 'my picture',
  keywords: 'node.js demo cairo',
  creationDate: new Date()
})

It is also possible to create pages with different sizes by passing width and height to the .addPage() method:

ctx.font = '22px Helvetica'
ctx.fillText('Hello World', 50, 80)
ctx.addPage(400, 800)

ctx.fillText('Hello World 2', 50, 80)

It is possible to add hyperlinks using .beginTag() and .endTag():

ctx.beginTag('Link', "uri='https://google.com'")
ctx.font = '22px Helvetica'
ctx.fillText('Hello World', 50, 80)
ctx.endTag('Link')

Or with a defined rectangle:

ctx.beginTag('Link', "uri='https://google.com' rect=[50 80 100 20]")
ctx.endTag('Link')

Note that the syntax for attributes is unique to Cairo. See cairo_tag_begin for the full documentation.

You can create areas on the canvas using the "cairo.dest" tag, and then link to them using the "Link" tag with the dest= attribute. You can also define PDF structure for accessibility by using tag names like "P", "H1", and "TABLE". The standard tags are defined in §14.8.4 of the PDF 1.7 specification.

See also:

SVG Output Support

node-canvas can create SVG documents instead of images. The canvas type must be set when creating the canvas as follows:

const canvas = createCanvas(200, 500, 'svg')
// Use the normal primitives.
fs.writeFileSync('out.svg', canvas.toBuffer())

SVG Image Support

If librsvg is available when node-canvas is installed, node-canvas can render SVG images to your canvas context. This currently works by rasterizing the SVG image (i.e. drawing an SVG image to an SVG canvas will not preserve the SVG data).

const img = new Image()
img.onload = () => ctx.drawImage(img, 0, 0)
img.onerror = err => { throw err }
img.src = './example.svg'

Image pixel formats (experimental)

node-canvas has experimental support for additional pixel formats, roughly following the Canvas color space proposal.

const canvas = createCanvas(200, 200)
const ctx = canvas.getContext('2d', { pixelFormat: 'A8' })

By default, canvases are created in the RGBA32 format, which corresponds to the native HTML Canvas behavior. Each pixel is 32 bits. The JavaScript APIs that involve pixel data (getImageData, putImageData) store the colors in the order {red, green, blue, alpha} without alpha pre-multiplication. (The C++ API stores the colors in the order {alpha, red, green, blue} in native-endian ordering, with alpha pre-multiplication.)

These additional pixel formats have experimental support:

Notes and caveats:

Local Development and Testing

To set up node-canvas for local develoment, use the --force argument (until npm/cli#6138 is fixed):

npm install --force

For visual tests: npm run test-server and point your browser to http://localhost:4000.

For unit tests: npm run test.

Benchmarks

Benchmarks live in the benchmarks directory.

Examples

Examples line in the examples directory. Most produce a png image of the same name, and others such as live-clock.js launch an HTTP server to be viewed in the browser.

Original Authors

License

node-canvas

(The MIT License)

Copyright (c) 2010 LearnBoost, and contributors <[email protected]>

Copyright (c) 2014 Automattic, Inc and contributors <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

BMP parser

See license

node-canvas (Automattic/node-canvas)

Searching Database…

这是什么?node-canvas 是 Node.js 的一个库,它基于 Cairo 实现了 HTML5 Canvas API,让你可以在服务器端绘制图形、图片和文字,并将结果导出为 PNG、JPEG、PDF 或 SVG。
这是我要导入的库,还是一个独立应用?它是一个 Node.js 库(通过 npm 安装),尽可能贴近浏览器的 Web Canvas API,并提供 createCanvas()、createImageData() 和 loadImage() 等辅助函数。
怎样把它加到 Node 项目里?将它作为 npm 依赖添加即可。该包为支持的平台附带了预编译的原生二进制文件,因此通常无需在本地构建 Cairo。当前的主要发布版本是 v4(预发布版),如果需要稳定标签,可以使用 v3。
当前版本可以用于生产环境吗?v4 仍处于预发布阶段(新增了工作线程支持和更小的二进制文件,但仍在稳定化中);如果你需要生产环境的可靠性,v3 仍是经过验证的稳定选择。无论选哪个版本,都需要 Node.js 22+。
哪些操作系统和 CPU 组合有现成的二进制文件?预编译二进制文件覆盖 macOS x64/arm64、Linux x64/x86/arm64(glibc 或 musl)、Linux arm/riscv64(glibc)以及 Windows x64/x86/arm64。
我能把画布导出成 PNG 以外的格式吗?可以。画布可以通过 toBuffer() 生成 JPEG 和原始缓冲区,通过 createJPEGStream()/createPNGStream() 输出流式 JPEG/PNG;在创建时把画布类型设为 pdf 或 svg,还能生成完整的 PDF 或 SVG 文档。
我能使用自定义字体吗?可以。通过 FontFace API,以及仿照浏览器 document.fonts 设计的字体注册表:你可以用一个族名注册 TrueType 文件(路径或缓冲区),然后像使用系统字体一样绘制文字。
能把 SVG 图片渲染到画布上吗?只有在安装 node-canvas 时系统中已有 librsvg 的情况下才可以。如果有,SVG 图片会被栅格化后绘制到画布上,而不是保留为矢量数据。
为什么要使用 A8 或 RGB16565 这类实验性像素格式?它们用默认的 RGBA32 换取内存或速度:RGB24 不包含透明度通道,渲染不透明内容更快;A8 可输出灰度或索引 PNG 图像;RGB16565 则与某些硬件帧缓冲区相匹配。
Creator
Automattic
Size
5.3 MB
Stars
10.7K
Age
16y
Commits · 90 Days