At the conclusion of the last article, we mentioned that we would explore Pango, along with its associated libraries: GNU FriBidi and CairoGraphics. In this article, we describe how to build and install them on both Ubuntu and Windows, and verify the installation using the native CLI tool included with Pango.

🦀 Index of the Complete Series.

155-feature-image.png
Rust: PDFs — Build and Install Pango and Associated Libraries

We will first describe how to build and install Pango on Ubuntu 🐧, and then on Windows 🪟. This process assumes that HarfBuzz has already been built and installed, as described in the first article.

Originally, I planned to discuss the Rust code as well. I have already completed the study code, but this article became too long. For now, I want to capture as much detail as possible so that if I need to repeat the process later, the information will be readily available.

🙏 Please note that Pango depends on HarfBuzz. On Windows 🪟, the default Pango build process will include its own HarfBuzz build. We must explicitly instruct it to use our existing HarfBuzz build and installation instead.

Ubuntu: Build and Install Pango and Associated Libraries

💥 Please note: At the time of writing this section, I recalled that my earlier research suggested Pango and its associated libraries may already be included with the Ubuntu distribution. However, I did not verify this. I proceeded with building and installing them manually, and afterward encountered a Pango version conflict as discussed. 🙏 If you are following this article, I recommend reading this section in full before deciding whether you need to perform the build and installation yourself.

Upgrade the Meson Build Tool

In the first article, we installed Meson. However, building Pango requires a newer version. The following commands install the latest Meson in the user path, without modifying the system Python:

$ sudo apt remove meson
$ sudo apt install pipx 
$ pipx ensurepath
$ pipx install meson
$ pipx ensurepath

Running meson --version reports 1.10.0. I am not certain how this change would affect the HarfBuzz build and installation described in the first article, should I need to repeat that process.

● The first pipx ensurepath command reports:

Success! Added /home/behai/.local/bin to the PATH environment variable.

Consider adding shell completions for pipx. Run 'pipx completions' for instructions.

You will need to open a new terminal or re-login for the PATH changes to take effect.

Otherwise pipx is ready to go! ✨ 🌟 ✨

● After successful completion, the pipx install meson command suggests running pipx ensurepath again:

  installed package meson 1.10.0, installed using Python 3.12.3
  These apps are now globally available
    - meson
  These manual pages are now globally available
    - man1/meson.1
⚠️  Note: '/home/behai/.local/bin' is not on your PATH environment variable. These apps will not be globally accessible until your PATH is updated. Run `pipx ensurepath` to automatically add it, or manually
    modify your PATH in your shell's config file (i.e. ~/.bashrc).
done! ✨ 🌟 ✨

● The second pipx ensurepath command reports:

/home/behai/.local/bin has been been added to PATH, but you need to open a new terminal or re-login for this PATH change to take effect.

You will need to open a new terminal or re-login for the PATH changes to take effect.

Otherwise pipx is ready to go! ✨ 🌟 ✨

Build and Install Pango and Associated Libraries

Install Development Packages

The following commands install all required development packages to build Pango:

$ sudo apt-get update
$ sudo apt-get install meson ninja-build pkg-config \
      gcc g++ ragel gtk-doc-tools \
      libglib2.0-dev libfreetype6-dev libfontconfig1-dev \
      libcairo2-dev libfribidi-dev libharfbuzz-dev

To confirm, run:

$ ls -l /usr/lib/x86_64-linux-gnu/pkgconfig/ | more

You should see entries such as:

cairo*.pc, freetype2.pc, fribidi.pc, glib-2.0.pc, gobject-2.0.pc, harfbuzz-*.pc, harfbuzz.pc, harfbuzz-subset.pc

Build Pango From Source and Install

Download the latest tarball from GNOME:

$ wget https://download.gnome.org/sources/pango/1.56/pango-1.56.4.tar.xz
$ tar xf pango-1.56.4.tar.xz
$ cd pango-1.56.4

Configure with Meson:

$ meson setup build --prefix=/usr/local

This command produces a long output. The final lines include:

...
pango 1.56.4

  Font backends
    Fontconfig   : true
    DirectWrite  : false
    CoreText     : false

  Features
    Cairo        : true
    Thai         : false
    Sysprof      : false

  Toolchain
    Compiler     : gcc
    Linker       : ld.bfd

  Build
    Debugging    : true
    Optimization : 2
    Introspection: false
    Documentation: false
    Man pages    : false
    Tests        : true
    Examples     : true

  Directories
    prefix       : /usr/local
    includedir   : /usr/local/include
    libdir       : /usr/local/lib/x86_64-linux-gnu
    datadir      : /usr/local/share

  Subprojects
    glib         : YES
    gvdb         : YES (from glib)

  User defined options
    prefix       : /usr/local

Found ninja-1.11.1 at /usr/bin/ninja

💥 Observation: In the Subprojects section, HarfBuzz is not listed. This suggests it is using the build and installation from the first article. If so, it raises the question of why the libharfbuzz-dev package was required as described. Most likely, the dev package ensures headers and pkg-config files are available for detection.

Build and Install:

$ ninja -C build
$ sudo ninja -C build install

The output of ninja -C build is lengthy and includes many warnings. The output of sudo ninja -C build install is also long, but appears clean. After installation, you should find pango.pc in /usr/local/lib/x86_64-linux-gnu/pkgconfig:

$ ls -l /usr/local/lib/x86_64-linux-gnu/pkgconfig/pango.pc

You should also have the pango-view executable:

$ sudo find / -iname pango-view

Output:

find: ‘/proc/5116’: No such file or directory
/home/behai/pango-1.56.4/build/utils/pango-view
/usr/local/bin/pango-view

Resolving Pango Version Conflict

/usr/local/bin/pango-view should be in the user path:

$ which pango-view

Checking the version:

$ pango-view --version

reports:

pango-view (pango) 1.56.4
Linked Pango library has a different version: 1.52.1

During configuration, the target version was pango 1.56.4. Observation: the linked version 1.52.1 likely comes with the Ubuntu distribution as noted earlier. It is a system-installed shared library located in /usr/lib/x86_64-linux-gnu/:

$ ls -l /usr/lib/x86_64-linux-gnu/libpango*-1.0.so*
lrwxrwxrwx 1 root root     24 Mar 31  2024 /usr/lib/x86_64-linux-gnu/libpango-1.0.so.0 -> libpango-1.0.so.0.5200.1
-rw-r--r-- 1 root root 433912 Mar 31  2024 /usr/lib/x86_64-linux-gnu/libpango-1.0.so.0.5200.1
lrwxrwxrwx 1 root root     29 Mar 31  2024 /usr/lib/x86_64-linux-gnu/libpangocairo-1.0.so.0 -> libpangocairo-1.0.so.0.5200.1
-rw-r--r-- 1 root root  67712 Mar 31  2024 /usr/lib/x86_64-linux-gnu/libpangocairo-1.0.so.0.5200.1
lrwxrwxrwx 1 root root     27 Mar 31  2024 /usr/lib/x86_64-linux-gnu/libpangoft2-1.0.so.0 -> libpangoft2-1.0.so.0.5200.1
-rw-r--r-- 1 root root 108536 Mar 31  2024 /usr/lib/x86_64-linux-gnu/libpangoft2-1.0.so.0.5200.1
lrwxrwxrwx 1 root root     27 Mar 31  2024 /usr/lib/x86_64-linux-gnu/libpangoxft-1.0.so.0 -> libpangoxft-1.0.so.0.5200.1
-rw-r--r-- 1 root root  38904 Mar 31  2024 /usr/lib/x86_64-linux-gnu/libpangoxft-1.0.so.0.5200.1

We configured pango 1.56.4 to install under /usr/local:

ls -l /usr/local/lib/x86_64-linux-gnu/libpango*-1.0.so*
lrwxrwxrwx 1 root root      17 Dec 15 11:34 /usr/local/lib/x86_64-linux-gnu/libpango-1.0.so -> libpango-1.0.so.0
lrwxrwxrwx 1 root root      24 Dec 15 11:34 /usr/local/lib/x86_64-linux-gnu/libpango-1.0.so.0 -> libpango-1.0.so.0.5600.4
-rwxr-xr-x 1 root root 1659184 Dec 15 11:34 /usr/local/lib/x86_64-linux-gnu/libpango-1.0.so.0.5600.4
lrwxrwxrwx 1 root root      22 Dec 15 11:34 /usr/local/lib/x86_64-linux-gnu/libpangocairo-1.0.so -> libpangocairo-1.0.so.0
lrwxrwxrwx 1 root root      29 Dec 15 11:34 /usr/local/lib/x86_64-linux-gnu/libpangocairo-1.0.so.0 -> libpangocairo-1.0.so.0.5600.4
-rwxr-xr-x 1 root root  245784 Dec 15 11:34 /usr/local/lib/x86_64-linux-gnu/libpangocairo-1.0.so.0.5600.4
lrwxrwxrwx 1 root root      20 Dec 15 11:34 /usr/local/lib/x86_64-linux-gnu/libpangoft2-1.0.so -> libpangoft2-1.0.so.0
lrwxrwxrwx 1 root root      27 Dec 15 11:34 /usr/local/lib/x86_64-linux-gnu/libpangoft2-1.0.so.0 -> libpangoft2-1.0.so.0.5600.4
-rwxr-xr-x 1 root root  454288 Dec 15 11:34 /usr/local/lib/x86_64-linux-gnu/libpangoft2-1.0.so.0.5600.4
lrwxrwxrwx 1 root root      20 Dec 15 11:34 /usr/local/lib/x86_64-linux-gnu/libpangoxft-1.0.so -> libpangoxft-1.0.so.0
lrwxrwxrwx 1 root root      27 Dec 15 11:34 /usr/local/lib/x86_64-linux-gnu/libpangoxft-1.0.so.0 -> libpangoxft-1.0.so.0.5600.4
-rwxr-xr-x 1 root root  165104 Dec 15 11:34 /usr/local/lib/x86_64-linux-gnu/libpangoxft-1.0.so.0.5600.4

At runtime, the dynamic linker ld searches library paths in this order:

  1. /lib, /usr/lib (system defaults)
  2. Any paths in /etc/ld.so.conf and ld.so.cache
  3. /usr/local/lib if configured

Because /usr/lib/x86_64-linux-gnu/ precedes /usr/local/lib/x86_64-linux-gnu/, pango-view links against pango 1.52.1 instead of pango 1.56.4. We need to instruct the linker to prefer /usr/local/lib/x86_64-linux-gnu/.

As a temporary measure, update LD_LIBRARY_PATH for the current session:

$ export LD_LIBRARY_PATH=/usr/local/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH
$ pango-view --version

It should now report pango-view (pango) 1.56.4.

To make this change permanent, use ldconfig.

Create /etc/ld.so.conf.d/local.conf containing:

/usr/local/lib/x86_64-linux-gnu

Then update the system’s library cache so /usr/local libraries are preferred:

$ sudo ldconfig

Verify with ldd:

$ ldd /usr/local/bin/pango-view | grep pango

You should see output similar to:

libpango-1.0.so.0 => /usr/local/lib/x86_64-linux-gnu/libpango-1.0.so.0 (0x000078a9b3e33000)
libpangoft2-1.0.so.0 => /usr/local/lib/x86_64-linux-gnu/libpangoft2-1.0.so.0 (0x000078a9b3e16000)
libpangoxft-1.0.so.0 => /usr/local/lib/x86_64-linux-gnu/libpangoxft-1.0.so.0 (0x000078a9b3e0b000)
libpangocairo-1.0.so.0 => /usr/local/lib/x86_64-linux-gnu/libpangocairo-1.0.so.0 (0x000078a9b3df7000)

After starting a fresh ssh session, pango-view should now report pango-view (pango) 1.56.4.

Test Pango Installation with pango-view

We can use pango-view to verify that Pango and its associated libraries are functioning correctly:

$ pango-view --no-display --text="Hello, 世界" --font="NotoSansTC-Regular 16" --output=hello.png

This should produce a small hello.png file containing the rendered text. Note that pango-view does not support PDF generation.

Windows: Build and Install Pango and Associated Libraries

Environment — the 64-bit Developer Command Prompt for Visual Studio 2022

💡 Important 💥 The steps that follow rely on an up-to-date installation of Visual Studio 2022 on Windows. I have not tested this process with later versions of Visual Studio, so I cannot comment on compatibility beyond 2022.

To access the 64-bit Developer Command Prompt for Visual Studio 2022 (or for your installed version), open a Windows Terminal session, launch a new Command Prompt, and run the following batch file:

"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"

You should see output similar to the following—note the x64 architecture:

**********************************************************************
** Visual Studio 2022 Developer Command Prompt v17.14.18
** Copyright (c) 2025 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

We covered this environment setup in detail in the first article.

Build and Install GNU FriBidi

The build process needs to know where to find vcpkg:

set PKG_CONFIG=C:\PF\vcpkg\installed\x64-windows\tools\pkgconf\pkgconf.exe
set PKG_CONFIG_PATH=C:\PF\vcpkg\installed\x64-windows\lib\pkgconfig

I am using the C:\PF\ directory as the root folder, as before:

c:
cd\PF

Download the source and prepare to build:

git clone https://github.com/fribidi/fribidi.git
cd fribidi
mkdir build

Then build:

meson setup build --buildtype=release --default-library=both -Ddocs=false
cd build
meson compile

Finally, install:

meson install --destdir ../dist

● The meson setup build... step should not take long. The output is short, ending with:

...
fribidi 1.0.16

  User defined options
    buildtype      : release
    default_library: both
    docs           : false

Found ninja-1.12.1 at "C:\Program Files\Meson\ninja.EXE"

● The meson compile command also produces a short output.

● The meson install... command should complete quickly, with minimal output. After installation, we should have the following files:

C:\PF\fribidi\dist\bin\[fribidi.exe, fribidi-0.dll]
C:\PF\fribidi\dist\include\fribidi\*.h
C:\PF\fribidi\dist\lib\[fribidi.lib, libfribidi.a]
C:\PF\fribidi\dist\lib\pkgconfig\fribidi.pc

⓷ Build and Install Cairo-1.18.4

The build process needs to know where to find vcpkg:

set PKG_CONFIG=C:\PF\vcpkg\installed\x64-windows\tools\pkgconf\pkgconf.exe
set PKG_CONFIG_PATH=C:\PF\vcpkg\installed\x64-windows\lib\pkgconfig

Download and Prepare Source

● Download https://cairographics.org/releases/cairo-1.18.4.tar.xz

● Extract and copy the entire cairo-1.18.4\ directory from the archive to C:\PF\cairo-1.18.4\

● Change to C:\PF\cairo-1.18.4\

Build Cairo From Source and Install

🦀 The working directory is C:\PF\cairo-1.18.4\. Run the following commands:

meson setup build --buildtype=release --default-library=shared -Dtests=disabled -Dpng=enabled -Dzlib=enabled -Dglib=enabled
meson compile -C build
meson install -C build --destdir ../dist

● The first command meson setup build... produces long output. The final lines include:

...
Build targets in project: 51

cairo 1.18.4

  Surface Backends
    Image                   : YES
    Recording               : YES
    Observer                : YES
    Mime                    : YES
    Tee                     : YES
    Xlib                    : NO
    Xlib Xrender            : NO
    Quartz                  : NO
    Quartz-image            : NO
    XCB                     : NO
    Win32                   : YES
    CairoScript             : YES
    PostScript              : YES
    PDF                     : YES
    SVG                     : YES

  Font Backends
    User                    : YES
    FreeType                : NO
    Fontconfig              : NO
    Win32                   : YES
    Win32 DWrite            : YES
    Quartz                  : NO

  Functions
    PNG functions           : YES
    X11-xcb                 : NO
    XCB-shm                 : NO

  Features and Utilities
    cairo-trace:            : NO
    cairo-script-interpreter: YES
    API reference           : NO

  Subprojects
    pixman                  : YES

  User defined options
    buildtype               : release
    default_library         : shared
    glib                    : enabled
    png                     : enabled
    tests                   : disabled
    zlib                    : enabled

Found ninja-1.12.1 at "C:\Program Files\Meson\ninja.EXE"

● The second command meson compile -C build produces a large number of warnings, but no errors.

● The third command meson install -C build --destdir ../dist produces only minimal output and completes fairly quickly.

After installation, we should have the following files:

C:\PF\cairo-1.18.4\dist\bin\cairo-2.dll
C:\PF\cairo-1.18.4\dist\bin\cairo-gobject-2.dll
C:\PF\cairo-1.18.4\dist\bin\cairo-script-interpreter-2.dll
C:\PF\cairo-1.18.4\dist\bin\pixman-1-0.dll

C:\PF\cairo-1.18.4\dist\include\cairo
C:\PF\cairo-1.18.4\dist\include\pixman-1

C:\PF\cairo-1.18.4\dist\include\cairo\cairo-deprecated.h
C:\PF\cairo-1.18.4\dist\include\cairo\cairo-dwrite.h
C:\PF\cairo-1.18.4\dist\include\cairo\cairo-features.h
C:\PF\cairo-1.18.4\dist\include\cairo\cairo-gobject.h
C:\PF\cairo-1.18.4\dist\include\cairo\cairo-pdf.h
C:\PF\cairo-1.18.4\dist\include\cairo\cairo-ps.h
C:\PF\cairo-1.18.4\dist\include\cairo\cairo-script-interpreter.h
C:\PF\cairo-1.18.4\dist\include\cairo\cairo-script.h
C:\PF\cairo-1.18.4\dist\include\cairo\cairo-svg.h
C:\PF\cairo-1.18.4\dist\include\cairo\cairo-tee.h
C:\PF\cairo-1.18.4\dist\include\cairo\cairo-version.h
C:\PF\cairo-1.18.4\dist\include\cairo\cairo-win32.h
C:\PF\cairo-1.18.4\dist\include\cairo\cairo.h
C:\PF\cairo-1.18.4\dist\include\pixman-1\pixman-version.h
C:\PF\cairo-1.18.4\dist\include\pixman-1\pixman.h

C:\PF\cairo-1.18.4\dist\lib\cairo-gobject.lib
C:\PF\cairo-1.18.4\dist\lib\cairo-script-interpreter.lib
C:\PF\cairo-1.18.4\dist\lib\cairo.lib
C:\PF\cairo-1.18.4\dist\lib\pixman-1.lib

C:\PF\cairo-1.18.4\dist\lib\pkgconfig\cairo-dwrite-font.pc
C:\PF\cairo-1.18.4\dist\lib\pkgconfig\cairo-gobject.pc
C:\PF\cairo-1.18.4\dist\lib\pkgconfig\cairo-pdf.pc
C:\PF\cairo-1.18.4\dist\lib\pkgconfig\cairo-png.pc
C:\PF\cairo-1.18.4\dist\lib\pkgconfig\cairo-ps.pc
C:\PF\cairo-1.18.4\dist\lib\pkgconfig\cairo-script-interpreter.pc
C:\PF\cairo-1.18.4\dist\lib\pkgconfig\cairo-script.pc
C:\PF\cairo-1.18.4\dist\lib\pkgconfig\cairo-svg.pc
C:\PF\cairo-1.18.4\dist\lib\pkgconfig\cairo-tee.pc
C:\PF\cairo-1.18.4\dist\lib\pkgconfig\cairo-win32-font.pc
C:\PF\cairo-1.18.4\dist\lib\pkgconfig\cairo-win32.pc
C:\PF\cairo-1.18.4\dist\lib\pkgconfig\cairo.pc
C:\PF\cairo-1.18.4\dist\lib\pkgconfig\pixman-1.pc

Using Our Own HarfBuzz Library

As mentioned at the beginning, by default the Pango build process will include its own HarfBuzz build. We need to explicitly instruct it to use our existing HarfBuzz build and installation instead.

⓹ Build and Install Pango

The build process needs to know where to find vcpkg and all dependent libraries. For example, to use our own HarfBuzz build, we must include C:\PF\harfbuzz\dist\lib\pkgconfig\ in the PKG_CONFIG_PATH environment variable:

set PKG_CONFIG=C:\PF\vcpkg\installed\x64-windows\tools\pkgconf\pkgconf.exe
set PKG_CONFIG_PATH=C:\PF\vcpkg\installed\x64-windows\lib\pkgconfig;C:\PF\harfbuzz\dist\lib\pkgconfig;C:\PF\fribidi\dist\lib\pkgconfig;C:\PF\cairo-1.18.4\dist\lib\pkgconfig

I am using the C:\PF\ directory as the root folder, as before:

c:
cd\PF

Download the source and prepare to build:

git clone https://gitlab.gnome.org/GNOME/pango.git
cd pango

Then build:

meson setup build --buildtype=release --default-library=shared -Dintrospection=disabled
meson compile -C build

Finally, install:

meson install -C build --destdir ../dist

● The meson setup build... step should not take long. The output is moderately lengthy, ending with:

...
pango 1.57.1

  Font backends
    Fontconfig     : false
    DirectWrite    : true
    CoreText       : false

  Features
    Cairo          : true
    Thai           : false
    Sysprof        : false

  Toolchain
    Compiler       : msvc
    Linker         : link

  Build
    Debugging      : false
    Optimization   : 3
    Introspection  : false
    Documentation  : false
    Man pages      : false
    Tests          : true
    Examples       : true

  Directories
    prefix         : c:/
    includedir     : c:/include
    libdir         : c:/lib
    datadir        : c:/share

  User defined options
    buildtype      : release
    default_library: shared
    introspection  : disabled

Found ninja-1.12.1 at "C:\Program Files\Meson\ninja.EXE"

● The meson compile... command produces moderately long output with warnings, but no errors.

● The meson install... command completes quickly, with minimal output. After installation, we should have the following files:

C:\PF\pango\dist\bin\pango-1.0-0.dll
C:\PF\pango\dist\bin\pango-list.exe
C:\PF\pango\dist\bin\pango-segmentation.exe
C:\PF\pango\dist\bin\pango-view.exe
C:\PF\pango\dist\bin\pangocairo-1.0-0.dll
C:\PF\pango\dist\bin\pangowin32-1.0-0.dll

C:\PF\pango\dist\include\pango-1.0\pango\pango-*.h
C:\PF\pango\dist\include\pango-1.0\pango\pango.h
C:\PF\pango\dist\include\pango-1.0\pango\pangocairo.h
C:\PF\pango\dist\include\pango-1.0\pango\pangowin32.h

C:\PF\pango\dist\lib\pango-1.0.lib
C:\PF\pango\dist\lib\pangocairo-1.0.lib
C:\PF\pango\dist\lib\pangowin32-1.0.lib

C:\PF\pango\dist\lib\pkgconfig\pango.pc
C:\PF\pango\dist\lib\pkgconfig\pangocairo.pc
C:\PF\pango\dist\lib\pkgconfig\pangowin32.pc

Test Pango Installation with pango-view

Check the version of pango-view.exe:

C:\PF\pango\dist\bin\pango-view.exe --version

This reports pango-view (pango) 1.57.1, which matches the version built above.

As on Ubuntu 🐧, we use pango-view to verify that Pango and its associated libraries are functioning correctly.

We need to ensure that all required DLLs are available in the PATH. Run the following once:

set PATH=C:\PF\harfbuzz\dist\bin\;%PATH%
set PATH=C:\PF\vcpkg\installed\x64-windows\bin\;%PATH%
set PATH=C:\PF\pango\dist\bin;C:\PF\cairo-1.18.4\dist\bin;C:\PF\fribidi\dist\bin;%PATH%

Then run pango-view:

C:\PF\pango\dist\bin\pango-view.exe --no-display --text="Hello, 世界" --font="Arial Unicode MS 16" --output=hello.png

This should produce a small hello.png file containing the rendered text, just as on Ubuntu 🐧.

What’s Next

While much of the information in this article was supplied by AIs, it still takes considerable effort to implement the instructions. I could not find any post that documents this process, so I decided to write it down for myself first and foremost. I hope it also proves useful to anyone else who happens to be on the same path.

As mentioned earlier, I have already completed the study Rust code that uses Pango and its associated libraries. I originally planned to discuss it here, but the article became too long, and I felt a bit exhausted writing it. We will cover the code in the next article.

Thanks for reading! As always—stay curious, stay safe 🦊

✿✿✿

Feature image sources:

🦀 Index of the Complete Series.