Skip to content

Instantly share code, notes, and snippets.

@VitoVan
Last active July 2, 2024 10:19
Show Gist options
  • Save VitoVan/92ba4f2b68fec31cda803119686295e5 to your computer and use it in GitHub Desktop.
Save VitoVan/92ba4f2b68fec31cda803119686295e5 to your computer and use it in GitHub Desktop.
Build Pango, Cairo, GLib and the Whole Universe Into Webassembly

Note

The latest version is here:

https://vitovan.com/pango-cairo-wasm/

Build Pango, Cairo, GLib

and the Whole Universe Into Webassembly

Before executing the following commands, you need to follow how-to-build.sh and create two c files, they are provided.

You also need to have a folder named fonts with some fonts in it, I am using Open Sans.

Not to me, but to @kleisauke glory should be given.

Compile without pthread and pango, pure cairo

emcc -s USE_SDL=2 \
     -s ASYNCIFY \
     --embed-file ./fonts@/usr/share/fonts/ \
     $(pkg-config --libs --cflags glib-2.0, gobject-2.0, cairo, pixman-1, freetype2, fontconfig, cairo, expat) \
     hello-cairo.c -o hello-cairo.html

imagen

Compile with pthread and pango

Why EMULATE_FUNCTION_POINTER_CASTS?

https://github.com/kleisauke/glib/commit/934d55a2814024a1357e4db0b883dc040f8fb401

https://emscripten.org/docs/porting/guidelines/function_pointer_issues.html

emcc  -s EMULATE_FUNCTION_POINTER_CASTS \
      -s PTHREAD_POOL_SIZE=10 \
      -s USE_PTHREADS=1 \
      -s USE_SDL=2 \
      -s ASYNCIFY \
      --embed-file ./fonts@/usr/share/fonts/ \
      $(pkg-config --libs --cflags glib-2.0, gobject-2.0, cairo, pixman-1, freetype2, fontconfig, cairo, expat, pangocairo) \
      hello-pango.c -o hello-pango.html

imagen

#include <SDL2/SDL.h>
#include <cairo/cairo.h>
#include <emscripten.h>
#include <stdbool.h>
#include <stdio.h>
void _draw(cairo_t *cr) {
cairo_set_source_rgba(cr, 0.047, 0.21, 0.51, 1);
cairo_paint(cr);
cairo_set_source_rgba(cr, 1, 1, 1, 1);
cairo_move_to(cr, 30, 100);
cairo_set_font_size(cr, 84);
cairo_show_text(cr, "DON'T PANIC");
}
int main(int argc, char *argv[]) {
setenv("PANGOCAIRO_BACKEND", "fontconfig", 1);
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *window = SDL_CreateWindow(
"An SDL2 window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640,
480, SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI);
SDL_Renderer *renderer = SDL_CreateRenderer(
window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
bool done = false;
while (!done) {
int window_width;
int window_height;
SDL_GetWindowSize(window, &window_width, &window_height);
int renderer_width;
int renderer_height;
SDL_GetRendererOutputSize(renderer, &renderer_width, &renderer_height);
int cairo_x_multiplier = renderer_width / window_width;
int cairo_y_multiplier = renderer_height / window_height;
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
renderer_width, renderer_height);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
int *pixels = NULL;
int pitch;
SDL_LockTexture(texture, NULL, (void **)&pixels, &pitch);
cairo_surface_t *cr_surface = cairo_image_surface_create_for_data(
(unsigned char *)pixels, CAIRO_FORMAT_RGB24, renderer_width,
renderer_height, pitch);
cairo_surface_set_device_scale(cr_surface, cairo_x_multiplier,
cairo_y_multiplier);
cairo_t *cr = cairo_create(cr_surface);
_draw(cr);
SDL_UnlockTexture(texture);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
cairo_destroy(cr);
cairo_surface_destroy(cr_surface);
SDL_DestroyTexture(texture);
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
// SDL_Delay(100);
emscripten_sleep(100);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}
#include <SDL2/SDL.h>
#include <cairo/cairo.h>
#include <emscripten.h>
#include <pango/pangocairo.h>
#include <stdbool.h>
#include <stdio.h>
void _draw(cairo_t *cr) {
cairo_set_source_rgba(cr, 0.047, 0.21, 0.51, 1);
cairo_paint(cr);
cairo_set_source_rgba(cr, 1, 1, 1, 1);
cairo_move_to(cr, 30, 100);
cairo_set_font_size(cr, 84);
cairo_show_text(cr, "DON'T PANIC");
PangoFontDescription *font_description = pango_font_description_new();
pango_font_description_set_family(font_description, "Open Sans");
pango_font_description_set_weight(font_description, PANGO_WEIGHT_BOLD);
pango_font_description_set_absolute_size(font_description, 42 * PANGO_SCALE);
PangoLayout *layout = pango_cairo_create_layout(cr);
pango_layout_set_font_description(layout, font_description);
// pango_layout_set_text(layout, "Hello, pango", -1);
pango_layout_set_markup(layout,
"<i>Hello</i>, <span foreground='yellow' "
"size='x-large'>pango</span><span foreground='red' "
"size='large'>markup</span>",
-1);
cairo_set_source_rgb(cr, 1, 1, 1.0);
cairo_move_to(cr, 100, 150.0);
pango_cairo_show_layout(cr, layout);
g_object_unref(layout);
pango_font_description_free(font_description);
}
int main(int argc, char *argv[]) {
setenv("PANGOCAIRO_BACKEND", "fontconfig", 1);
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *window = SDL_CreateWindow(
"An SDL2 window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640,
480, SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI);
SDL_Renderer *renderer = SDL_CreateRenderer(
window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
bool done = false;
while (!done) {
int window_width;
int window_height;
SDL_GetWindowSize(window, &window_width, &window_height);
int renderer_width;
int renderer_height;
SDL_GetRendererOutputSize(renderer, &renderer_width, &renderer_height);
int cairo_x_multiplier = renderer_width / window_width;
int cairo_y_multiplier = renderer_height / window_height;
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
renderer_width, renderer_height);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
int *pixels = NULL;
int pitch;
SDL_LockTexture(texture, NULL, (void **)&pixels, &pitch);
cairo_surface_t *cr_surface = cairo_image_surface_create_for_data(
(unsigned char *)pixels, CAIRO_FORMAT_RGB24, renderer_width,
renderer_height, pitch);
cairo_surface_set_device_scale(cr_surface, cairo_x_multiplier,
cairo_y_multiplier);
cairo_t *cr = cairo_create(cr_surface);
_draw(cr);
SDL_UnlockTexture(texture);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
cairo_destroy(cr);
cairo_surface_destroy(cr_surface);
SDL_DestroyTexture(texture);
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
// SDL_Delay(100);
emscripten_sleep(100);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}
dnf -y groupinstall "Development Tools"
dnf -y install ragel byacc flex
# some other dev-tools might also need to be installed,
# such as meson or whatever,
# install them as required, I didn't list them all here
cd $HOME
######################
##### setup Emscripten
######################
git clone --depth 1 --branch 3.1.35 https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
######################
##### setup ENVs
######################
source $HOME/emsdk/emsdk_env.sh
export MAKEFLAGS="-j$(nproc)"
export magicdir=${HOME}/magic/
export magicprefix=${magicdir}/build
export EM_PKG_CONFIG_PATH=${magicprefix}/lib/pkgconfig/
export PKG_CONFIG_PATH=${magicprefix}/lib/pkgconfig/
export EM_PKG_CONFIG_LIBDIR=${magicprefix}/lib/
export PKG_CONFIG_LIBDIR=${magicprefix}/lib/
export CHOST="wasm32-unknown-linux"
export ax_cv_c_float_words_bigendian=no
######################
##### setup meson
######################
export MESON_CROSS="${magicdir}/emscripten-crossfile.meson"
cat > "${magicdir}/emscripten-crossfile.meson" <<END
[binaries]
c = 'emcc'
cpp = 'em++'
ld = 'wasm-ld'
ar = 'emar'
ranlib = 'emranlib'
pkgconfig = ['emconfigure', 'pkg-config']
# https://docs.gtk.org/glib/cross-compiling.html#cross-properties
[properties]
growing_stack = true
have_c99_vsnprintf = true
have_c99_snprintf = true
have_unix98_printf = true
# Ensure that '-s PTHREAD_POOL_SIZE=*' is not injected into .pc files
[built-in options]
c_thread_count = 0
cpp_thread_count = 0
[host_machine]
system = 'emscripten'
cpu_family = 'wasm32'
cpu = 'wasm32'
endian = 'little'
END
mkdir ${magicdir}
mkdir ${magicprefix}
######################
##### zlib
######################
cd ${magicdir}
git clone --depth 1 --branch v1.2.13 https://github.com/madler/zlib.git
cd ${magicdir}/zlib
emconfigure ./configure --static --prefix=${magicprefix} && \
emmake make && \
emmake make install
######################
##### libpng
######################
cd ${magicdir}
git clone --depth 1 --branch v1.6.39 https://github.com/glennrp/libpng.git
cd ${magicdir}/libpng
emconfigure ./configure --host=${CHOST} --prefix=${magicprefix} --enable-shared=no --disable-dependency-tracking CFLAGS='-s USE_PTHREADS' LDFLAGS='-lpthread' && \
emmake make clean && \
emmake make && \
emmake make install
######################
##### pixman
######################
cd ${magicdir}
git clone --depth 1 --branch pixman-0.42.2 git://anongit.freedesktop.org/git/pixman
cd ${magicdir}/pixman
./autogen.sh && \
emconfigure ./configure --host=${CHOST} --prefix=${magicprefix} --enable-shared=no --disable-dependency-tracking CFLAGS='-s USE_PTHREADS' LDFLAGS='-lpthread' && \
emmake make clean && \
emmake make && \
emmake make install
######################
##### freetype
######################
cd ${magicdir}
git clone --depth 1 --branch VER-2-13-0 https://gitlab.freedesktop.org/freetype/freetype.git
cd ${magicdir}/freetype
./autogen.sh && \
emconfigure ./configure --host=${CHOST} --prefix=${magicprefix} --enable-shared=no --disable-dependency-tracking CFLAGS='-s USE_PTHREADS' LDFLAGS='-lpthread' && \
emmake make clean && \
emmake make && \
emmake make install
######################
##### expat
######################
cd ${magicdir}
git clone --depth 1 --branch R_2_5_0 https://github.com/libexpat/libexpat.git
cd ${magicdir}/libexpat/expat
./buildconf.sh &&
emconfigure ./configure --without-docbook --host=${CHOST} --prefix=${magicprefix} --enable-shared=no --disable-dependency-tracking CFLAGS='-s USE_PTHREADS' LDFLAGS='-lpthread' && \
emmake make && \
emmake make install
######################
##### fontconfig
######################
cd ${magicdir}
git clone --depth 1 --branch 2.14.2 https://gitlab.freedesktop.org/fontconfig/fontconfig.git
cd ${magicdir}/fontconfig
emconfigure ./autogen.sh --prefix=${magicprefix} && \
emconfigure ./configure --host=${CHOST} --cache-file=enabled --disable-docs --disable-docbook --prefix=${magicprefix} --enable-shared=no --disable-dependency-tracking CFLAGS='-s USE_PTHREADS -pthread' LDFLAGS='-lpthread' && \
emmake make clean && \
emmake make && \
cp fc-cache/fc-cache fc-cache/fc-cache-bak && \
chmod +x fc-cache/fc-cache && \
echo 'exit 0' > fc-cache/fc-cache && \
emmake make install
######################
##### libffi
######################
cd ${magicdir}
git clone --depth 1 https://github.com/libffi/libffi.git
cd ${magicdir}/libffi
# the latest source code of libffi has already got emscripten support
# https://github.com/libffi/libffi/pull/763
# but not released yet, so ... the latest commit for me is ac598b7
git checkout ac598b7
./autogen.sh && \
emconfigure ./configure --host=${CHOST} --prefix=${magicprefix} --enable-static --disable-shared --disable-dependency-tracking --disable-builddir --disable-multi-os-directory --disable-raw-api --disable-structs --disable-docs && \
emmake make && \
emmake make install SUBDIRS='include'
######################
##### glib
######################
cd ${magicdir}
git clone --depth 1 --branch wasm-calm-2.76.1 https://github.com/VitoVan/glib.git
cd ${magicdir}/glib
CFLAGS='-s USE_PTHREADS' LDFLAGS='-lpthread' meson setup _build --prefix=${magicprefix} --cross-file=$MESON_CROSS --default-library=static --buildtype=release \
--force-fallback-for=pcre2,gvdb -Dselinux=disabled -Dxattr=false -Dlibmount=disabled -Dnls=disabled \
-Dtests=false -Dglib_assert=false -Dglib_checks=false && \
meson install -C _build
######################
##### cairo
######################
cd ${magicdir}
git clone --depth 1 --branch 1.17.8 https://gitlab.freedesktop.org/cairo/cairo.git
cd ${magicdir}/cairo
CFLAGS="$(pkg-config --cflags freetype2, fontconfig, expat) -s USE_PTHREADS" LDFLAGS="$(pkg-config --libs freetype2, fontconfig, expat) -lpthread" meson setup _build --prefix=${magicprefix} --cross-file=$MESON_CROSS --default-library=static --buildtype=release -Dtests=disabled && \
meson install -C _build
######################
##### harfbuzz
######################
cd ${magicdir}
git clone --depth 1 --branch 7.1.0 https://github.com/harfbuzz/harfbuzz.git
cd ${magicdir}/harfbuzz
./autogen.sh && \
emconfigure ./configure --host=${CHOST} --prefix=${magicprefix} --enable-shared=no --disable-dependency-tracking CFLAGS='-s USE_PTHREADS -pthread' LDFLAGS='-lpthread' && \
emmake make clean && \
emmake make && \
emmake make install
######################
##### reinstall: freetype
##### somebody said I should do this, I don't know why
##### but... fuck it, whatever
######################
cd ${magicdir}/freetype
./autogen.sh && \
emconfigure ./configure --host=${CHOST} --prefix=${magicprefix} --enable-shared=no --disable-dependency-tracking CFLAGS='-s USE_PTHREADS' LDFLAGS='-lpthread' && \
emmake make clean && \
emmake make && \
emmake make install
######################
##### fribidi
######################
cd ${magicdir}
git clone --depth 1 --branch v1.0.12 https://github.com/fribidi/fribidi.git
cd ${magicdir}/fribidi
.ci/build-c2man.sh
export PATH=$PATH:${magicdir}/fribidi/c2man/c2man-install
./autogen.sh && \
emconfigure ./configure --host=${CHOST} --prefix=${magicprefix} --enable-shared=no --disable-dependency-tracking CFLAGS='-s USE_PTHREADS -pthread' LDFLAGS='-lpthread' && \
emmake make clean && \
emmake make && \
emmake make install
######################
##### pango
######################
cd ${magicdir}
git clone --depth 1 --branch wasm-calm-1.50.14 https://github.com/VitoVan/pango.git
cd ${magicdir}/pango
# remove test
# because it uses something emscripten don't have?
# like: g_io_channel_unix_new ?
mv meson.build meson.build.original
grep -vwE "subdir\('tests'\)" meson.build.original > meson.build
CFLAGS="$(pkg-config --cflags glib-2.0, cairo, pixman-1, fribidi, freetype2, fontconfig, expat) -s USE_PTHREADS" LDFLAGS="$(pkg-config --libs glib-2.0, cairo, pixman-1, fribidi, freetype2, fontconfig, expat) -lpthread" meson setup _build --prefix=${magicprefix} --cross-file=$MESON_CROSS --default-library=static --buildtype=release -Dintrospection=disabled && \
CFLAGS="$(pkg-config --cflags glib-2.0, cairo, pixman-1, fribidi, freetype2, fontconfig, expat) -s USE_PTHREADS" LDFLAGS="$(pkg-config --libs glib-2.0, cairo, pixman-1, fribidi, freetype2, fontconfig, expat) -lpthread" meson install -C _build
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment