Skip to content

Commit 15df6b9

Browse files
committed
zig build!
1 parent 52cdf3f commit 15df6b9

20 files changed

+115
-246
lines changed

.github/workflows/ci.yaml

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,9 @@ jobs:
1919
with:
2020
node-version: ${{ matrix.node }}
2121
- uses: actions/checkout@v4
22-
- name: Install Dependencies
23-
run: |
24-
sudo apt update
25-
sudo apt install -y libcairo2-dev libjpeg-dev libpango1.0-dev libgif-dev librsvg2-dev
22+
- uses: mlugg/setup-zig@v2
2623
- name: Install
27-
run: npm install --build-from-source
24+
run: npm install
2825
- name: Test
2926
run: npm test
3027

@@ -39,17 +36,9 @@ jobs:
3936
with:
4037
node-version: ${{ matrix.node }}
4138
- uses: actions/checkout@v4
42-
- name: Install Dependencies
43-
run: |
44-
Invoke-WebRequest "https://ftp.gnome.org/pub/GNOME/binaries/win64/gtk+/2.22/gtk+-bundle_2.22.1-20101229_win64.zip" -OutFile "gtk.zip"
45-
Expand-Archive gtk.zip -DestinationPath "C:\GTK"
46-
Invoke-WebRequest "https://downloads.sourceforge.net/project/libjpeg-turbo/2.0.4/libjpeg-turbo-2.0.4-vc64.exe" -OutFile "libjpeg.exe" -UserAgent NativeHost
47-
.\libjpeg.exe /S
48-
winget install --accept-source-agreements --id=Microsoft.VCRedist.2010.x64 -e
49-
npm install -g node-gyp@8
50-
npm prefix -g | % {npm config set node_gyp "$_\node_modules\node-gyp\bin\node-gyp.js"}
39+
- uses: mlugg/setup-zig@v2
5140
- name: Install
52-
run: npm install --build-from-source
41+
run: npm install
5342
- name: Test
5443
run: npm test
5544

@@ -64,12 +53,9 @@ jobs:
6453
with:
6554
node-version: ${{ matrix.node }}
6655
- uses: actions/checkout@v4
67-
- name: Install Dependencies
68-
run: |
69-
brew update
70-
brew install python-setuptools pkg-config cairo pango libpng jpeg giflib librsvg
56+
- uses: mlugg/setup-zig@v2
7157
- name: Install
72-
run: npm install --build-from-source
58+
run: npm install
7359
- name: Test
7460
run: npm test
7561

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ package-lock.json
1919
npm-debug.log
2020

2121
.idea
22+
23+
.zig-cache/

build.zig

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
const std = @import("std");
2+
3+
pub fn build(b: *std.Build) void {
4+
const target = b.standardTargetOptions(.{});
5+
const optimize = b.standardOptimizeOption(.{});
6+
const cairo = b.dependency("cairo", .{
7+
.use_spectre = false,
8+
.use_xlib = false,
9+
.use_xcb = false,
10+
.use_png = true,
11+
.use_glib = false
12+
}).artifact("cairo");
13+
14+
const libjpeg_turbo = b.dependency("libjpeg_turbo", .{}).artifact("libjpeg_turbo");
15+
16+
const giflib = b.dependency("giflib", .{}).artifact("giflib");
17+
18+
const canvas = b.addSharedLibrary(.{
19+
.name = "canvas",
20+
.root_module = b.createModule(.{
21+
.target = target,
22+
.optimize = optimize,
23+
})
24+
});
25+
26+
const node_api_headers = b.dependency("node_api_headers", .{});
27+
const node_addon_api = b.dependency("node_addon_api", .{});
28+
29+
canvas.addIncludePath(node_api_headers.path("include"));
30+
canvas.addIncludePath(node_addon_api.path("."));
31+
canvas.addCSourceFiles(.{
32+
.files = &.{
33+
"src/bmp/BMPParser.cc",
34+
"src/Canvas.cc",
35+
"src/CanvasGradient.cc",
36+
"src/CanvasPattern.cc",
37+
"src/CanvasRenderingContext2d.cc",
38+
"src/closure.cc",
39+
"src/color.cc",
40+
"src/Image.cc",
41+
"src/ImageData.cc",
42+
"src/init.cc",
43+
"src/FontParser.cc"
44+
} ,
45+
.flags = &.{
46+
"-DNAPI_DISABLE_CPP_EXCEPTIONS",
47+
"-DNODE_ADDON_API_ENABLE_MAYBE"
48+
}
49+
});
50+
51+
canvas.linker_allow_shlib_undefined = true;
52+
53+
canvas.linkLibC();
54+
canvas.linkLibCpp();
55+
canvas.linkLibrary(cairo);
56+
canvas.linkLibrary(libjpeg_turbo);
57+
canvas.linkLibrary(giflib);
58+
59+
const move = b.addInstallFile(canvas.getEmittedBin(), "../bin/canvas.node");
60+
move.step.dependOn(&canvas.step);
61+
b.getInstallStep().dependOn(&move.step);
62+
}

build.zig.zon

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
.{
2+
.name = .canvas,
3+
.fingerprint = 0xa59f6c18a5521654,
4+
.version = "0.0.0",
5+
.paths = .{""},
6+
.dependencies = .{
7+
.node_addon_api = .{
8+
.url = "https://github.com/nodejs/node-addon-api/archive/refs/tags/v8.4.0.tar.gz",
9+
.hash = "N-V-__8AAB0dFQBL0NSpsyPolKPJGjyCmfSTQ7Km5wrRQpb7"
10+
},
11+
.node_api_headers = .{
12+
.url = "https://github.com/nodejs/node-api-headers/archive/refs/tags/v1.5.0.tar.gz",
13+
.hash = "N-V-__8AAASNAQA0UI0mvQk4LyFEhdFuFBPgH8HSokdFFpnz"
14+
},
15+
.cairo = .{
16+
.url = "https://github.com/bandithedoge/cairo.zig/archive/bfc51c8232486ed21e83e020711aa039c7b6bc04.tar.gz",
17+
.hash = "cairo_zig-1.18.4-GbxI8QpbAQD1mm-pi1QJuwYmxvhdVf0K1XhMEFlgsVhb"
18+
},
19+
.libjpeg_turbo = .{
20+
.path = "../libjpeg-turbo",
21+
},
22+
.giflib = .{
23+
.path = "../giflib-5.2.2"
24+
}
25+
},
26+
}

lib/bindings.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22

3-
const bindings = require('../build/Release/canvas.node')
3+
const bindings = require('../bin/canvas.node')
44

55
module.exports = bindings
66

lib/jpegstream.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@ class JPEGStream extends Readable {
1313
constructor (canvas, options) {
1414
super()
1515

16-
if (canvas.streamJPEGSync === undefined) {
17-
throw new Error('node-canvas was built without JPEG support.')
18-
}
19-
2016
this.options = options
2117
this.canvas = canvas
2218
}

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@
2424
"homepage": "https://github.com/Automattic/node-canvas",
2525
"repository": "git://github.com/Automattic/node-canvas.git",
2626
"scripts": {
27-
"prebenchmark": "node-gyp build",
27+
"prebenchmark": "zig build",
2828
"benchmark": "node benchmarks/run.js",
2929
"lint": "standard examples/*.js test/server.js test/public/*.js benchmarks/run.js lib/context2d.js util/has_lib.js browser.js index.js",
3030
"test": "mocha test/*.test.js",
31-
"pretest-server": "node-gyp build",
31+
"pretest-server": "zig build",
3232
"test-server": "node test/server.js",
3333
"generate-wpt": "node ./test/wpt/generate.js",
3434
"test-wpt": "mocha test/wpt/generated/*.js",
35-
"install": "prebuild-install -r napi || node-gyp rebuild",
35+
"install": "zig build",
3636
"tsd": "tsd"
3737
},
3838
"files": [

src/Canvas.cc

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,15 @@
1111
#include <cstring>
1212
#include <cctype>
1313
#include <ctime>
14-
#include <glib.h>
1514
#include "PNG.h"
1615
#include <sstream>
1716
#include <stdlib.h>
1817
#include <string>
1918
#include <unordered_set>
2019
#include "Util.h"
2120
#include <vector>
22-
#include "node_buffer.h"
2321
#include "FontParser.h"
24-
25-
#ifdef HAVE_JPEG
2622
#include "JPEGStream.h"
27-
#endif
2823

2924
#define GENERIC_FACE_ERROR \
3025
"The second argument to registerFont is required, and should be an object " \
@@ -49,9 +44,7 @@ Canvas::Initialize(Napi::Env& env, Napi::Object& exports) {
4944
InstanceMethod<&Canvas::ToBuffer>("toBuffer", napi_default_method),
5045
InstanceMethod<&Canvas::StreamPNGSync>("streamPNGSync", napi_default_method),
5146
InstanceMethod<&Canvas::StreamPDFSync>("streamPDFSync", napi_default_method),
52-
#ifdef HAVE_JPEG
5347
InstanceMethod<&Canvas::StreamJPEGSync>("streamJPEGSync", napi_default_method),
54-
#endif
5548
InstanceAccessor<&Canvas::GetType>("type", napi_default_jsproperty),
5649
InstanceAccessor<&Canvas::GetStride>("stride", napi_default_jsproperty),
5750
InstanceAccessor<&Canvas::GetWidth, &Canvas::SetWidth>("width", napi_default_jsproperty),
@@ -84,8 +77,8 @@ Canvas::Canvas(const Napi::CallbackInfo& info) : Napi::ObjectWrap<Canvas>(info),
8477
this->width = 0;
8578
this->height = 0;
8679

87-
int32_t width;
88-
int32_t height;
80+
uint32_t width;
81+
uint32_t height;
8982

9083
if (info[0].IsNumber()) {
9184
width = info[0].As<Napi::Number>().Int32Value();
@@ -224,13 +217,11 @@ Canvas::ToPngBufferAsync(Closure* base) {
224217
closure);
225218
}
226219

227-
#ifdef HAVE_JPEG
228220
void
229221
Canvas::ToJpegBufferAsync(Closure* base) {
230222
JpegClosure* closure = static_cast<JpegClosure*>(base);
231223
write_to_jpeg_buffer(closure->canvas->ensureSurface(), closure);
232224
}
233-
#endif
234225

235226
static void
236227
parsePNGArgs(Napi::Value arg, PngClosure& pngargs) {
@@ -275,7 +266,6 @@ parsePNGArgs(Napi::Value arg, PngClosure& pngargs) {
275266
}
276267
}
277268

278-
#ifdef HAVE_JPEG
279269
static void parseJPEGArgs(Napi::Value arg, JpegClosure& jpegargs) {
280270
// "If Type(quality) is not Number, or if quality is outside that range, the
281271
// user agent must use its default quality value, as if the quality argument
@@ -307,9 +297,6 @@ static void parseJPEGArgs(Napi::Value arg, JpegClosure& jpegargs) {
307297
}
308298
}
309299
}
310-
#endif
311-
312-
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 16, 0)
313300

314301
static inline void setPdfMetaStr(cairo_surface_t* surf, Napi::Object opts,
315302
cairo_pdf_metadata_t t, const char* propName) {
@@ -343,8 +330,6 @@ static void setPdfMetadata(Canvas* canvas, Napi::Object opts) {
343330
setPdfMetaDate(surf, opts, CAIRO_PDF_METADATA_MOD_DATE, "modDate");
344331
}
345332

346-
#endif // CAIRO 16+
347-
348333
/*
349334
* Converts/encodes data to a Buffer. Async when a callback function is passed.
350335
@@ -380,11 +365,9 @@ Canvas::ToBuffer(const Napi::CallbackInfo& info) {
380365
// mime type may be present, but it's not checked
381366
PdfSvgClosure* closure = static_cast<PdfSvgClosure*>(_closure);
382367
if (isPDF()) {
383-
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 16, 0)
384368
if (info[1].IsObject()) { // toBuffer("application/pdf", config)
385369
setPdfMetadata(this, info[1].As<Napi::Object>());
386370
}
387-
#endif // CAIRO 16+
388371
}
389372

390373
cairo_surface_t *surf = ensureSurface();
@@ -403,10 +386,6 @@ Canvas::ToBuffer(const Napi::CallbackInfo& info) {
403386
if (info[0].StrictEquals(Napi::String::New(env, "raw"))) {
404387
cairo_surface_t *surface = ensureSurface();
405388
cairo_surface_flush(surface);
406-
if (nBytes() > node::Buffer::kMaxLength) {
407-
Napi::Error::New(env, "Data exceeds maximum buffer length.").ThrowAsJavaScriptException();
408-
return env.Undefined();
409-
}
410389
return Napi::Buffer<uint8_t>::Copy(env, cairo_image_surface_get_data(surface), nBytes());
411390
}
412391

@@ -467,7 +446,6 @@ Canvas::ToBuffer(const Napi::CallbackInfo& info) {
467446
return env.Undefined();
468447
}
469448

470-
#ifdef HAVE_JPEG
471449
// Sync JPEG
472450
Napi::Value jpegStr = Napi::String::New(env, "image/jpeg");
473451
if (info[0].StrictEquals(jpegStr)) {
@@ -503,7 +481,6 @@ Canvas::ToBuffer(const Napi::CallbackInfo& info) {
503481
worker->Queue();
504482
return env.Undefined();
505483
}
506-
#endif
507484

508485
return env.Undefined();
509486
}
@@ -608,11 +585,9 @@ Canvas::StreamPDFSync(const Napi::CallbackInfo& info) {
608585
return;
609586
}
610587

611-
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 16, 0)
612588
if (info[1].IsObject()) {
613589
setPdfMetadata(this, info[1].As<Napi::Object>());
614590
}
615-
#endif
616591

617592
cairo_surface_finish(ensureSurface());
618593

@@ -638,7 +613,6 @@ Canvas::StreamPDFSync(const Napi::CallbackInfo& info) {
638613
* Stream JPEG data synchronously.
639614
*/
640615

641-
#ifdef HAVE_JPEG
642616
static uint32_t getSafeBufSize(Canvas* canvas) {
643617
// Don't allow the buffer size to exceed the size of the canvas (#674)
644618
// TODO not sure if this is really correct, but it fixed #674
@@ -659,7 +633,6 @@ Canvas::StreamJPEGSync(const Napi::CallbackInfo& info) {
659633
uint32_t bufsize = getSafeBufSize(this);
660634
write_to_jpeg_stream(ensureSurface(), bufsize, &closure);
661635
}
662-
#endif
663636

664637
char *
665638
str_value(Napi::Maybe<Napi::Value> maybe, const char *fallback, bool can_be_number) {
@@ -714,16 +687,14 @@ Canvas::ParseFont(const Napi::CallbackInfo& info) {
714687
// This returns an approximate value only, suitable for
715688
// Napi::MemoryManagement:: AdjustExternalMemory.
716689
// The formats that don't map to intrinsic types (RGB30, A1) round up.
717-
int32_t
690+
uint32_t
718691
Canvas::approxBytesPerPixel() {
719692
switch (format) {
720693
case CAIRO_FORMAT_ARGB32:
721694
case CAIRO_FORMAT_RGB24:
722695
return 4;
723-
#ifdef CAIRO_FORMAT_RGB30
724696
case CAIRO_FORMAT_RGB30:
725697
return 3;
726-
#endif
727698
case CAIRO_FORMAT_RGB16_565:
728699
return 2;
729700
case CAIRO_FORMAT_A8:

src/Canvas.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ struct Closure;
66

77
#include "closure.h"
88
#include <cairo.h>
9-
#include "dll_visibility.h"
109
#include <napi.h>
1110
#include <vector>
1211
#include <cstddef>
1312

13+
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
14+
#define DLL_LOCAL __attribute__ ((visibility ("hidden")))
15+
1416
/*
1517
* Canvas types.
1618
*/
@@ -62,7 +64,7 @@ class Canvas : public Napi::ObjectWrap<Canvas> {
6264
DLL_PUBLIC inline int getWidth() { return width; }
6365
DLL_PUBLIC inline int getHeight() { return height; }
6466

65-
int32_t approxBytesPerPixel();
67+
uint32_t approxBytesPerPixel();
6668
void setFormat(cairo_format_t format);
6769
cairo_format_t getFormat();
6870
void resurface(Napi::Object This);

0 commit comments

Comments
 (0)