Files
opencv/cmake/OpenCVFindHarfBuzz.cmake
T
Mulham Fetna bfd89ecb8f cmake: check every hb_raster_* symbol used before selecting system HarfBuzz
The system-HarfBuzz probe referenced only hb_raster_draw_create_or_fail()
and hb_raster_draw_render(), while modules/imgproc/src/drawing_text.cpp
calls ten hb_raster_* entry points. A HarfBuzz that declares the whole
hb-raster API in its header but exports only part of it passes the probe,
so OpenCV links against it instead of falling back to the bundled copy,
and the build then fails with

    undefined reference to `hb_raster_draw_destroy'

Reference all ten symbols in the probe. The array is volatile because the
check compiles at -O3: a plain function address is never null, so the
compiler folds the test away and emits no relocations, which lets a
partial library pass.

Closes #29458
2026-08-22 14:27:19 +03:00

103 lines
4.6 KiB
CMake

# HarfBuzz text shaping + rasterization for cv::putText / cv::getTextSize.
#
# Behaviour (mirrors BUILD_PNG / BUILD_ZLIB etc.):
# WITH_HARFBUZZ=ON (default)
# Try to use a system HarfBuzz; if it is missing or too old to provide the
# hb-raster software-rasterizer API we depend on, build the bundled subset
# in 3rdparty/harfbuzz.
# WITH_HARFBUZZ=ON BUILD_HARFBUZZ=ON
# Skip the system search and always build the bundled subset.
# WITH_HARFBUZZ=OFF
# Disabled entirely; BUILD_HARFBUZZ has no effect (as with BUILD_PNG etc.).
if(WITH_HARFBUZZ)
include(CheckCXXSourceCompiles)
ocv_clear_vars(HAVE_HARFBUZZ HARFBUZZ_IS_BUNDLED)
if(NOT BUILD_HARFBUZZ)
# --- look for a system HarfBuzz via pkg-config ---
ocv_clear_internal_cache_vars(HARFBUZZ_LIBRARY HARFBUZZ_LIBRARIES HARFBUZZ_INCLUDE_DIR)
ocv_check_modules(HARFBUZZ harfbuzz)
if(HAVE_HARFBUZZ)
# The hb-raster software rasterizer is a recent addition and may be
# shipped as a separate library: e.g. Homebrew/Linux split it into
# libharfbuzz-raster (with its own harfbuzz-raster.pc) while the plain
# harfbuzz.pc links only -lharfbuzz (no hb_raster_* symbols). Pull in the
# raster module when present; otherwise assume a monolithic build.
ocv_check_modules(HARFBUZZ_RASTER harfbuzz-raster)
if(HAVE_HARFBUZZ_RASTER)
# harfbuzz-raster.pc "Requires: harfbuzz", so its resolved flags already
# include the core library and include dir.
set(_hb_inc "${HARFBUZZ_RASTER_INCLUDE_DIRS}")
set(_hb_libs "${HARFBUZZ_RASTER_LIBRARIES}")
else()
set(_hb_inc "${HARFBUZZ_INCLUDE_DIRS}")
set(_hb_libs "${HARFBUZZ_LIBRARIES}")
endif()
# Verify the header and symbols actually compile and link with the chosen
# libraries; otherwise fall back to the bundled copy.
# Reference every hb_raster_* entry point used by
# modules/imgproc/src/drawing_text.cpp: a system HarfBuzz may declare the
# whole hb-raster API in its header while exporting only part of it, which
# passes a narrower probe and then fails at link time. The array is
# volatile so the initializers are not optimized away at -O3 (a function
# address is never null, so the compiler would otherwise fold the test
# away and emit no relocations, letting a partial library pass).
ocv_clear_vars(HAVE_HARFBUZZ)
set(CMAKE_REQUIRED_INCLUDES "${_hb_inc}")
set(CMAKE_REQUIRED_LIBRARIES "${_hb_libs}")
check_cxx_source_compiles("
#include <hb.h>
#include <hb-raster.h>
int main() {
void (*volatile fns[])() = {
(void(*)())&hb_raster_draw_create_or_fail,
(void(*)())&hb_raster_draw_destroy,
(void(*)())&hb_raster_draw_set_scale_factor,
(void(*)())&hb_raster_draw_set_extents,
(void(*)())&hb_raster_draw_set_glyph_extents,
(void(*)())&hb_raster_draw_glyph,
(void(*)())&hb_raster_draw_render,
(void(*)())&hb_raster_draw_recycle_image,
(void(*)())&hb_raster_image_get_extents,
(void(*)())&hb_raster_image_get_buffer,
};
return fns[0] ? 0 : 1;
}" HARFBUZZ_HAS_RASTER)
unset(CMAKE_REQUIRED_INCLUDES)
unset(CMAKE_REQUIRED_LIBRARIES)
if(HARFBUZZ_HAS_RASTER)
set(HARFBUZZ_INCLUDE_DIR "${_hb_inc}" CACHE INTERNAL "")
set(HARFBUZZ_LIBRARIES "${_hb_libs}" CACHE INTERNAL "")
set(HAVE_HARFBUZZ 1)
else()
message(STATUS "HarfBuzz: found system version ${HARFBUZZ_VERSION} but it lacks the hb-raster API; building the bundled copy instead")
endif()
endif()
endif()
if(NOT HAVE_HARFBUZZ)
# --- build the bundled HarfBuzz subset (HB_TINY + hb-raster) ---
ocv_clear_vars(HARFBUZZ_LIBRARY HARFBUZZ_LIBRARIES HARFBUZZ_INCLUDE_DIR)
set(HARFBUZZ_LIBRARY libharfbuzz CACHE INTERNAL "")
set(HARFBUZZ_LIBRARIES ${HARFBUZZ_LIBRARY})
add_subdirectory("${OpenCV_SOURCE_DIR}/3rdparty/harfbuzz")
set(HARFBUZZ_INCLUDE_DIR "${${HARFBUZZ_LIBRARY}_SOURCE_DIR}/src" CACHE INTERNAL "")
# Read the bundled version straight from the sources so it stays correct
# when HarfBuzz is updated (re-run of hb_extract.py) without touching this file.
set(_hb_version "unknown")
file(STRINGS "${OpenCV_SOURCE_DIR}/3rdparty/harfbuzz/src/hb-version.h" _hb_version_line
REGEX "^#define[ \t]+HB_VERSION_STRING[ \t]+\"[^\"]*\"")
if(_hb_version_line MATCHES "\"([^\"]*)\"")
set(_hb_version "${CMAKE_MATCH_1}")
endif()
set(HARFBUZZ_VERSION "build (${_hb_version})" CACHE INTERNAL "")
set(HARFBUZZ_IS_BUNDLED YES)
set(HAVE_HARFBUZZ 1)
endif()
endif()