Skip to content

Commit

Permalink
Merge pull request #201 from eugo-inc/main
Browse files Browse the repository at this point in the history
Use System HarfBuzz
  • Loading branch information
anthrotype committed Aug 23, 2024
2 parents e66c546 + 8d646f5 commit a156682
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 58 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@ for info, pos in zip(infos, positions):
print(f"{glyph_name} gid{gid}={cluster}@{x_advance},{y_offset}+{x_advance}")
```

### Installation

When building the uharfbuzz package, it automatically incorporates minimal HarfBuzz sources so you don't have to install the native HarfBuzz library.

However, if you want to use uharfbuzz with your system-provided HarfBuzz (e.g., if you built it from sources with custom configuration), you can set `USE_SYSTEM_LIBS=1` environment variable (see example below).

```shell
USE_SYSTEM_LIBS=1 pip install uharfbuzz --no-binary :uharfbuzz:
```

harfbuzz installation is found using `pkg-config`, so you must have harfbuzz's `.pc` files in your system.
If you've built it from sources, meson installs them automatically. Otherwise, you may want to install harfbuzz development package, like `harfbuzz-devel` on Fedora-derived distros.

Note: you **must** build HarfBuzz with experimental API support enabled.

### How to make a release

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ requires = [
"wheel",
"setuptools_scm >= 2.1",
"cython >= 0.28.1",
"pkgconfig"
]
build-backend = "setuptools.build_meta"

Expand Down
203 changes: 145 additions & 58 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,71 +1,158 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from io import open
import os
import sys
import platform
from setuptools import Extension, setup
from io import open
from typing import List

import pkgconfig
from Cython.Build import cythonize
from setuptools import Extension, setup

def bool_from_environ(key: str):
value = os.environ.get(key)
if not value:
return False
if value == "1":
return True
if value == "0":
return False
raise ValueError(f"Environment variable {key} has invalid value {value}. Please set it to 1, 0 or an empty string")

here = os.path.abspath(os.path.dirname(__file__))

# Get the long description from the README file
with open(os.path.join(here, "README.md"), encoding="utf-8") as f:
long_description = f.read()

define_macros = [("HB_NO_MT", "1"), ("HB_EXPERIMENTAL_API", "1")]
linetrace = False
if int(os.environ.get("CYTHON_LINETRACE", "0")):
linetrace = True
define_macros.append(("CYTHON_TRACE_NOGIL", "1"))

extra_compile_args = []
extra_link_args = []
libraries = []
if platform.system() != "Windows":
extra_compile_args.append("-std=c++11")
define_macros.append(("HAVE_MMAP", "1"))
define_macros.append(("HAVE_UNISTD_H", "1"))
define_macros.append(("HAVE_SYS_MMAN_H", "1"))
else:
define_macros.append(("HAVE_DIRECTWRITE", "1"))
define_macros.append(("HAVE_UNISCRIBE", "1"))
libraries += ["usp10", "gdi32", "user32", "rpcrt4", "dwrite"]

if platform.system() == "Darwin":
define_macros.append(("HAVE_CORETEXT", "1"))
extra_link_args.extend(["-framework", "ApplicationServices"])

extension = Extension(
"uharfbuzz._harfbuzz",
define_macros=define_macros,
include_dirs=["harfbuzz/src"],
sources=[
"harfbuzz/src/harfbuzz-subset.cc",
"harfbuzz/src/hb-coretext.cc",
"harfbuzz/src/hb-directwrite.cc",
"harfbuzz/src/hb-uniscribe.cc",
"src/uharfbuzz/_harfbuzz.pyx",
],
language="c++",
libraries=libraries,
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
)
use_system_libraries = bool_from_environ("USE_SYSTEM_LIBS")
use_cython_linetrace = bool_from_environ("CYTHON_LINETRACE")
use_cython_annotate = bool_from_environ("CYTHON_ANNOTATE")

extension_test = Extension(
"uharfbuzz._harfbuzz_test",
define_macros=define_macros,
include_dirs=["harfbuzz/src"],
sources=[
"src/uharfbuzz/_draw_test_funcs.cc",
"src/uharfbuzz/_harfbuzz_test.pyx",
],
language="c++",
libraries=libraries,
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
)
def _configure_extensions_with_system_libs() -> List[Extension]:
include_dirs = []
define_macros = []
libraries = []
library_dirs = []

# There is no way (or, at least, no reliable one) to check whether `harfbuzz` has been built with experimental API support.
#
# meson-generated CMake[1] and pkg-config[2] files don't include harfbuzz define macros, neither separately nor as `-D` defines in `cflags`.
#
# `config.h`[3] and `hb-config.hh`[4] are generated at buildtime but treated as internal headers. That is, they are only used to compile harfbuzz,
# but are never installed nor exposed to the library consumers in other ways. Some configuration parameters (e.g., either harfbuzz has been built
# with `directwrite` support) are exposed via `hb-features.h`[5, 6] header, but not the one associated with experimental API.
#
# One solution here could be, patch `harfbuzz` to expose so, but the quickest and "cheapest" fix is just force-turn the experimental API in headers.
# If native `harfbuzz` is built without experimental API support, `uharfbuzz` will fail to build with undefined symbols errors.
#
# References:
# 1. https://github.com/harfbuzz/harfbuzz/blob/9.0.0/src/harfbuzz-config.cmake.in
# 2. https://github.com/harfbuzz/harfbuzz/blob/9.0.0/src/harfbuzz.pc.in
# 3. https://github.com/harfbuzz/harfbuzz/blob/9.0.0/meson.build#L461
# 4. https://github.com/harfbuzz/harfbuzz/blob/main/src/hb-config.hh
# 5. https://github.com/harfbuzz/harfbuzz/blob/9.0.0/src/hb-features.h.in
# 6. https://github.com/harfbuzz/harfbuzz/pull/3847
define_macros.append(("HB_EXPERIMENTAL_API", "1"))

harfbuzz_components = ["harfbuzz-subset"]
for harfbuzz_component in harfbuzz_components:
harfbuzz_component_configuration = pkgconfig.parse(harfbuzz_component)
include_dirs += harfbuzz_component_configuration["include_dirs"]
define_macros += harfbuzz_component_configuration["define_macros"]
libraries += harfbuzz_component_configuration["libraries"]
library_dirs += harfbuzz_component_configuration["library_dirs"]

if use_cython_linetrace:
define_macros.append(("CYTHON_TRACE_NOGIL", "1"))

extension = Extension(
"uharfbuzz._harfbuzz",
define_macros=define_macros,
include_dirs=include_dirs,
sources=[
"src/uharfbuzz/_harfbuzz.pyx",
],
language="c++",
libraries=libraries,
library_dirs=library_dirs
)

extension_test = Extension(
"uharfbuzz._harfbuzz_test",
define_macros=define_macros,
include_dirs=include_dirs,
sources=[
"src/uharfbuzz/_draw_test_funcs.cc",
"src/uharfbuzz/_harfbuzz_test.pyx",
],
language="c++",
libraries=libraries,
library_dirs=library_dirs
)

return [extension, extension_test]

def _configure_extensions_with_vendored_libs() -> List[Extension]:
define_macros = [("HB_NO_MT", "1"), ("HB_EXPERIMENTAL_API", "1")]
if use_cython_linetrace:
define_macros.append(("CYTHON_TRACE_NOGIL", "1"))

extra_compile_args = []
extra_link_args = []
libraries = []
if platform.system() != "Windows":
extra_compile_args.append("-std=c++11")
define_macros.append(("HAVE_MMAP", "1"))
define_macros.append(("HAVE_UNISTD_H", "1"))
define_macros.append(("HAVE_SYS_MMAN_H", "1"))
else:
define_macros.append(("HAVE_DIRECTWRITE", "1"))
define_macros.append(("HAVE_UNISCRIBE", "1"))
libraries += ["usp10", "gdi32", "user32", "rpcrt4", "dwrite"]

if platform.system() == "Darwin":
define_macros.append(("HAVE_CORETEXT", "1"))
extra_link_args.extend(["-framework", "ApplicationServices"])

extension = Extension(
"uharfbuzz._harfbuzz",
define_macros=define_macros,
include_dirs=["harfbuzz/src"],
sources=[
"harfbuzz/src/harfbuzz-subset.cc",
"harfbuzz/src/hb-coretext.cc",
"harfbuzz/src/hb-directwrite.cc",
"harfbuzz/src/hb-uniscribe.cc",
"src/uharfbuzz/_harfbuzz.pyx",
],
language="c++",
libraries=libraries,
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
)

extension_test = Extension(
"uharfbuzz._harfbuzz_test",
define_macros=define_macros,
include_dirs=["harfbuzz/src"],
sources=[
"src/uharfbuzz/_draw_test_funcs.cc",
"src/uharfbuzz/_harfbuzz_test.pyx",
],
language="c++",
libraries=libraries,
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
)

return [extension, extension_test]

def configure_extensions() -> List[Extension]:
if use_system_libraries:
return _configure_extensions_with_system_libs()
else:
return _configure_extensions_with_vendored_libs()

setup(
name="uharfbuzz",
Expand All @@ -83,8 +170,8 @@
setup_requires=["setuptools_scm"],
python_requires=">=3.5",
ext_modules=cythonize(
[extension, extension_test],
annotate=bool(int(os.environ.get("CYTHON_ANNOTATE", "0"))),
compiler_directives={"linetrace": linetrace},
configure_extensions(),
annotate=use_cython_annotate,
compiler_directives={"linetrace": use_cython_linetrace},
),
)

0 comments on commit a156682

Please sign in to comment.