diff --git a/.ci/install.sh b/.ci/install.sh index db680db96b6..86656664853 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -19,7 +19,7 @@ set -e sudo apt-get -qq install libfreetype6-dev liblcms2-dev libtiff-dev python3-tk\ ghostscript libjpeg-turbo8-dev libopenjp2-7-dev\ - cmake meson imagemagick libharfbuzz-dev libfribidi-dev\ + cmake meson imagemagick libharfbuzz-dev\ sway wl-clipboard libopenblas-dev nasm python3 -m pip install --upgrade pip @@ -49,7 +49,7 @@ pushd depends && ./install_webp.sh && popd pushd depends && ./install_imagequant.sh && popd # raqm -pushd depends && sudo ./install_raqm.sh && popd +pushd depends && sudo ./install_sheenbidi.sh && sudo ./install_raqm.sh && popd # libavif pushd depends && ./install_libavif.sh && popd diff --git a/.github/INCIDENT_RESPONSE.md b/.github/INCIDENT_RESPONSE.md index 1c2e395ddf8..88c2f1c0008 100644 --- a/.github/INCIDENT_RESPONSE.md +++ b/.github/INCIDENT_RESPONSE.md @@ -241,7 +241,7 @@ require a Pillow point release even if Pillow's own code is unchanged. | [lcms2](https://www.littlecms.com/) | ICC color management | [GitHub](https://github.com/mm2/Little-CMS/security) | | [harfbuzz](https://harfbuzz.github.io/) | Text shaping (via raqm) | [GitHub](https://github.com/harfbuzz/harfbuzz/security) | | [raqm](https://github.com/HOST-Oman/libraqm) | Complex text layout | [GitHub](https://github.com/HOST-Oman/libraqm) | -| [fribidi](https://github.com/fribidi/fribidi) | Unicode bidi (via raqm) | [GitHub](https://github.com/fribidi/fribidi) | +| [sheenbidi](https://github.com/Tehreer/SheenBidi) | Unicode bidi (via raqm) | [GitHub](https://github.com/Tehreer/SheenBidi) | | [zlib](https://zlib.net/) | Deflate compression | [zlib.net](https://zlib.net/) | | [liblzma / xz-utils](https://tukaani.org/xz/) | XZ/LZMA compression | [GitHub](https://github.com/tukaani-project/xz/security) | | [bzip2](https://gitlab.com/bzip2/bzip2) | BZ2 compression | [GitLab](https://gitlab.com/bzip2/bzip2/-/work_items) | diff --git a/.github/dependencies.json b/.github/dependencies.json index c75baa88d29..344d37ed558 100644 --- a/.github/dependencies.json +++ b/.github/dependencies.json @@ -2,7 +2,6 @@ "brotli": "1.2.0", "bzip2": "1.0.8", "freetype": "2.14.3", - "fribidi": "1.0.16", "harfbuzz": "14.2.1", "jpegturbo": "3.2.0", "lcms2": "2.19.1", @@ -12,6 +11,8 @@ "libwebp": "1.6.0", "libxcb": "1.17.0", "openjpeg": "2.5.4", + "raqm": "0.11.0", + "sheenbidi": "3.0.0", "tiff": "4.7.2", "xz": "5.8.3", "zlib-ng": "2.3.3", diff --git a/.github/generate-sbom.py b/.github/generate-sbom.py index 347edf51462..837b2c50c87 100755 --- a/.github/generate-sbom.py +++ b/.github/generate-sbom.py @@ -11,12 +11,9 @@ from __future__ import annotations import argparse -import base64 import datetime as dt -import difflib import hashlib import json -import urllib.request import uuid from pathlib import Path @@ -35,28 +32,6 @@ def sha256_file(path: Path) -> str: return hashlib.sha256(path.read_bytes()).hexdigest() -def upstream_diff_b64( - upstream_url: str, - upstream_display: bytes, - local_path: Path, - local_display: bytes, -) -> str: - """ - Fetch an upstream file and return a base64-encoded unified diff vs the local copy. - """ - with urllib.request.urlopen(upstream_url) as resp: - upstream_text = resp.read() - local_text = local_path.read_bytes() - diff_lines = difflib.diff_bytes( - difflib.unified_diff, - upstream_text.splitlines(keepends=True), - local_text.splitlines(keepends=True), - fromfile=b"a/" + upstream_display, - tofile=b"b/" + local_display, - ) - return base64.b64encode(b"".join(diff_lines)).decode() - - def generate(version: str) -> dict: serial = str(uuid.uuid4()) now = dt.datetime.now(dt.UTC).strftime("%Y-%m-%dT%H:%M:%SZ") @@ -115,28 +90,6 @@ def generate(version: str) -> dict: ] vendored_components = [ - { - "bom-ref": f"{purl}#thirdparty/fribidi-shim", - "type": "library", - "name": "fribidi-shim", - "version": "1.x", - "description": "FriBiDi runtime-loading shim " - "(vendored in src/thirdparty/fribidi-shim/); " - "loads libfribidi dynamically", - "licenses": [{"license": {"id": "LGPL-2.1-or-later"}}], - "hashes": [ - { - "alg": "SHA-256", - "content": sha256_file(thirdparty / "fribidi-shim" / "fribidi.c"), - } - ], - "pedigree": { - "notes": "Pillow-authored shim; not taken from an upstream project." - }, - "externalReferences": [ - {"type": "website", "url": "https://github.com/fribidi/fribidi"}, - ], - }, { "bom-ref": "pkg:github/python/pythoncapi-compat", "type": "library", @@ -160,90 +113,6 @@ def generate(version: str) -> dict: }, ], }, - { - "bom-ref": f"{purl}#thirdparty/raqm", - "type": "library", - "name": "raqm", - "version": "0.11.0", - "description": "Complex text layout library " - "(vendored in src/thirdparty/raqm/)", - "licenses": [{"license": {"id": "MIT"}}], - "hashes": [ - { - "alg": "SHA-256", - "content": sha256_file(thirdparty / "raqm" / "raqm.c"), - } - ], - "pedigree": { - "ancestors": [ - { - "bom-ref": "pkg:github/HOST-Oman/libraqm@0.11.0#upstream", - "type": "library", - "name": "raqm", - "version": "0.11.0", - "purl": "pkg:github/HOST-Oman/libraqm@0.11.0", - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/HOST-Oman/libraqm/releases/tag/v0.11.0", - } - ], - } - ], - "patches": [ - { - "type": "unofficial", - "diff": { - "text": { - # raqm-version.h.in → raqm-version.h: - # template @RAQM_VERSION_*@ placeholders replaced - # with literal 0.11.0 values; filename changed to - # drop the .in suffix. - "content": upstream_diff_b64( - "https://raw.githubusercontent.com/HOST-Oman/libraqm/v0.11.0/src/raqm-version.h.in", - b"src/raqm-version.h.in", - thirdparty / "raqm" / "raqm-version.h", - b"src/raqm-version.h", - ), - "encoding": "base64", - } - }, - }, - { - "type": "unofficial", - "diff": { - "text": { - # raqm.c: wrap the include in an - # #ifdef HAVE_FRIBIDI_SYSTEM guard so that when - # building without a system FriBiDi Pillow's own - # fribidi-shim is used instead. - "content": upstream_diff_b64( - "https://raw.githubusercontent.com/HOST-Oman/libraqm/v0.11.0/src/raqm.c", - b"src/raqm.c", - thirdparty / "raqm" / "raqm.c", - b"src/raqm.c", - ), - "encoding": "base64", - } - }, - }, - ], - "notes": ( - "Vendored from upstream HOST-Oman/libraqm v0.11.0 with two " - "Pillow-specific modifications: (1) raqm-version.h.in was " - "pre-processed into raqm-version.h with version placeholders " - "replaced by literal values; (2) raqm.c wraps the " - "include in an #ifdef HAVE_FRIBIDI_SYSTEM guard so Pillow's " - "bundled fribidi-shim is used when a system FriBiDi is absent." - ), - }, - "externalReferences": [ - { - "type": "vcs", - "url": "https://github.com/python-pillow/Pillow/tree/main/src/thirdparty/raqm", - }, - ], - }, ] native_deps = [ @@ -264,23 +133,6 @@ def generate(version: str) -> dict: }, ], }, - { - "bom-ref": "pkg:generic/fribidi", - "type": "library", - "name": "FriBiDi", - "version": versions["fribidi"], - "scope": "optional", - "description": "Unicode bidi algorithm library (optional, " - "loaded at runtime by fribidi-shim).", - "licenses": [{"license": {"id": "LGPL-2.1-or-later"}}], - "externalReferences": [ - {"type": "website", "url": "https://github.com/fribidi/fribidi"}, - { - "type": "distribution", - "url": "https://github.com/fribidi/fribidi/releases", - }, - ], - }, { "bom-ref": "pkg:generic/harfbuzz", "type": "library", @@ -434,6 +286,39 @@ def generate(version: str) -> dict: }, ], }, + { + "bom-ref": "pkg:generic/raqm", + "type": "library", + "name": "Raqm", + "version": versions["raqm"], + "scope": "optional", + "description": "Complex text layout (optional, used by PIL._imagingft).", + "licenses": [{"license": {"id": "MIT"}}], + "externalReferences": [ + {"type": "website", "url": "https://github.com/HOST-Oman/libraqm"}, + { + "type": "distribution", + "url": "https://github.com/HOST-Oman/libraqm/releases", + }, + ], + }, + { + "bom-ref": "pkg:generic/sheenbidi", + "type": "library", + "name": "SheenBidi", + "version": versions["sheenbidi"], + "scope": "optional", + "description": "Unicode bidirectional algorithm (optional, required " + "by libraqm for bidirectional text).", + "licenses": [{"license": {"id": "Apache-2.0"}}], + "externalReferences": [ + {"type": "website", "url": "https://github.com/Tehreer/SheenBidi"}, + { + "type": "distribution", + "url": "https://github.com/Tehreer/SheenBidi/releases", + }, + ], + }, { "bom-ref": "pkg:pypi/pybind11", "type": "library", @@ -492,10 +377,7 @@ def generate(version: str) -> dict: "ref": f"{purl}#c-ext/PIL._imagingft", "dependsOn": [ "pkg:generic/freetype2", - "pkg:generic/fribidi", - "pkg:generic/harfbuzz", - f"{purl}#thirdparty/fribidi-shim", - f"{purl}#thirdparty/raqm", + "pkg:generic/raqm", ], }, { @@ -503,10 +385,11 @@ def generate(version: str) -> dict: "dependsOn": ["pkg:generic/libwebp"], }, { - "ref": f"{purl}#thirdparty/raqm", + "ref": "pkg:generic/raqm", "dependsOn": [ + "pkg:generic/freetype2", "pkg:generic/harfbuzz", - f"{purl}#thirdparty/fribidi-shim", + "pkg:generic/sheenbidi", ], }, ] diff --git a/.github/renovate.json b/.github/renovate.json index f26109f29ec..43a3c97f9ff 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -41,15 +41,6 @@ "extractVersionTemplate": "^VER-(?[\\d-]+)$", "versioningTemplate": "regex:^(?\\d+)[.-](?\\d+)[.-](?\\d+)$" }, - { - "customType": "regex", - "managerFilePatterns": ["/^\\.github/dependencies\\.json$/"], - "matchStrings": ["\"fribidi\":\\s*\"(?\\d+[^\"]*)\""], - "depNameTemplate": "fribidi", - "packageNameTemplate": "fribidi/fribidi", - "datasourceTemplate": "github-releases", - "extractVersionTemplate": "^v(?.+)$" - }, { "customType": "regex", "managerFilePatterns": ["/^\\.github/dependencies\\.json$/"], @@ -129,6 +120,24 @@ "datasourceTemplate": "github-releases", "extractVersionTemplate": "^v(?.+)$" }, + { + "customType": "regex", + "managerFilePatterns": ["/^\\.github/dependencies\\.json$/"], + "matchStrings": ["\"raqm\":\\s*\"(?\\d+[^\"]*)\""], + "depNameTemplate": "raqm", + "packageNameTemplate": "HOST-Oman/libraqm", + "datasourceTemplate": "github-releases", + "extractVersionTemplate": "^v(?.+)$" + }, + { + "customType": "regex", + "managerFilePatterns": ["/^\\.github/dependencies\\.json$/"], + "matchStrings": ["\"sheenbidi\":\\s*\"(?\\d+[^\"]*)\""], + "depNameTemplate": "sheenbidi", + "packageNameTemplate": "Tehreer/SheenBidi", + "datasourceTemplate": "github-releases", + "extractVersionTemplate": "^v(?.+)$" + }, { "customType": "regex", "managerFilePatterns": ["/^\\.github/dependencies\\.json$/"], diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index b5e3a7f46a1..b843dafc560 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -176,9 +176,13 @@ jobs: run: "& winbuild\\build\\build_dep_harfbuzz.cmd" # Raqm dependencies - - name: Build dependencies / FriBidi + - name: Build dependencies / SheenBidi if: steps.build-cache.outputs.cache-hit != 'true' - run: "& winbuild\\build\\build_dep_fribidi.cmd" + run: "& winbuild\\build\\build_dep_sheenbidi.cmd" + + - name: Build dependencies / Raqm + if: steps.build-cache.outputs.cache-hit != 'true' + run: "& winbuild\\build\\build_dep_raqm.cmd" # trim ~150MB for each job - name: Optimize build cache @@ -189,7 +193,7 @@ jobs: - name: Build Pillow run: | - $FLAGS="-C raqm=vendor -C fribidi=vendor" + $FLAGS="-C raqm=enable" cmd /c "winbuild\build\build_env.cmd && $env:pythonLocation\python.exe -m pip install -v $FLAGS .[tests]" & $env:pythonLocation\python.exe selftest.py --installed shell: pwsh diff --git a/.github/workflows/wheels-dependencies.sh b/.github/workflows/wheels-dependencies.sh index b3a5782091b..e02d34b4d95 100755 --- a/.github/workflows/wheels-dependencies.sh +++ b/.github/workflows/wheels-dependencies.sh @@ -107,6 +107,8 @@ BZIP2_VERSION=$(_get_ver bzip2) LIBXCB_VERSION=$(_get_ver libxcb) BROTLI_VERSION=$(_get_ver brotli) LIBAVIF_VERSION=$(_get_ver libavif) +RAQM_VERSION=$(_get_ver raqm) +SHEENBIDI_VERSION=$(_get_ver sheenbidi) function build_pkg_config { if [ -e pkg-config-stamp ]; then return; fi @@ -165,12 +167,36 @@ function build_harfbuzz { local out_dir=$(fetch_unpack https://github.com/harfbuzz/harfbuzz/releases/download/$HARFBUZZ_VERSION/harfbuzz-$HARFBUZZ_VERSION.tar.xz harfbuzz-$HARFBUZZ_VERSION.tar.xz) (cd $out_dir \ - && meson setup build --prefix=$BUILD_PREFIX --libdir=$BUILD_PREFIX/lib --buildtype=minsize -Dfreetype=enabled -Dglib=disabled -Dtests=disabled $HOST_MESON_FLAGS) + && meson setup build --prefix=$BUILD_PREFIX --libdir=$BUILD_PREFIX/lib --buildtype=minsize -Dfreetype=enabled -Dglib=disabled -Dtests=disabled -Dutilities=disabled $HOST_MESON_FLAGS) (cd $out_dir/build \ && meson install) touch harfbuzz-stamp } +function build_sheenbidi { + if [ -e sheenbidi-stamp ]; then return; fi + python3 -m pip install meson ninja + + local out_dir=$(fetch_unpack https://github.com/Tehreer/SheenBidi/archive/v$SHEENBIDI_VERSION.tar.gz SheenBidi-$SHEENBIDI_VERSION.tar.gz) + (cd $out_dir \ + && meson setup build --prefix=$BUILD_PREFIX --libdir=$BUILD_PREFIX/lib --buildtype=minsize $HOST_MESON_FLAGS) + (cd $out_dir/build \ + && meson install) + touch sheenbidi-stamp +} + +function build_raqm { + if [ -e raqm-stamp ]; then return; fi + python3 -m pip install meson ninja + + local out_dir=$(fetch_unpack https://github.com/HOST-Oman/libraqm/releases/download/v$RAQM_VERSION/raqm-$RAQM_VERSION.tar.xz raqm-$RAQM_VERSION.tar.xz) + (cd $out_dir \ + && meson setup build --prefix=$BUILD_PREFIX --libdir=$BUILD_PREFIX/lib --buildtype=minsize -Dsheenbidi=true -Ddocs=false -Dtests=false $HOST_MESON_FLAGS) + (cd $out_dir/build \ + && meson install) + touch raqm-stamp +} + function build_libavif { if [ -e libavif-stamp ]; then return; fi @@ -311,11 +337,9 @@ function build { build_freetype fi - if [[ -z "$IOS_SDK" ]]; then - # On iOS, there's no vendor-provided raqm, and we can't ship it due to - # licensing, so there's no point building harfbuzz. - build_harfbuzz - fi + build_harfbuzz + build_sheenbidi + build_raqm } function create_meson_cross_config { diff --git a/.github/workflows/wheels-test.sh b/.github/workflows/wheels-test.sh index d73b6be5847..0c6040174c4 100755 --- a/.github/workflows/wheels-test.sh +++ b/.github/workflows/wheels-test.sh @@ -1,28 +1,10 @@ #!/bin/bash set -e -# Ensure fribidi is installed by the system. -if [[ "$OSTYPE" == "darwin"* ]]; then - # If Homebrew is on the path during the build, it may leak into the wheels. - # However, we *do* need Homebrew to provide a copy of fribidi for - # testing purposes so that we can verify the fribidi shim works as expected. - if [[ "$(uname -m)" == "x86_64" ]]; then - HOMEBREW_PREFIX=/usr/local - else - HOMEBREW_PREFIX=/opt/homebrew - fi - $HOMEBREW_PREFIX/bin/brew install fribidi - - # Add the lib folder for fribidi so that the vendored library can be found. - # Don't use $HOMEWBREW_PREFIX/lib directly - use the lib folder where the - # installed copy of fribidi is cellared. This ensures we don't pick up the - # Homebrew version of any other library that we're dependent on (most notably, - # freetype). - export DYLD_LIBRARY_PATH=$(dirname $(realpath $HOMEBREW_PREFIX/lib/libfribidi.dylib)) -elif [ "${AUDITWHEEL_POLICY::9}" == "musllinux" ]; then - apk add curl fribidi -else - yum install -y fribidi +if [ "${AUDITWHEEL_POLICY::9}" == "musllinux" ]; then + apk add curl +elif [ "${AUDITWHEEL_POLICY::9}" == "manylinux" ]; then + ldconfig fi if [ ! -d "test-images-main" ]; then diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index b9fbd9f84ed..3b09e7fa8aa 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -22,7 +22,7 @@ on: - "setup.py" - "wheels/*" - "winbuild/build_prepare.py" - - "winbuild/fribidi.cmake" + - "winbuild/raqm.cmake" tags: - "*" pull_request: @@ -221,8 +221,6 @@ jobs: run: | for f in winbuild/build/license/*; do name=$(basename "${f%.*}") - # Skip FriBiDi license, it is not included in the wheel. - [[ $name == fribidi* ]] && continue # Skip imagequant license, it is not included in the wheel. [[ $name == libimagequant* ]] && continue echo "" >> LICENSE @@ -261,12 +259,6 @@ jobs: name: dist-windows-${{ matrix.cibw_arch }} path: ./wheelhouse/*.whl - - name: Upload fribidi.dll - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: fribidi-windows-${{ matrix.cibw_arch }} - path: winbuild\build\bin\fribidi* - sdist: if: github.event_name != 'schedule' || github.event.repository.fork == false runs-on: ubuntu-latest diff --git a/Tests/test_features.py b/Tests/test_features.py index 93d803fc133..ed6fd2234a7 100644 --- a/Tests/test_features.py +++ b/Tests/test_features.py @@ -21,6 +21,8 @@ def test_check() -> None: for codec in features.codecs: assert features.check_codec(codec) == features.check(codec) for feature in features.features: + if feature in features.deprecated_features: + continue assert features.check_feature(feature) == features.check(feature) @@ -47,9 +49,23 @@ def test(name: str, function: Callable[[str], str | None]) -> None: for codec in features.codecs: test(codec, features.version_codec) for feature in features.features: + if feature in features.deprecated_features: + continue test(feature, features.version_feature) +@pytest.mark.parametrize("feature", features.deprecated_features) +def test_deprecated_feature(feature: str) -> None: + match = f'features.check\\("{feature}"\\)' + with pytest.warns(DeprecationWarning, match=match): + assert features.check_feature(feature) == features.check_feature("raqm") + + with pytest.warns(DeprecationWarning, match=match): + assert features.version_feature(feature) is None + + assert feature not in features.get_supported_features() + + @skip_unless_feature("libjpeg_turbo") def test_libjpeg_turbo_version() -> None: version = features.version("libjpeg_turbo") diff --git a/checks/check_wheel.py b/checks/check_wheel.py index f716c8498bb..e9382051260 100644 --- a/checks/check_wheel.py +++ b/checks/check_wheel.py @@ -38,8 +38,6 @@ def test_wheel_codecs() -> None: def test_wheel_features() -> None: expected_features = { "raqm", - "fribidi", - "harfbuzz", "libjpeg_turbo", "zlib_ng", "xcb", @@ -47,9 +45,5 @@ def test_wheel_features() -> None: if sys.platform == "win32": expected_features.remove("xcb") - elif sys.platform == "ios": - # Can't distribute raqm due to licensing, and there's no system version; - # fribidi and harfbuzz won't be available if raqm isn't available. - expected_features -= {"raqm", "fribidi", "harfbuzz"} assert set(features.get_supported_features()) == expected_features diff --git a/depends/README.rst b/depends/README.rst index b69c9dcbf8f..3bb4b43f2fc 100644 --- a/depends/README.rst +++ b/depends/README.rst @@ -2,8 +2,9 @@ Depends ======= ``install_openjpeg.sh``, ``install_webp.sh``, ``install_imagequant.sh``, -``install_raqm.sh`` and ``install_raqm_cmake.sh`` can be used to download, -build & install non-packaged dependencies; useful for testing on CI. +``install_sheenbidi.sh``, ``install_raqm.sh`` and ``install_raqm_cmake.sh`` can +be used to download, build & install non-packaged dependencies; useful for +testing on CI. ``install_extra_test_images.sh`` can be used to install additional test images that are used by CI. diff --git a/depends/install_raqm.sh b/depends/install_raqm.sh index f2dcb8f5278..cb838a32947 100755 --- a/depends/install_raqm.sh +++ b/depends/install_raqm.sh @@ -8,6 +8,6 @@ archive=libraqm-0.11.0 pushd $archive -meson build --prefix=/usr && ninja -C build install +meson build --prefix=/usr -Dsheenbidi=true && ninja -C build install popd diff --git a/depends/install_sheenbidi.sh b/depends/install_sheenbidi.sh new file mode 100755 index 00000000000..19dc8b51fbf --- /dev/null +++ b/depends/install_sheenbidi.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# install sheenbidi + +version=3.0.0 + +archive=SheenBidi-$version + +./download-and-extract.sh $archive https://github.com/Tehreer/SheenBidi/archive/refs/tags/v$version.tar.gz + +pushd $archive + +meson setup build --prefix=/usr && ninja -C build install + +popd diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 70745104483..3f82c29f061 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -30,6 +30,15 @@ Image getdata() identical, except that it returns a tuple of pixel values, instead of an internal Pillow data type. +fribidi and harfbuzz features +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. deprecated:: 13.0.0 + +``fribidi`` and ``harfbuzz`` are deprecated aliases of ``raqm``. Raqm selects its +own bidirectional text and shaping libraries when it is built, so Pillow cannot +reliably report which ones are in use. + Removed features ---------------- diff --git a/docs/installation/basic-installation.rst b/docs/installation/basic-installation.rst index f66ee8707f1..ec5acd7183c 100644 --- a/docs/installation/basic-installation.rst +++ b/docs/installation/basic-installation.rst @@ -32,8 +32,7 @@ and :pypi:`olefile` for Pillow to read FPX and MIC images:: We provide binaries for Linux for each of the supported Python versions in the manylinux wheel format. These include support for all - optional libraries except libimagequant. Raqm support requires - FriBiDi to be installed separately:: + optional libraries except libimagequant:: python3 -m pip install --upgrade pip python3 -m pip install --upgrade Pillow @@ -47,8 +46,7 @@ and :pypi:`olefile` for Pillow to read FPX and MIC images:: We provide binaries for macOS for each of the supported Python versions in the wheel format. These include support for all optional - libraries except libimagequant. Raqm support requires - FriBiDi to be installed separately:: + libraries except libimagequant:: python3 -m pip install --upgrade pip python3 -m pip install --upgrade Pillow @@ -70,8 +68,7 @@ and :pypi:`olefile` for Pillow to read FPX and MIC images:: We provide Pillow binaries for Windows compiled for the matrix of supported Pythons in the wheel format. These include x86, x86-64 and arm64 versions. These binaries include support - for all optional libraries except libimagequant and libxcb. Raqm support - requires FriBiDi to be installed separately:: + for all optional libraries except libimagequant and libxcb:: python3 -m pip install --upgrade pip python3 -m pip install --upgrade Pillow diff --git a/docs/installation/building-from-source.rst b/docs/installation/building-from-source.rst index 437677c3446..cbea7390189 100644 --- a/docs/installation/building-from-source.rst +++ b/docs/installation/building-from-source.rst @@ -71,21 +71,13 @@ Many of Pillow's features require external libraries: * **libraqm** provides complex text layout support. - * libraqm provides bidirectional text support (using FriBiDi), + * libraqm provides bidirectional text support (using SheenBidi or FriBiDi), shaping (using HarfBuzz), and proper script itemization. As a result, Raqm can support most writing systems covered by Unicode. - * libraqm depends on the following libraries: FreeType, HarfBuzz, - FriBiDi, make sure that you install them before installing libraqm - if not available as package in your system. + * libraqm depends on the following libraries: FreeType, HarfBuzz, and either + SheenBidi or FriBiDi. Make sure that you install them before installing + libraqm if not available as package in your system. * Setting text direction or font features is not supported without libraqm. - * Pillow wheels since version 8.2.0 include a modified version of libraqm that - loads libfribidi at runtime if it is installed. - On Windows this requires compiling FriBiDi and installing ``fribidi.dll`` - into a directory listed in the `Dynamic-link library search order (Microsoft Learn) - `_ - (``fribidi-0.dll`` or ``libfribidi-0.dll`` are also detected). - See `Build Options`_ to see how to build this version. - * Previous versions of Pillow (5.0.0 to 8.1.2) linked libraqm dynamically at runtime. * **libxcb** provides X11 screengrab support. @@ -120,10 +112,10 @@ Many of Pillow's features require external libraries: sudo apt-get install libtiff5-dev libjpeg8-dev libopenjp2-7-dev zlib1g-dev \ libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk \ - libharfbuzz-dev libfribidi-dev libxcb1-dev + libharfbuzz-dev libxcb1-dev To install libraqm, ``sudo apt-get install meson`` and then see - ``depends/install_raqm.sh``. + ``depends/install_sheenbidi.sh`` and ``depends/install_raqm.sh``. Build prerequisites for libavif on Ubuntu are installed with:: @@ -135,7 +127,7 @@ Many of Pillow's features require external libraries: sudo dnf install libtiff-devel libjpeg-devel openjpeg2-devel zlib-devel \ freetype-devel lcms2-devel libwebp-devel tcl-devel tk-devel \ - harfbuzz-devel fribidi-devel libraqm-devel libimagequant-devel libxcb-devel + libraqm-devel libimagequant-devel libxcb-devel Note that the package manager may be yum or DNF, depending on the exact distribution. @@ -143,7 +135,7 @@ Many of Pillow's features require external libraries: Prerequisites are installed for **Alpine** with:: sudo apk add tiff-dev jpeg-dev openjpeg-dev zlib-dev freetype-dev lcms2-dev \ - libwebp-dev tcl-dev tk-dev harfbuzz-dev fribidi-dev libimagequant-dev \ + libwebp-dev tcl-dev tk-dev libraqm-dev libimagequant-dev \ libxcb-dev libpng-dev See also the ``Dockerfile``\s in the Test Infrastructure repo @@ -222,9 +214,7 @@ Many of Pillow's features require external libraries: Prerequisites are installed on **FreeBSD 10 or 11** with:: - sudo pkg install jpeg-turbo tiff webp lcms2 freetype2 openjpeg harfbuzz fribidi libxcb libavif - - Then see ``depends/install_raqm_cmake.sh`` to install libraqm. + sudo pkg install jpeg-turbo tiff webp lcms2 freetype2 openjpeg libraqm libxcb libavif .. tab:: Android @@ -297,12 +287,6 @@ Build options an exception if the libraries are not found. Tcl and Tk must be used together. -* Config settings: ``-C raqm=vendor``, ``-C fribidi=vendor``. - These flags are used to compile a modified version of libraqm and - a shim that dynamically loads libfribidi at runtime. These are - used to compile the standard Pillow wheels. Compiling libraqm requires - a C99-compliant compiler. - * Config setting: ``-C platform-guessing=disable``. Skips all of the platform dependent guessing of include and library directories for automated build systems that configure the proper paths in the diff --git a/docs/reference/features.rst b/docs/reference/features.rst index 45067ba3587..08b659ed5e3 100644 --- a/docs/reference/features.rst +++ b/docs/reference/features.rst @@ -61,6 +61,8 @@ Support for the following features can be checked: * ``libimagequant``: (compile time) ImageQuant quantization support in :py:func:`PIL.Image.Image.quantize`. Run-time version number is available. * ``xcb``: (compile time) Support for X11 in :py:func:`PIL.ImageGrab.grab` via the XCB library. +``fribidi`` and ``harfbuzz`` are deprecated aliases of ``raqm``. Raqm selects its own bidirectional text and shaping libraries when it is built, so Pillow cannot reliably report which ones are in use. + .. autofunction:: PIL.features.check_feature .. autofunction:: PIL.features.version_feature .. autofunction:: PIL.features.get_supported_features diff --git a/docs/releasenotes/13.0.0.rst b/docs/releasenotes/13.0.0.rst index 4db2f496701..b660096bec1 100644 --- a/docs/releasenotes/13.0.0.rst +++ b/docs/releasenotes/13.0.0.rst @@ -66,13 +66,29 @@ ImageCms.ImageCmsProfile.product_name and .product_info ``.product_info`` attributes have been removed. They were set to ``None`` since Pillow 2.3.0. +Raqm is no longer bundled +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Pillow used to compile a bundled copy of libraqm, together with a shim that loaded +libfribidi at runtime, because FriBiDi is LGPL-licensed and could not be linked into +Pillow's wheels. libraqm is now linked like any other dependency, so building Pillow +with text layout support requires libraqm to be installed. + Deprecations ============ -TODO -^^^^ +fribidi and harfbuzz features +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -TODO +``fribidi`` and ``harfbuzz`` are now deprecated aliases of ``raqm``. Raqm selects its +own bidirectional text and shaping libraries when it is built, so Pillow cannot +reliably report which ones are in use. + +raqm=vendor and fribidi=vendor config settings +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``-C raqm=vendor`` and ``-C fribidi=vendor`` build config settings are deprecated +and ignored. Raqm is no longer bundled, so there is nothing to vendor. API changes =========== @@ -97,6 +113,21 @@ that share a local name across different namespaces. Other changes ============= +Wheels no longer need FriBiDi at runtime +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Wheels bundle Raqm built against SheenBidi, which is Apache-2.0-licensed and so can +be shipped. Text layout works out of the box, where it previously required a system +copy of libfribidi that the bundled shim could load at import time. On Windows this +means ``fribidi.dll`` no longer has to be installed separately. + +iOS wheels now support Raqm +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Raqm was disabled in iOS wheels because FriBiDi could not be shipped and iOS cannot +load a system copy at runtime. iOS wheels now support text layout like every other +platform. + Python 3.15 ^^^^^^^^^^^ diff --git a/pyproject.toml b/pyproject.toml index 16d82d46711..dbf2670e9d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,7 +96,7 @@ dynamic.version = { attr = "PIL.__version__" } [tool.cibuildwheel] build-verbosity = 1 -config-settings = "raqm=enable raqm=vendor fribidi=vendor imagequant=disable" +config-settings = "raqm=enable imagequant=disable" before-all = ".github/workflows/wheels-dependencies.sh" test-command = "cd {project} && .github/workflows/wheels-test.sh" test-requires = [ @@ -104,12 +104,11 @@ test-requires = [ ] test-extras = "tests" # Disable platform guessing on macOS to avoid picking up Homebrew etc. -macos.config-settings = "raqm=enable raqm=vendor fribidi=vendor imagequant=disable platform-guessing=disable" +macos.config-settings = "raqm=enable imagequant=disable platform-guessing=disable" # Isolate macOS build environment from Homebrew etc. macos.environment.PATH = "$(pwd)/build/deps/darwin/bin:$(dirname $(which python3)):/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin" -# Disable platform guessing on iOS, and disable raqm (since there won't be a -# vendor version, and we can't distribute it due to licensing) -ios.config-settings = "raqm=disable imagequant=disable platform-guessing=disable" +# Disable platform guessing on iOS +ios.config-settings = "raqm=enable imagequant=disable platform-guessing=disable" # iOS needs to be given a specific pytest invocation and list of test sources. ios.test-command = [ "python -m selftest", diff --git a/setup.py b/setup.py index c49dbe96575..c2332b7422f 100644 --- a/setup.py +++ b/setup.py @@ -46,8 +46,6 @@ def get_version() -> str: PILLOW_VERSION = get_version() AVIF_ROOT = None FREETYPE_ROOT = None -HARFBUZZ_ROOT = None -FRIBIDI_ROOT = None IMAGEQUANT_ROOT = None JPEG2K_ROOT = None JPEG_ROOT = None @@ -254,20 +252,6 @@ def _find_library_file(self: pil_build_ext, library: str) -> str | None: return ret -def _find_include_dir(self: pil_build_ext, dirname: str, include: str) -> bool | str: - for directory in self.compiler.include_dirs: - _dbg("Checking for include file %s in %s", (include, directory)) - if os.path.isfile(os.path.join(directory, include)): - _dbg("Found %s in %s", (include, directory)) - return True - subdir = os.path.join(directory, dirname) - _dbg("Checking for include file %s in %s", (include, subdir)) - if os.path.isfile(os.path.join(subdir, include)): - _dbg("Found %s in %s", (include, subdir)) - return subdir - return False - - def _cmd_exists(cmd: str) -> bool: if "PATH" not in os.environ: return False @@ -324,7 +308,6 @@ class ext_feature: ] required = {"jpeg", "zlib"} - vendor: set[str] = set() def __init__(self) -> None: self._settings: dict[str, str | bool | None] = {} @@ -343,9 +326,6 @@ def set(self, feat: str, value: str | bool | None) -> None: def want(self, feat: str) -> bool: return self._settings[feat] is None - def want_vendor(self, feat: str) -> bool: - return feat in self.vendor - def __iter__(self) -> Iterator[str]: yield from self.features @@ -355,10 +335,7 @@ def __iter__(self) -> Iterator[str]: build_ext.user_options + [(f"disable-{x}", None, f"Disable support for {x}") for x in feature] + [(f"enable-{x}", None, f"Enable support for {x}") for x in feature] - + [ - (f"vendor-{x}", None, f"Use vendored version of {x}") - for x in ("raqm", "fribidi") - ] + + [(f"vendor-{x}", None, "Deprecated, ignored") for x in ("raqm", "fribidi")] + [ ("disable-platform-guessing", None, "Disable platform guessing"), ("debug", None, "Debug logging"), @@ -425,16 +402,11 @@ def finalize_options(self) -> None: self.feature.required.add("freetype") for x in ("raqm", "fribidi"): if getattr(self, f"vendor_{x}"): - if getattr(self, "disable_raqm"): - msg = f"Conflicting options: '-C {x}=vendor' and '-C raqm=disable'" - raise ValueError(msg) - if x == "fribidi" and not getattr(self, "vendor_raqm"): - msg = ( - f"Conflicting options: '-C {x}=vendor' and not '-C raqm=vendor'" - ) - raise ValueError(msg) - _dbg("Using vendored version of %s", x) - self.feature.vendor.add(x) + warnings.warn( + f"'-C {x}=vendor' is deprecated and ignored. " + "Raqm is no longer bundled.", + DeprecationWarning, + ) def _update_extension( self, @@ -512,8 +484,6 @@ def build_extensions(self) -> None: "TIFF_ROOT": ("libtiff-5", "libtiff-4"), "ZLIB_ROOT": "zlib", "FREETYPE_ROOT": "freetype2", - "HARFBUZZ_ROOT": "harfbuzz", - "FRIBIDI_ROOT": "fribidi", "RAQM_ROOT": "raqm", "WEBP_ROOT": "libwebp", "LCMS_ROOT": "lcms2", @@ -815,37 +785,12 @@ def build_extensions(self) -> None: _add_directory(self.compiler.include_dirs, subdir, 0) if feature.get("freetype") and feature.want("raqm"): - if not feature.want_vendor("raqm"): # want system Raqm - _dbg("Looking for Raqm") - if _find_include_file(self, "raqm.h"): - if _find_library_file(self, "raqm"): - feature.set("raqm", "raqm") - elif _find_library_file(self, "libraqm"): - feature.set("raqm", "libraqm") - else: # want to build Raqm from src/thirdparty - _dbg("Looking for HarfBuzz") - feature.set("harfbuzz", None) - hb_dir = _find_include_dir(self, "harfbuzz", "hb.h") - if hb_dir: - if isinstance(hb_dir, str): - _add_directory(self.compiler.include_dirs, hb_dir, 0) - if _find_library_file(self, "harfbuzz"): - feature.set("harfbuzz", "harfbuzz") - if feature.get("harfbuzz"): - if not feature.want_vendor("fribidi"): # want system FriBiDi - _dbg("Looking for FriBiDi") - feature.set("fribidi", None) - fribidi_dir = _find_include_dir(self, "fribidi", "fribidi.h") - if fribidi_dir: - if isinstance(fribidi_dir, str): - _add_directory( - self.compiler.include_dirs, fribidi_dir, 0 - ) - if _find_library_file(self, "fribidi"): - feature.set("fribidi", "fribidi") - feature.set("raqm", True) - else: # want to build FriBiDi shim from src/thirdparty - feature.set("raqm", True) + _dbg("Looking for Raqm") + if _find_include_file(self, "raqm.h"): + if _find_library_file(self, "raqm"): + feature.set("raqm", "raqm") + elif _find_library_file(self, "libraqm"): + feature.set("raqm", "libraqm") if feature.want("lcms"): _dbg("Looking for lcms") @@ -943,29 +888,24 @@ def build_extensions(self) -> None: # additional libraries if feature.get("freetype"): - srcs = [] libs = ["freetype"] defs = [] if feature.get("raqm"): - if not feature.want_vendor("raqm"): # using system Raqm - defs.append(("HAVE_RAQM", None)) - defs.append(("HAVE_RAQM_SYSTEM", None)) - libs.append(feature.get("raqm")) - else: # building Raqm from src/thirdparty - defs.append(("HAVE_RAQM", None)) - srcs.append("src/thirdparty/raqm/raqm.c") - libs.append(feature.get("harfbuzz")) - if not feature.want_vendor("fribidi"): # using system FriBiDi - defs.append(("HAVE_FRIBIDI_SYSTEM", None)) - libs.append(feature.get("fribidi")) - else: # building FriBiDi shim from src/thirdparty - srcs.append("src/thirdparty/fribidi-shim/fribidi.c") + defs.append(("HAVE_RAQM", None)) + libs.append(feature.get("raqm")) + + if ( + feature.get("raqm") + and sys.platform in ["ios", "win32"] + and _find_library_file(self, "SheenBidi") + ): + libs.extend(["harfbuzz", "SheenBidi"]) if sys.platform == "ios": # Ensure transitive dependencies are linked. libs.extend(["z", "bz2", "brotlicommon", "brotlidec", "png"]) - self._update_extension("PIL._imagingft", libs, defs, srcs) + self._update_extension("PIL._imagingft", libs, defs) else: self._remove_extension("PIL._imagingft") @@ -1017,12 +957,6 @@ def summary_report(self, feature: ext_feature) -> None: print(f" [{v.strip()}") print("-" * 68) - raqm_extra_info = "" - if feature.want_vendor("raqm"): - raqm_extra_info += "bundled" - if feature.want_vendor("fribidi"): - raqm_extra_info += ", FriBiDi shim" - options = [ (feature.get("jpeg"), "JPEG"), ( @@ -1034,7 +968,7 @@ def summary_report(self, feature: ext_feature) -> None: (feature.get("imagequant"), "LIBIMAGEQUANT"), (feature.get("tiff"), "LIBTIFF"), (feature.get("freetype"), "FREETYPE2"), - (feature.get("raqm"), "RAQM (Text shaping)", raqm_extra_info), + (feature.get("raqm"), "RAQM (Text shaping)"), (feature.get("lcms"), "LITTLECMS2"), (feature.get("webp"), "WEBP"), (feature.get("xcb"), "XCB (X protocol)"), diff --git a/src/PIL/features.py b/src/PIL/features.py index 8d72d92cb05..a5164ca8ac9 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -8,9 +8,11 @@ import PIL from . import Image +from ._deprecate import deprecate TYPE_CHECKING = False if TYPE_CHECKING: + from types import FrameType from typing import IO modules = { "pil": ("PIL._imaging", "PILLOW_VERSION"), @@ -123,8 +125,8 @@ def get_supported_codecs() -> list[str]: features: dict[str, tuple[str, str, str | None]] = { "raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"), - "fribidi": ("PIL._imagingft", "HAVE_FRIBIDI", "fribidi_version"), - "harfbuzz": ("PIL._imagingft", "HAVE_HARFBUZZ", "harfbuzz_version"), + "fribidi": ("PIL._imagingft", "HAVE_RAQM", None), + "harfbuzz": ("PIL._imagingft", "HAVE_RAQM", None), "libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO", "libjpeg_turbo_version"), "mozjpeg": ("PIL._imaging", "HAVE_MOZJPEG", "libjpeg_turbo_version"), "zlib_ng": ("PIL._imaging", "HAVE_ZLIBNG", "zlib_ng_version"), @@ -132,15 +134,33 @@ def get_supported_codecs() -> list[str]: "xcb": ("PIL._imaging", "HAVE_XCB", None), } +# Raqm chooses its own bidi and shaping libraries, so Pillow cannot reliably +# report them. +deprecated_features = {"fribidi": "raqm", "harfbuzz": "raqm"} + + +def _deprecate_feature(feature: str) -> None: + if feature not in deprecated_features: + return + + # Report against the first frame outside this module, so that the warning + # points at the caller whichever entry point it used + stacklevel = 2 + frame: FrameType | None = sys._getframe() + while frame is not None and frame.f_globals.get("__name__") == __name__: + stacklevel += 1 + frame = frame.f_back + + replacement = deprecated_features[feature] + deprecate( + f'features.check("{feature}")', + None, + f'check("{replacement}")', + stacklevel=stacklevel, + ) -def check_feature(feature: str) -> bool | None: - """ - Checks if a feature is available. - :param feature: The feature to check for. - :returns: ``True`` if available, ``False`` if unavailable, ``None`` if unknown. - :raises ValueError: If the feature is not defined in this version of Pillow. - """ +def _feature_available(feature: str) -> bool | None: if feature not in features: msg = f"Unknown feature {feature}" raise ValueError(msg) @@ -157,13 +177,28 @@ def check_feature(feature: str) -> bool | None: return None +def check_feature(feature: str) -> bool | None: + """ + Checks if a feature is available. + + :param feature: The feature to check for. + :returns: ``True`` if available, ``False`` if unavailable, ``None`` if unknown. + :raises ValueError: If the feature is not defined in this version of Pillow. + """ + available = _feature_available(feature) + _deprecate_feature(feature) + return available + + def version_feature(feature: str) -> str | None: """ :param feature: The feature to check for. :returns: The version number as a string, or ``None`` if not available. :raises ValueError: If the feature is not defined in this version of Pillow. """ - if not check_feature(feature): + available = _feature_available(feature) + _deprecate_feature(feature) + if not available: return None module, flag, ver = features[feature] @@ -178,7 +213,9 @@ def get_supported_features() -> list[str]: """ :returns: A list of all supported features. """ - return [f for f in features if check_feature(f)] + return [ + f for f in features if f not in deprecated_features and _feature_available(f) + ] def check(feature: str) -> bool | None: @@ -301,11 +338,6 @@ def pilinfo(out: IO[str] | None = None, supported_formats: bool = True) -> None: zlib_ng_version = version_feature("zlib_ng") if zlib_ng_version is not None: v += ", compiled for zlib-ng " + zlib_ng_version - elif name == "raqm": - for f in ("fribidi", "harfbuzz"): - v2 = version_feature(f) - if v2 is not None: - v += f", {f} {v2}" print("---", feature, "support ok,", t, v, file=out) else: print("---", feature, "support ok", file=out) diff --git a/src/_imagingft.c b/src/_imagingft.c index dd795ed2e66..6664fe3908e 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -48,17 +48,7 @@ ; #ifdef HAVE_RAQM -#ifdef HAVE_RAQM_SYSTEM #include -#else -#include "thirdparty/raqm/raqm.h" -#ifdef HAVE_FRIBIDI_SYSTEM -#include -#else -#include "thirdparty/fribidi-shim/fribidi.h" -#include -#endif -#endif #endif static int have_raqm = 0; @@ -1665,21 +1655,11 @@ setup_module(PyObject *m) { Py_DECREF(v); #ifdef HAVE_RAQM -#if defined(HAVE_RAQM_SYSTEM) || defined(HAVE_FRIBIDI_SYSTEM) have_raqm = 1; -#else - load_fribidi(); - have_raqm = !!p_fribidi; -#endif -#else - have_raqm = 0; #endif - /* if we have Raqm, we have all three (but possibly no version info) */ v = PyBool_FromLong(have_raqm); PyDict_SetItemString(d, "HAVE_RAQM", v); - PyDict_SetItemString(d, "HAVE_FRIBIDI", v); - PyDict_SetItemString(d, "HAVE_HARFBUZZ", v); Py_DECREF(v); if (have_raqm) { v = NULL; @@ -1691,32 +1671,6 @@ setup_module(PyObject *m) { #endif PyDict_SetItemString(d, "raqm_version", v ? v : Py_None); Py_XDECREF(v); - - v = NULL; -#ifdef FRIBIDI_MAJOR_VERSION - { - const char *a = strchr(fribidi_version_info, ')'); - const char *b = strchr(fribidi_version_info, '\n'); - if (a && b && a + 2 < b) { - v = PyUnicode_FromStringAndSize(a + 2, b - (a + 2)); - if (!v) { - return -1; - } - } - } -#endif - PyDict_SetItemString(d, "fribidi_version", v ? v : Py_None); - Py_XDECREF(v); - - v = NULL; -#ifdef HB_VERSION_STRING - v = PyUnicode_FromString(hb_version_string()); - if (!v) { - return -1; - } -#endif - PyDict_SetItemString(d, "harfbuzz_version", v ? v : Py_None); - Py_XDECREF(v); } return 0; diff --git a/src/thirdparty/fribidi-shim/fribidi.c b/src/thirdparty/fribidi-shim/fribidi.c deleted file mode 100644 index 7b1a8f8c51e..00000000000 --- a/src/thirdparty/fribidi-shim/fribidi.c +++ /dev/null @@ -1,109 +0,0 @@ - -#ifndef _WIN32 -#include -#else -#define WIN32_LEAN_AND_MEAN -#include -#endif - -#define FRIBIDI_SHIM_IMPLEMENTATION -#include "fribidi.h" - - -/* FriBiDi>=1.0.0 adds bracket_types param, ignore and call legacy function */ -static FriBidiLevel fribidi_get_par_embedding_levels_ex_compat( - const FriBidiCharType *bidi_types, - const FriBidiBracketType *bracket_types, - const FriBidiStrIndex len, - FriBidiParType *pbase_dir, - FriBidiLevel *embedding_levels) -{ - return fribidi_get_par_embedding_levels( - bidi_types, len, pbase_dir, embedding_levels); -} - -/* FriBiDi>=1.0.0 gets bracket types here, ignore */ -static void fribidi_get_bracket_types_compat( - const FriBidiChar *str, - const FriBidiStrIndex len, - const FriBidiCharType *types, - FriBidiBracketType *btypes) -{ /* no-op*/ } - - -int load_fribidi(void) { - int error = 0; - const char **p_fribidi_version_info = 0; - - p_fribidi = 0; - - /* Microsoft needs a totally different system */ -#ifndef _WIN32 -#define LOAD_FUNCTION(func) \ - func = (t_##func)dlsym(p_fribidi, #func); \ - error = error || (func == 0); - - p_fribidi = dlopen("libfribidi.so", RTLD_LAZY); - if (!p_fribidi) { - p_fribidi = dlopen("libfribidi.so.0", RTLD_LAZY); - } - if (!p_fribidi) { - p_fribidi = dlopen("libfribidi.dylib", RTLD_LAZY); - } - if (!p_fribidi) { - p_fribidi = dlopen("/usr/local/lib/libfribidi.dylib", RTLD_LAZY); - } -#else -#define LOAD_FUNCTION(func) \ - func = (t_##func)GetProcAddress(p_fribidi, #func); \ - error = error || (func == 0); - - p_fribidi = LoadLibrary("fribidi"); - if (!p_fribidi) { - p_fribidi = LoadLibrary("fribidi-0"); - } - /* MSYS2 */ - if (!p_fribidi) { - p_fribidi = LoadLibrary("libfribidi-0"); - } -#endif - - if (!p_fribidi) { - return 1; - } - - /* load FriBiDi>=1.0.0 functions first, use error to detect version */ - LOAD_FUNCTION(fribidi_get_par_embedding_levels_ex); - LOAD_FUNCTION(fribidi_get_bracket_types); - if (error) { - /* using FriBiDi<1.0.0, ignore new parameters */ - error = 0; - fribidi_get_par_embedding_levels_ex = &fribidi_get_par_embedding_levels_ex_compat; - fribidi_get_bracket_types = &fribidi_get_bracket_types_compat; - } - - LOAD_FUNCTION(fribidi_unicode_to_charset); - LOAD_FUNCTION(fribidi_charset_to_unicode); - LOAD_FUNCTION(fribidi_get_bidi_types); - LOAD_FUNCTION(fribidi_get_par_direction); - LOAD_FUNCTION(fribidi_get_par_embedding_levels); - -#ifndef _WIN32 - p_fribidi_version_info = (const char**)dlsym(p_fribidi, "fribidi_version_info"); - if (error || (p_fribidi_version_info == 0) || (*p_fribidi_version_info == 0)) { - dlclose(p_fribidi); - p_fribidi = 0; - return 2; - } -#else - p_fribidi_version_info = (const char**)GetProcAddress(p_fribidi, "fribidi_version_info"); - if (error || (p_fribidi_version_info == 0) || (*p_fribidi_version_info == 0)) { - FreeLibrary(p_fribidi); - p_fribidi = 0; - return 2; - } -#endif - fribidi_version_info = *p_fribidi_version_info; - - return 0; -} diff --git a/src/thirdparty/fribidi-shim/fribidi.h b/src/thirdparty/fribidi-shim/fribidi.h deleted file mode 100644 index bbd3c23b98e..00000000000 --- a/src/thirdparty/fribidi-shim/fribidi.h +++ /dev/null @@ -1,118 +0,0 @@ - -#define FRIBIDI_MAJOR_VERSION 1 - -/* fribidi-types.h */ - -# if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \ - defined (_sgi) || defined (__sun) || defined (sun) || \ - defined (__digital__) || defined (__HP_cc) -# include -# elif defined (_AIX) -# include -# else -# include -# endif - -typedef uint32_t FriBidiChar; -typedef int FriBidiStrIndex; - -typedef FriBidiChar FriBidiBracketType; - - - -/* fribidi-char-sets.h */ - -typedef enum -{ - _FRIBIDI_CHAR_SET_NOT_FOUND, - FRIBIDI_CHAR_SET_UTF8, - FRIBIDI_CHAR_SET_CAP_RTL, - FRIBIDI_CHAR_SET_ISO8859_6, - FRIBIDI_CHAR_SET_ISO8859_8, - FRIBIDI_CHAR_SET_CP1255, - FRIBIDI_CHAR_SET_CP1256, - _FRIBIDI_CHAR_SETS_NUM_PLUS_ONE -} -FriBidiCharSet; - - - -/* fribidi-bidi-types.h */ - -typedef signed char FriBidiLevel; - -#define FRIBIDI_TYPE_LTR_VAL 0x00000110L -#define FRIBIDI_TYPE_RTL_VAL 0x00000111L -#define FRIBIDI_TYPE_ON_VAL 0x00000040L - -typedef uint32_t FriBidiCharType; -#define FRIBIDI_TYPE_LTR FRIBIDI_TYPE_LTR_VAL - -typedef uint32_t FriBidiParType; -#define FRIBIDI_PAR_LTR FRIBIDI_TYPE_LTR_VAL -#define FRIBIDI_PAR_RTL FRIBIDI_TYPE_RTL_VAL -#define FRIBIDI_PAR_ON FRIBIDI_TYPE_ON_VAL - -#define FRIBIDI_LEVEL_IS_RTL(lev) ((lev) & 1) -#define FRIBIDI_DIR_TO_LEVEL(dir) ((FriBidiLevel) (FRIBIDI_IS_RTL(dir) ? 1 : 0)) -#define FRIBIDI_IS_RTL(p) ((p) & 0x00000001L) -#define FRIBIDI_IS_EXPLICIT_OR_BN_OR_WS(p) ((p) & 0x00901000L) - - - -/* functions */ - -#ifdef FRIBIDI_SHIM_IMPLEMENTATION -#ifdef _MSC_VER -#define FRIBIDI_ENTRY -#else -#define FRIBIDI_ENTRY __attribute__((visibility ("hidden"))) -#endif -#else -#define FRIBIDI_ENTRY extern -#endif - -#define FRIBIDI_FUNC(ret, name, ...) \ - typedef ret (*t_##name) (__VA_ARGS__); \ - FRIBIDI_ENTRY t_##name name; - -FRIBIDI_FUNC(FriBidiStrIndex, fribidi_unicode_to_charset, - FriBidiCharSet, const FriBidiChar *, FriBidiStrIndex, char *); - -FRIBIDI_FUNC(FriBidiStrIndex, fribidi_charset_to_unicode, - FriBidiCharSet, const char *, FriBidiStrIndex, FriBidiChar *); - -FRIBIDI_FUNC(void, fribidi_get_bidi_types, - const FriBidiChar *, const FriBidiStrIndex, FriBidiCharType *); - -FRIBIDI_FUNC(FriBidiParType, fribidi_get_par_direction, - const FriBidiCharType *, const FriBidiStrIndex); - -FRIBIDI_FUNC(FriBidiLevel, fribidi_get_par_embedding_levels, - const FriBidiCharType *, const FriBidiStrIndex, FriBidiParType *, - FriBidiLevel *); - -/* FriBiDi>=1.0.0 */ -FRIBIDI_FUNC(FriBidiLevel, fribidi_get_par_embedding_levels_ex, - const FriBidiCharType *, const FriBidiBracketType *, const FriBidiStrIndex, - FriBidiParType *, FriBidiLevel *); - -/* FriBiDi>=1.0.0 */ -FRIBIDI_FUNC(void, fribidi_get_bracket_types, - const FriBidiChar *, const FriBidiStrIndex, const FriBidiCharType *, - FriBidiBracketType *); - -#undef FRIBIDI_FUNC - -/* constant, not a function */ -FRIBIDI_ENTRY const char *fribidi_version_info; - - - -/* shim */ - -FRIBIDI_ENTRY void *p_fribidi; - -FRIBIDI_ENTRY int load_fribidi(void); - -#undef FRIBIDI_ENTRY diff --git a/src/thirdparty/raqm/AUTHORS b/src/thirdparty/raqm/AUTHORS deleted file mode 100644 index bd5c3ac6b6a..00000000000 --- a/src/thirdparty/raqm/AUTHORS +++ /dev/null @@ -1,9 +0,0 @@ -Abderraouf Adjal -Ali Yousuf -Anood Almuharbi -Asma Albahanta -Fahad Alsaidi -Ibtisam Almabsali -Khaled Hosny -Mazoon Almaamari -Shamsa Alqassabi diff --git a/src/thirdparty/raqm/COPYING b/src/thirdparty/raqm/COPYING deleted file mode 100644 index 99468479637..00000000000 --- a/src/thirdparty/raqm/COPYING +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright © 2015 Information Technology Authority (ITA) -Copyright © 2016-2026 Khaled Hosny - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/src/thirdparty/raqm/NEWS b/src/thirdparty/raqm/NEWS deleted file mode 100644 index 8be10bf718c..00000000000 --- a/src/thirdparty/raqm/NEWS +++ /dev/null @@ -1,204 +0,0 @@ -Overview of changes leading to 0.11.0 -Thursday, July 23, 2026 -==================================== - -Update grapheme cluster boundary detection to Unicode 17, conforming to -Unicode UAX #29 and verified against the Unicode conformance test. - -Reject input that spans multiple paragraphs; raqm_layout() now fails when -the text contains a paragraph separator. - -Fix out-of-bounds read in the UTF-8 and UTF-16 decoders. - -Build, CI and documentation updates. - -New API: - * raqm_get_par_detected_direction - * raqm_allowed_grapheme_boundary - -Overview of changes leading to 0.10.5 -Saturday, April 11, 2026 -==================================== - -Check for NULL return from malloc in couple of places. - -Overview of changes leading to 0.10.4 -Thursday, February 5, 2026 -==================================== - -Add build option to skip tests. - -Add dependency override for use as a subproject. - -Fix tests when b_ndebug=true. - -Build, CI and documentation updates. - -Overview of changes leading to 0.10.3 -Tuesday, August 5, 2025 -==================================== - -Fix raqm_set_text_utf8/utf16 reading beyond len for multibyte. - -Support building against SheenBidi 2.9. - -Fix deprecation warning with latest HarfBuzz. - -Overview of changes leading to 0.10.2 -Sunday, September 22, 2024 -==================================== - -Fix Unicode codepoint conversion from UTF-16. - -Overview of changes leading to 0.10.1 -Wednesday, April 12, 2023 -==================================== - -Make combining marks always inherit the script of their base. - -Overview of changes leading to 0.10.0 -Wednesday, January 11, 2023 -==================================== - -Fix font feature ranges. - -Fix resolved direction for all-neutral text. - -Implement letter and word spacing support. - -New API: - * raqm_set_text_utf16 - -Overview of changes leading to 0.9.0 -Sunday, January 30, 2022 -==================================== - -Raise the minimum versions of Raqm dependencies: no longer conditionally -enabling any features based on specific dependency version. - -raqm_t objects can now be reused by calling raqm_clear_contents() before -re-use, to potentially reduce the number memory allocations. - -Don't hardcode python3 in tests. - -New API: - * raqm_set_freetype_load_flags_range - * raqm_clear_contents - -Overview of changes leading to 0.8.0 -Monday, December 13, 2021 -==================================== - -Remove autotools build. - -Support using SheenBiDi instead of FriBiDi for Unicode BiDi support. - -Fix running tests with Python <= 3.6. - -New API: - * raqm_get_par_resolved_direction - * raqm_get_direction_at_index - -Overview of changes leading to 0.7.2 -Monday, September 27, 2021 -==================================== - -Fix test failure with newer HarfBuzz versions. - -Apply FT_Face transformation matrix when built against FreeType 2.11 or later. - -Add meson build system. Autotools build system will be dropped in next release. - -Improve MSVC support. - -Build and documentation fixes. - -Overview of changes leading to 0.7.1 -Sunday, November 22, 2020 -==================================== - -Require HarfBuzz >= 2.0.0 - -Build and documentation fixes. - -Overview of changes leading to 0.7.0 -Monday, May 27, 2019 -==================================== - -New API: - * raqm_version - * raqm_version_string - * raqm_version_atleast - * RAQM_VERSION_MAJOR - * RAQM_VERSION_MICRO - * RAQM_VERSION_MINOR - * RAQM_VERSION_STRING - * RAQM_VERSION_ATLEAST - -Overview of changes leading to 0.6.0 -Sunday, May 5, 2019 -==================================== - -Fix TTB direction regression from the previous release. - -Correctly detect script of Common and Inherite characters at start of text. - -Undef HAVE_CONFIG_H workaround, for older versions of Fribidi. - -Drop test suite dependency on GLib. - -Port test runner to Python instead of shell script. - -New API: -* raqm_set_invisible_glyph() - -Overview of changes leading to 0.5.0 -Saturday, February 24, 2018 -==================================== - -Use FriBiDi 1.x API when available. - -Overview of changes leading to 0.4.0 -Sunday, January 21, 2018 -==================================== - -Set begin-of-text and end-of-text HarfBuzz buffer flags. - -Dynamically allocate memory instead of using stack allocation for input text. - -Accept zero length text and do nothing instead of treating it as error. - -Overview of changes leading to 0.3.0 -Monday, August 21, 2017 -==================================== - -Fix stack corruption on MSVC. - -New API: -* raqm_set_freetype_load_flags - -Overview of changes leading to 0.2.0 -Wednesday, August 25, 2016 -==================================== - -Fix building with MSVC due to lacking C99 support. - -Make multiple fonts support actually work. Start and length now respect the -input encoding. - -New API: -* raqm_index_to_position -* raqm_position_to_index -* raqm_set_language - -Overview of changes leading to 0.1.1 -Sunday, May 1, 2016 -==================================== - -Fix make check on 32-bit systems. - -Overview of changes leading to 0.1.0 -Wednesday, January 20, 2016 -==================================== - -First release. diff --git a/src/thirdparty/raqm/README.md b/src/thirdparty/raqm/README.md deleted file mode 100644 index a24c73c57c3..00000000000 --- a/src/thirdparty/raqm/README.md +++ /dev/null @@ -1,78 +0,0 @@ -Raqm -==== - -[![Build](https://github.com/HOST-Oman/libraqm/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/HOST-Oman/libraqm/actions) - -Raqm is a small library that encapsulates the logic for complex text layout and -provides a convenient API. - -It currently provides bidirectional text support (using [FriBiDi][1] or -[SheenBidi][2]), shaping (using [HarfBuzz][3]), and proper script itemization. -As a result, Raqm can support most writing systems covered by Unicode. - -The documentation can be accessed on the web at: -> https://host-oman.github.io/libraqm/ - -Raqm (Arabic: رَقْم) is writing, also number or digit and the Arabic word for -digital (رَقَمِيّ) shares the same root, so it is a play on “digital writing”. - -Building --------- - -Raqm depends on the following libraries: -* [FreeType][4] -* [HarfBuzz][3] -* [FriBiDi][1] or [SheenBidi][2] - -To build the documentation you will also need: -* [GTK-Doc][5] - -To install dependencies on Fedora: - - sudo dnf install freetype-devel harfbuzz-devel fribidi-devel meson gtk-doc - -To install dependencies on Ubuntu: - - sudo apt-get install libfreetype6-dev libharfbuzz-dev libfribidi-dev meson gtk-doc-tools - -On Mac OS X you can use Homebrew: - - brew install freetype harfbuzz fribidi meson gtk-doc - export XML_CATALOG_FILES="/usr/local/etc/xml/catalog" # for the docs - -Once you have the source code and the dependencies, you can proceed to build. -To do that, run the customary sequence of commands in the source code -directory: - - $ meson build - $ ninja -C build - $ ninja -C build install - -To build the documentation, pass `-Ddocs=true` to the `meson`. - -To run the tests: - - $ ninja -C build test - -Contributing ------------- - -Once you have made a change that you are happy with, contribute it back, we’ll -be happy to integrate it! Just fork the repository and make a pull request. - -Projects using Raqm -------------------- - -1. [ImageMagick](https://github.com/ImageMagick/ImageMagick) -2. [LibGD](https://github.com/libgd/libgd) -3. [FontView](https://github.com/googlei18n/fontview) -4. [Pillow](https://github.com/python-pillow) -5. [mplcairo](https://github.com/anntzer/mplcairo) -6. [CEGUI](https://github.com/cegui/cegui) - - -[1]: https://github.com/fribidi/fribidi -[2]: https://github.com/Tehreer/SheenBidi -[3]: https://github.com/harfbuzz/harfbuzz -[4]: https://freetype.org/ -[5]: https://www.gtk.org/gtk-doc diff --git a/src/thirdparty/raqm/grapheme-data.h b/src/thirdparty/raqm/grapheme-data.h deleted file mode 100644 index 10be59f01cd..00000000000 --- a/src/thirdparty/raqm/grapheme-data.h +++ /dev/null @@ -1,398 +0,0 @@ -/* Generated by scripts/gen-grapheme-data.py from Unicode 17.0.0 */ -/* DO NOT EDIT MANUALLY */ - -#ifndef _RAQM_GRAPHEME_DATA_H_ -#define _RAQM_GRAPHEME_DATA_H_ - -typedef enum -{ - RAQM_GRAPHEME_OTHER, - RAQM_GRAPHEME_CR, - RAQM_GRAPHEME_LF, - RAQM_GRAPHEME_CONTROL, - RAQM_GRAPHEME_EXTEND, - RAQM_GRAPHEME_ZWJ, - RAQM_GRAPHEME_REGIONAL_INDICATOR, - RAQM_GRAPHEME_PREPEND, - RAQM_GRAPHEME_SPACING_MARK, - RAQM_GRAPHEME_L, - RAQM_GRAPHEME_V, - RAQM_GRAPHEME_T, - RAQM_GRAPHEME_LV, - RAQM_GRAPHEME_LVT, - RAQM_GRAPHEME_EXTENDED_PICTOGRAPHIC -} _raqm_grapheme_t; - -typedef enum -{ - RAQM_INCB_NONE, - RAQM_INCB_CONSONANT, - RAQM_INCB_LINKER, - RAQM_INCB_EXTEND -} _raqm_incb_t; - -#include - -static const uint8_t _raqm_u8[4446]= -{ - 33, 0, 48, 84,118,128,144,186, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 0, 19, 20, 21, 22, 0, 23, 24, 25, 26, 27, 28, 29, - 0, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 33, 34, 2, - 3, 4, 5, 6, 7, 8, 2, 3, 4, 5, 6, 7, 8, 2, 3, 4, - 5, 6, 7, 8, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 36, 0, 37, 38, 39, 0, 0, 0, 40, 41, - 42, 43, 44, 45, 46, 47, 48, 49, 50, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 51, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 53, 54, - 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, - 57, 58, 59, 0, 0, 0, 60, 0, 0, 61, 62, 63, 64, 65, 0, 0, - 0, 66, 67, 68, 69, 70, 71, 72, 73, 74, 1, 1, 1, 1, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, - 1, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, - 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, - 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, - 8, 9, 3, 4, 5, 6, 7, 8, 9, 18, 11, 19, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 10, 20, 0, 0, 0, 0, 21, 0, 0, 0, 22, - 23, 24, 25, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, - 38, 12, 39, 12, 40, 41, 42, 43, 44, 45, 46, 47, 48, 13, 49, 50, - 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 0, 63, 64, 65, - 66, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 68, 69, 70, - 71, 72, 0, 73, 0, 74, 0, 0, 0, 75, 76, 77, 78, 79, 80, 81, - 82, 83, 0, 0, 84, 0, 0, 0, 10, 85, 86, 0, 87, 88, 0, 89, - 0, 0, 0, 0, 0, 90, 0, 0, 91, 0, 0, 0, 92, 0, 0, 93, - 94, 95, 96, 97, 98, 99,100,101, 0, 0, 0, 0, 0,102, 0, 0, - 0, 0, 0, 0, 0,103,104, 0, 0, 0, 0, 0,105, 0,106, 0, - 107,108, 0,109, 0, 0, 0, 0, 0, 0, 0,110, 0, 0, 0, 0, - 0, 0,111, 14,112, 0, 0, 0, 0,113, 0,114,115,116,117,118, - 119,120,121,122,123, 0, 0, 0,124, 3, 4, 5, 6, 7, 8,125, - 126, 0, 0, 0, 0,127, 0, 0, 0,128, 0, 0, 11, 0, 0, 14, - 129, 0, 0, 0, 0, 0, 0, 0,130, 0, 0, 0,131, 0,132, 0, - 0,133, 0, 0,134, 0, 0, 0, 0, 0, 0, 0, 0,135,136, 0, - 0, 0, 0,137,138, 0,139,140, 0,141,142,143,144,145,146,147, - 148,149,150, 0,151, 13,152,153,154,155,156,157,158, 0, 0,159, - 160,161,162,163, 0,164, 0, 0, 0,165, 0, 0, 0,166,167, 0, - 168,169,170,171, 0, 0,172, 0, 0,173, 0,174, 0,175,176,177, - 0, 0, 0, 0,178,179,180, 0, 0,181,182, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,183, 0, 0, 0, 0, 0, 0,184, 15, 0, 0, - 0, 0, 0, 0, 0, 0,185, 0, 0, 0, 0, 0, 0, 0,186,187, - 188, 0, 0,189, 0, 0, 0, 0, 0, 0, 0, 0, 0,190,191, 0, - 0, 0, 0, 0, 0, 0,192,193, 0, 0,194, 0, 0, 0, 0, 0, - 0,195,196,197, 0, 0, 0, 0, 0,198, 0,199, 0, 15, 0, 0, - 0, 0, 0,200, 16, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, - 201, 0, 0, 0,202, 0, 0, 0, 0, 0, 0, 0,203, 0,204, 0, - 0,205, 0,206,207, 0,208,209,210,211,212, 2, 2,213, 2,214, - 215, 2, 2, 2,216, 17,217,218,219, 2,220, 2,221, 0, 0, 0, - 222,223,224,225,226,227,228, 2, 2, 0,229, 2, 2, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 17,230, 10, 1, 1, 10, 10, 10,231, 0, 0, 0, 0, 1, 1, 1, - 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 1, 1, 1, 1, 1, 1, - 4, 1, 1, 4, 1, 1, 4, 1, 1, 5, 5, 5, 5, 9, 0, 0, - 0, 7, 0, 0, 0, 10, 10, 10, 10, 8, 8, 8, 8, 0, 0, 0, - 6, 0, 6, 0, 0, 16, 16, 16, 16, 5, 12, 0, 0, 0, 0, 6, - 5, 19, 0, 0, 0, 0, 0, 7, 5, 47, 0, 0, 0, 0, 0, 0, - 20, 0, 29, 0, 0, 0, 0, 0, 7, 0, 0, 0, 3, 49, 0, 0, - 0, 0, 0, 0, 18, 0, 0, 24, 3, 13, 0, 0, 0, 11, 0, 0, - 0, 0, 19, 0, 0, 12, 0, 0, 0, 5, 9, 0, 0, 0, 0, 0, - 17, 0, 0, 3, 3, 0, 0, 0, 52, 14, 5, 5, 5, 0, 7, 5, - 5, 5, 0, 0, 19, 26, 0, 0, 0, 27, 56, 48, 38, 57, 0, 0, - 0, 0, 0, 0, 46, 0, 0, 0, 31, 23, 0, 0, 0, 0, 0, 6, - 55, 5, 5, 5, 9, 0, 0, 0, 14, 0, 0, 0, 19, 7, 11, 0, - 0, 7, 9, 0, 0, 5, 5, 5, 11, 5, 0, 0, 0, 0, 0, 22, - 0, 28, 0, 0, 0, 0, 0, 40, 0, 0, 0, 17, 0, 18, 13, 0, - 0, 0, 24, 0, 0, 13, 0, 0, 18, 0, 0, 0, 32, 15, 15, 15, - 15, 0, 9, 0, 0, 0, 0, 0, 5, 0, 17, 3, 3, 3, 3, 3, - 21, 0, 0, 17, 3, 2, 2, 79, 80, 0, 0, 22, 81, 6, 5, 11, - 0, 5, 5, 5, 36, 29, 36, 0, 0, 64, 65, 0, 0, 5, 5, 12, - 82, 0, 7, 5, 83, 5, 37, 31, 11, 0, 0, 0, 84, 5, 5, 12, - 0, 0, 7, 36, 5, 5, 14, 14, 11, 0, 0, 14, 0, 65, 6, 5, - 5, 66, 5, 5, 5, 0, 0, 53, 46, 27, 5, 33, 54, 14, 5, 0, - 0, 0, 0, 0, 55, 34, 6, 37, 11, 11, 19, 0, 0, 27, 36, 85, - 38, 0, 14, 0, 0, 20, 0, 0, 0, 0, 0, 0, 53, 58, 35, 67, - 38, 33, 9, 0, 0, 33, 68, 31, 11, 0, 0, 0, 69, 59, 68, 31, - 11, 39, 0, 0, 0, 27, 46, 67, 86, 0, 0, 20, 6, 30, 60, 15, - 32, 35, 0, 0, 0, 70, 5, 12, 0, 0, 6, 5, 12, 70, 5, 5, - 9, 0, 0, 5, 12, 0, 0, 11, 0, 0, 71, 19, 35, 14, 5, 5, - 26, 5, 31, 0, 14, 5, 5, 14, 5, 0, 20, 0, 0, 43, 5, 57, - 34, 0, 35, 11, 7, 14, 9, 0, 0, 20, 34, 0, 19, 16, 16, 8, - 8, 0, 44, 5, 23, 15, 72, 27, 5, 0, 0, 6, 87, 0, 0, 19, - 0, 26, 32, 33, 0, 72, 15, 27, 0, 0, 6, 61, 0, 0, 88, 5, - 12, 60, 14, 5, 33, 32, 5, 5, 37, 5, 5, 5, 0, 5, 73, 0, - 0, 0, 5, 5, 23, 30, 9, 0, 0, 74, 23, 5, 11, 0, 53, 23, - 50, 0, 15, 15, 5, 5, 30, 0, 0, 12, 5, 5, 5, 43, 5, 9, - 19, 0, 56, 11, 0, 0, 0, 52, 89, 0, 0, 2, 90, 0, 0, 0, - 13, 0, 3, 21, 0, 0, 0, 13, 0, 3, 0, 25, 0, 0, 28, 0, - 0, 0, 0, 18, 25, 3, 13, 0, 28, 22, 21, 13, 22, 91, 28, 28, - 17, 0, 0, 25, 0, 62, 0, 3, 3, 3, 0, 0, 18, 45, 40, 13, - 0, 0, 0, 18, 17, 17, 3, 51, 13, 21, 18, 17, 0, 21, 0, 0, - 40, 0, 21, 13, 17, 51, 13, 0, 0, 3, 41, 25, 22, 28, 22, 3, - 41, 28, 62, 0, 22, 22, 0, 13, 0, 0, 45, 0, 62, 18, 41, 0, - 0, 22, 0, 0, 0, 0, 21, 0, 0, 0, 0, 18, 13, 13, 22, 0, - 0, 13, 0, 0, 22, 0, 0, 29, 0, 0, 18, 22, 0, 12, 5, 5, - 11, 20, 20, 6, 0, 39, 75, 0, 9, 92, 0, 0, 0, 0, 15, 15, - 15, 15, 11, 0, 0, 11, 0, 0, 6, 0, 7, 5, 11, 0, 6, 5, - 5, 44, 0, 0, 0, 10, 10, 10, 93, 6, 30, 23, 23, 0, 0, 14, - 26, 75, 34, 0, 0, 6, 0, 0, 94, 0, 0, 0, 9, 31, 37, 9, - 7, 0, 0, 39, 23, 0, 95, 0, 0, 39, 54, 58, 38, 1, 0, 0, - 0, 16, 76, 96, 8, 8, 8, 8, 0, 2, 2, 2, 0, 0, 7, 12, - 0, 14, 29, 0, 5, 0, 0, 12, 6, 0, 5, 0, 0, 0, 0, 14, - 11, 0, 0, 6, 9, 97, 0, 0, 0, 0, 0, 5, 5, 37, 9, 0, - 6, 32, 26, 34, 77, 20, 0, 0, 77, 0, 6, 5, 27, 0, 98, 0, - 0, 6, 0, 0, 0, 39, 30, 5, 26, 99, 0, 14, 69, 23, 5, 0, - 20, 32, 5, 12, 0, 33, 48, 48, 38, 35, 7, 5, 9, 0, 0, 61, - 5, 60, 71, 49, 30,100, 0, 0, 0, 29, 0, 0, 0, 0, 63, 5, - 5, 30,101, 0, 0, 61, 5, 59, 50, 43, 0, 0, 0, 30, 11, 15, - 44, 0, 0, 0, 11, 32, 5, 26, 50, 0, 0, 6, 54, 5, 5, 0, - 0, 0, 0, 0,102, 7, 44, 5, 0, 5, 5, 34, 0, 33,103,104, - 105,106, 0, 0, 0, 63, 5, 7, 15, 9, 73, 0, 0, 14, 5, 12, - 0, 6, 5,107, 12, 14, 26, 27, 0, 0, 64,108, 5, 5, 26, 11, - 0, 43, 59, 0, 0, 0, 0, 0, 39, 5, 12, 5, 44, 7, 5, 5, - 5, 5, 5, 74, 5, 43, 34, 0, 0, 14, 12, 20, 36, 5, 66, 0, - 0, 0, 0, 35,109,110, 50, 0, 0, 6, 58, 0, 0,111, 0, 0, - 0, 0, 30, 12, 35, 0, 0, 20, 0, 9, 6, 5, 5, 5, 11, 0, - 0, 5, 5, 23, 27, 78, 78, 76, 0, 63, 15, 15, 15, 15, 15, 0, - 6, 0, 0, 0, 29, 2, 0, 0, 0, 0, 14, 11, 14,112, 2,113, - 5, 12, 14, 5, 0, 0, 0, 7, 11, 5, 12, 6, 5, 5, 12, 5, - 5, 5, 5, 37, 5, 36, 31, 12, 0, 6, 20, 0, 7, 0, 5, 12, - 0, 0, 13, 0, 0, 0, 3, 3, 3, 21, 0, 0, 17, 0, 0, 0, - 28, 24, 3, 25, 0, 3,114, 42, 42, 42, 42, 42, 42, 24, 3, 3, - 3, 0, 0, 28, 0, 17, 3, 25, 3, 21, 3, 3, 3, 3, 17, 24, - 17, 45, 41,115, 5, 3, 3, 3, 41, 0, 0, 24, 25, 3, 3, 0, - 18, 45, 3, 25, 0, 0, 18, 17, 21, 13, 40, 0, 0, 0, 21, 13, - 0, 40, 0, 0, 13, 17, 13, 0, 0, 24, 0, 0, 25, 51, 0, 13, - 18, 18, 0, 17, 3, 3, 21, 18, 3, 25, 24, 3, 3, 3, 21, 51, - 3, 45, 3, 3, 3, 17, 3, 3, 3, 3, 3, 25, 3, 3, 41, 3, - 3, 0, 0,221,221, 51, 51,238,238,220,221, 68, 68, 0, 64, 0, - 68,187,187, 4, 0,153,153, 68, 0, 68, 4, 14, 0, 64, 68,136, - 136,170,170, 0,238, 0,224, 64, 0, 0, 4,238, 0,224, 0, 68, - 136,224,238,238, 14, 68,132, 72, 68, 0, 14, 64, 4,136, 68, 4, - 68,136, 72,132,136, 72, 4, 0,136, 68, 64, 4, 64, 72, 0, 0, - 128,224, 14,238,224,102,102,132, 68, 68, 72, 14,224, 4,136, 64, - 136, 8,128, 68, 8, 72, 72,224,224, 0, 48, 0,132, 72,136, 4, - 132, 4,128, 64,132,132, 8,132,132, 4, 4,132, 72, 14, 14,128, - 136,119,119,119, 0, 68, 71, 8,136, 8, 68, 4, 72, 64,128, 64, - 64,136,132, 8, 0,128, 68, 72,132,170, 10,112, 0, 0,160, 51, - 50, 19, 51, 48, 14, 3, 0,116, 64, 0,112,132,128, 72, 7, 68, - 67,128,132, 84, 51, 51, 3, 14,238,136, 0, 9, 0,132, 0,128, - 4, 0,176, 72, 8,128, 8, 4,119,116, 4,132, 4, 64, 72,136, - 128, 8, 64, 68,116,120, 72,132, 64,119, 68,136, 8, 68,128, 68, - 135, 68, 52, 51, 67,238,102,238, 78, 33, 0, 48, 64,101,112,128, - 169, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, - 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, - 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, - 0, 0, 36, 0, 0, 0, 0, 37, 0, 38, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 39, 0, 0, 0, 0, 0, 0, 0, 0, 40, 41, 42, 0, 0, 0, 43, - 0, 0, 44, 45, 46, 47, 48, 0, 0, 0, 0, 49, 0, 0, 0, 0, - 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 8, 0, 0, 0, 0, 9, 0, 0, 0, 10, 11, 12, 13, - 0, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 2, 41, 42, 43, - 44, 45, 46, 47, 48, 49, 50, 51, 52, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 53, 0, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 0, - 59, 0, 60, 0, 0, 0, 61, 62, 63, 64, 65, 66, 67, 68, 69, 0, - 0, 70, 0, 0, 0, 1, 71, 0, 0, 72, 0, 0, 0, 0, 0, 0, - 0, 73, 0, 74, 0, 3, 75, 0, 76, 0, 0, 0, 0, 0, 0, 77, - 4, 78, 0, 0, 0, 0, 79, 0, 0, 80, 81, 82, 83, 84, 85, 86, - 87, 88, 0, 0, 0, 89, 0, 0, 0, 0, 90, 0, 0, 0, 91, 0, - 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 92, 0, 0, - 0, 93, 0, 94, 0, 0, 95, 0, 0, 96, 0, 0, 0, 0, 0, 0, - 0, 0, 97, 98, 0, 0, 0, 0, 99,100, 0,101,102, 0,103,104, - 105,106,107,108,109,110,111, 2, 0,112,113,114,115,116,117,118, - 119,120, 0, 0,121,122,123,124,125, 0,126, 0, 0, 0,127, 0, - 0, 0,128,129, 0,130,131,132,133, 0, 0,134, 0, 0,135, 0, - 136, 0,137,138,139, 0, 0, 0, 0,140,141,142, 0, 0, 0,143, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,144, 0, 0, 0, 0, 0, - 0,145, 5, 0, 0, 0, 0, 0, 0, 0, 0, 6,146,147, 0, 0, - 148, 0, 0, 0, 0, 0, 0, 0, 0, 0,149,150, 0, 0, 0, 0, - 0, 0, 0,151,152, 0, 0,153, 0, 0, 0, 0, 0, 0,154,155, - 156, 0, 0, 0, 0, 0,157, 0, 6, 0, 5, 0, 0, 0, 0, 0, - 158, 7, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0,159, 0, 0, - 0,160, 0, 0, 0, 0, 0, 0, 0,161, 0,162, 0, 0, 0, 0, - 0, 0, 0, 0, 0,163, 3, 1, 0, 0, 1, 1, 1, 1, 0, 0, - 0, 0, 1, 1, 1, 1, 4, 0, 0, 0, 0, 0, 1, 1, 0, 13, - 0, 0, 0, 0, 0, 10, 3, 0, 0, 0, 0, 0, 20, 0, 1, 1, - 1, 0, 38, 0, 0, 0, 0, 21, 1, 39, 40, 0, 0, 0, 0, 22, - 0, 0, 9, 1, 0, 6, 0, 41, 42, 0, 0, 4, 0, 1, 22, 0, - 0, 0, 0, 0, 23, 6, 0, 0, 9, 43, 0, 44, 45, 0, 0, 46, - 0, 0, 0, 24, 0, 0, 14, 1, 47, 1, 25, 8, 2, 48, 49, 50, - 5, 51, 4, 8, 11, 52, 15, 53, 5, 54, 16, 0, 0, 55, 56, 4, - 0, 57, 16, 8, 11, 58, 59, 0, 5, 60, 4, 8, 11, 61, 15, 62, - 5, 63, 26, 0, 0, 12, 27, 17, 0, 0, 64, 8, 11, 65, 66, 67, - 5, 0, 4, 0, 0, 68, 69, 18, 5, 0, 7, 8, 2, 70, 15, 17, - 5, 0, 71, 72, 0, 0, 0, 0, 0, 73, 74, 0, 0, 0, 0, 0, - 0, 75, 76, 0, 0, 0, 0, 77, 0, 78, 0, 0, 0, 79, 80, 81, - 1, 28, 82, 0, 0, 0, 2, 2, 83, 84, 0, 85, 86, 87, 88, 19, - 0, 0, 0, 89, 0, 0, 0, 29, 0, 30, 0, 5, 0, 5, 2, 2, - 2, 90, 91, 92, 0, 0, 93, 0, 0, 0, 18, 0, 94, 0, 0, 0, - 95, 96, 0, 97, 2, 2, 2, 98, 99,100, 0, 0, 0, 1, 1, 31, - 101, 0,102,103, 2,104,105, 0, 9, 32,106, 2,107,108, 0, 0, - 109, 32, 0, 0, 20,110, 0,111,112,113, 19, 0, 0, 0, 0, 1, - 1, 6, 0, 0, 3, 7, 0, 0, 0, 3, 0, 0, 14, 0, 0,114, - 0, 0, 0, 0, 3,115, 0, 0, 0, 7,116, 0,117, 0,118, 0, - 1,119, 0, 0,120, 0, 24,121, 0, 0,122, 2, 2,123,124, 0, - 125,126, 0, 0,127,128,129, 0, 2,130, 0, 0, 0,131, 4, 0, - 132,133, 2,134,135, 0, 0, 12, 0, 0, 1, 0, 1, 0, 0, 0, - 0, 19, 0, 0, 6, 0, 0, 0, 0,136,137,138, 2,139, 0, 0, - 18, 0, 0, 0,140, 0, 0, 0,141, 0, 0, 0,142, 0, 0, 0, - 0, 14, 23, 6, 0, 0, 29, 0, 0, 0, 4, 0, 0, 33, 10, 0, - 0,143, 7, 0, 0,144, 26, 0, 0, 0,145, 2,146,147,148, 0, - 0, 34, 7, 0, 0,149,150, 0, 0, 0, 0, 0, 3,151, 0, 3, - 152, 0, 7, 0, 0,153, 27, 17,154, 35,155, 2, 2,156,157,158, - 16, 0, 0, 0, 0, 33,159, 12, 0, 0, 0, 0, 0,160,161, 0, - 0, 0, 0, 0, 3,162, 6,163, 0, 0, 0, 0, 0,164, 6, 0, - 0, 0, 0, 0,165,166, 0,167,168, 0, 0, 0, 3,169,170,171, - 2,172, 34, 0, 0, 0, 0,173, 6, 0,174, 2, 2,175,176,177, - 2, 2,178,179, 0, 0, 0, 0,180, 0, 0, 0, 0,181, 0,182, - 183,184, 0, 0, 0,185,186, 0, 0, 0, 0,187, 0, 0, 0, 0, - 0,188,189,190, 2,191,192,193, 0, 0,194,195, 0, 0, 0, 13, - 196, 0, 0, 0, 0, 35, 3, 25, 0, 0, 0, 0, 36, 7, 0,197, - 0, 0, 1, 1, 31, 1, 10, 0, 0, 0, 0, 0,198,199,200, 0, - 201, 0, 30, 0, 0, 0, 1, 1, 1,202, 1, 1, 28, 37, 36, 9, - 21, 0,203,204,205, 0, 0, 0, 12, 0, 0, 0, 13, 0, 0, 0, - 206, 37, 0, 10, 0, 0,207, 0, 0, 0, 0, 0, 0, 9, 0, 0, - 0, 0,255,255,255,255, 85, 85, 85, 85, 0, 0, 0,192, 12, 0, - 0, 0,240, 0, 0, 0, 3, 0, 0, 0, 15, 0, 0, 0, 0, 84, - 85, 85, 0, 0,192,255,255, 63, 0, 0, 85, 85, 81, 85, 0, 0, - 0, 48, 0, 0, 0,240, 0, 0,240,255,252, 3, 0, 8, 60, 0, - 0, 0, 0,192, 0, 0, 0, 60, 0, 0, 0, 0, 0, 12, 0, 0, - 0,255,252,255,255,255,255,255, 63, 0, 0,240,255,255, 0,192, - 255,255, 63, 0, 0, 0, 48, 0, 0, 0, 3, 0, 0, 12,255,255, - 255, 3,240, 15, 0, 0,240, 3, 0, 0,255,255,255, 15,255, 0, - 0, 0, 0, 0,255,255,192, 0, 0, 0,255, 3, 0, 0, 0, 3, - 0, 0, 0, 12, 0, 0,192,255, 15, 0,255,255,255,207, 60,207, - 0, 0, 0,240,255,195,255,195,243, 15,255, 0, 0, 12, 0,240, - 207,255,255,252,252, 15, 0, 0,252, 0,207,255,255,255, 85, 85, - 53, 3,252,255, 3, 8,252,255, 85, 85, 0, 0, 85, 85, 17, 80, - 5, 51, 0,192, 0, 69, 5, 0, 0, 48, 0, 0, 0, 3, 60,192, - 195, 15, 15, 12, 0, 0, 81, 84, 5, 3,252,207, 3, 8, 0, 0, - 244,255, 81, 84, 5,243, 0,252, 0, 69, 4, 0, 0, 0, 3, 3, - 0, 0, 85, 85, 5,243, 3,240,243, 11, 0, 60, 21, 0, 0, 0, - 0,195, 51,240,243, 15, 85, 85,213, 51, 0, 0, 48,192,240, 51, - 0,192, 12,255, 63, 0, 0,192,255, 63, 12,255,255, 3, 0, 0, - 255, 63, 0, 0, 15, 0, 0,204, 12, 0,252,255,255, 63,255,243, - 0,252,255,255,252,255, 0, 48, 0, 0, 85, 85, 21,252,243,255, - 56,124, 85, 5, 95,245, 7, 20, 0, 80,253, 87, 85, 85, 53, 60, - 0, 28, 0, 0, 0,252, 85,207,255, 15, 0, 48,252,255,239, 0, - 0, 12, 0, 0,192,207, 0, 0, 12, 0, 63,192, 3, 0, 48, 0, - 252, 0, 0,192,195, 0, 85, 49,255, 63, 50,252,255, 3,192,255, - 255,195,255,255,255, 0,255, 0, 64, 1, 64, 85, 85, 85, 85,255, - 255, 15,240, 86, 85, 1, 79, 85, 85, 85,241, 15,191, 95, 0, 0, - 64, 5, 0, 48, 15,204,255,240, 0, 0, 63,255,255,255,243,255, - 3, 12, 0, 3, 15, 0, 0, 0, 60, 0, 63,255,255, 15, 48, 48, - 192, 0, 0, 60, 0, 3, 0, 15, 0, 0, 15, 0, 0,192, 0,240, - 255, 15,207, 0, 0, 0, 63, 0, 84, 64,213,240, 15, 15, 2, 0, - 0, 0, 85, 77, 85, 85, 0, 0, 80, 21, 0, 0,252, 63, 60, 60, - 0, 0,192, 0, 0, 3, 84, 0, 16, 83,243,195, 3,240, 85, 85, - 21, 15, 0, 32, 0, 0, 85, 85, 21, 0, 0, 12, 3, 12, 0,240, - 63, 0,253, 60, 0,255, 85, 84, 84, 85, 85, 5, 63,128, 0,255, - 0, 0, 0, 0,252, 15, 0, 0,192, 3,195, 3, 0,192,192, 63, - 60, 0,127, 85, 85, 85, 85,213,255,252,191, 3, 0, 0, 0, 65, - 0, 0, 0,240,255, 63, 3, 0,252,195, 15,255, 0, 48,192,255, - 63, 0, 0, 0,192, 51, 0,240,255, 3, 85, 85, 69, 16, 85, 5, - 195,255, 51,204, 15,240, 50, 0, 0, 0,240, 51, 0, 0,195,255, - 51,204,243, 0, 0, 0,240, 15, 0,207, 0, 0, 0, 15,192,255, - 63,204, 0, 0,192, 12,255,255, 0, 0, 0, 0, 0,204,240,207, - 255, 0,255,255, 60, 0, 85, 21, 4, 85, 85, 20, 85, 85, 3, 0, - 192, 47, 0,255,240, 0,253,255,127, 85,213,255,195, 63, 0,128, - 0, 0,253, 63,252, 85, 85, 0,240,255,255, 63, 11, 0,243, 51, - 0, 0,255, 63,255,207,240,255,255,255,255,255,240,255,243, 60, - 0, 0,252, 63, 48,207,255,207, 0, 0, 15,204, 0, 0,192, 3, - 0, 0, 15, 85, 85, 85, 81, 85, 85, 85, 85,240, 63, 0, 47, 0, - 0, 0, 0, 0, 48, 0, 3,192,255,255,255, 15, 0, 0,255,255, - 15,252, 0, 0, 0, 60, 0,252, 15,252, 63, 0,192,255, 63,252, - 255, 0, 0, 0,240, 15,255, 63,192,255,255, 63,255,255,255,255, - 195,255,207,243, 63, 0,192, 48, 0,240, 0,255, 63, 0, -}; -static const uint16_t _raqm_u16[928]= -{ - 0, 0, 0, 0, 8, 8, 8, 8, 12, 12, 12, 12, 16, 20, 4, 24, - 4, 28, 4, 16, 20, 4, 24, 4, 28, 4, 16, 20, 4, 24, 4, 28, - 4, 16, 20, 4, 24, 4, 28, 4, 32, 32, 32, 32, 0, 0, 0, 140, - 164, 0, 0, 168, 176, 0, 0, 180, 0, 92, 0, 0, 0, 0, 0, 64, - 0, 0, 252, 0, 12, 12, 12, 260, 268, 8, 0, 0, 8, 8, 272, 0, - 32, 32, 32, 0, 276, 0, 0, 0, 0, 144, 32, 280, 284, 0, 0, 0, - 288, 292, 0, 0, 68, 32, 0, 36, 0, 296, 300, 0, 304, 72, 0, 32, - 308, 0, 0, 0, 0, 0, 148, 36, 0, 0, 68, 152, 0, 312, 316, 0, - 0, 320, 0, 0, 0, 324, 0, 0, 76, 32, 328, 32, 156, 0, 0, 332, - 336, 340, 40, 0, 80, 0, 0, 344, 160, 56, 40, 84, 348, 72, 0, 352, - 356, 0, 40, 76, 80, 0, 0, 172, 160, 360, 40, 0, 364, 0, 0, 368, - 372, 56, 0, 0, 376, 0, 0, 172, 380, 88, 40, 0, 80, 0, 0, 384, - 388, 88, 40, 392, 396, 56, 40, 0, 80, 0, 0, 0, 400, 404, 0, 408, - 0, 0, 0, 412, 416, 0, 0, 0, 0, 0, 0, 420, 424, 0, 0, 0, - 0, 428, 0, 432, 0, 0, 0, 436, 440, 444, 32, 184, 448, 0, 0, 0, - 0, 0, 188, 452, 0, 456, 36, 460, 464, 192, 0, 0, 44, 44, 44, 44, - 44, 44, 60, 60, 60, 60, 468, 48, 48, 48, 48, 48, 0, 188, 0, 0, - 0, 196, 0, 200, 0, 40, 0, 40, 0, 0, 0, 472, 476, 152, 0, 0, - 480, 0, 0, 0, 88, 0, 484, 0, 0, 0, 488, 492, 0, 496, 0, 0, - 0, 500, 504, 508, 0, 0, 0, 32, 32, 204, 512, 0, 516, 0, 0, 520, - 524, 0, 68, 208, 100, 0, 528, 0, 0, 0, 532, 208, 0, 0, 536, 540, - 0, 544, 548, 552, 556, 0, 560, 564, 212, 0, 8, 0, 0, 32, 32, 36, - 0, 0, 216, 212, 0, 568, 220, 0, 0, 224, 572, 0, 104, 0, 108, 576, - 216, 0, 0, 0, 0, 0, 224, 580, 112, 0, 0, 584, 588, 592, 596, 600, - 604, 608, 612, 616, 0, 620, 624, 628, 632, 636, 220, 640, 644, 648, 652, 228, - 656, 660, 228, 0, 0, 232, 664, 236, 0, 0, 0, 668, 232, 672, 0, 0, - 0, 676, 0, 0, 0, 0, 52, 116, 0, 0, 0, 52, 0, 0, 32, 32, - 0, 0, 76, 680, 0, 684, 0, 0, 0, 688, 0, 0, 0, 0, 52, 692, - 0, 0, 0, 116, 696, 0, 700, 0, 704, 0, 0, 708, 712, 0, 32, 716, - 0, 0, 720, 0, 724, 728, 44, 732, 156, 0, 0, 736, 36, 0, 120, 0, - 0, 0, 740, 744, 748, 0, 0, 752, 0, 0, 0, 756, 72, 0, 760, 764, - 0, 0, 768, 0, 24, 4, 772, 60, 776, 48, 48, 780, 0, 84, 0, 0, - 32, 0, 32, 0, 0, 0, 0, 784, 0, 0, 0, 192, 0, 0, 36, 0, - 0, 0, 0, 788, 792, 0, 0, 796, 0, 0, 88, 0, 0, 0, 800, 0, - 0, 0, 804, 0, 0, 0, 808, 0, 0, 0, 0, 76, 148, 36, 0, 0, - 196, 0, 0, 0, 812, 0, 0, 816, 64, 0, 0, 820, 100, 0, 0, 824, - 828, 0, 0, 0, 124, 0, 832, 128, 836, 0, 0, 840, 100, 0, 0, 844, - 848, 0, 0, 0, 0, 0, 240, 852, 72, 0, 0, 0, 0, 52, 856, 0, - 860, 56, 864, 128, 0, 0, 0, 868, 872, 876, 880, 0, 0, 0, 0, 884, - 888, 84, 0, 0, 0, 0, 0, 892, 896, 0, 0, 0, 0, 0, 52, 900, - 36, 904, 0, 0, 0, 0, 0, 908, 36, 0, 0, 0, 0, 0, 912, 916, - 0, 920, 924, 0, 0, 0, 240, 928, 0, 0, 0, 932, 936, 0, 0, 0, - 0, 940, 944, 0, 948, 0, 0, 952, 56, 956, 0, 0, 960, 964, 0, 0, - 0, 0, 968, 0, 0, 0, 972, 976, 0, 980, 984, 988, 0, 0, 0, 992, - 996, 0, 0, 0,1000,1004, 0, 0, 0, 0, 0,1008,1012, 0, 0,1016, - 124,1020, 0, 0, 0, 0, 0, 8,1024,1028, 0, 0, 0, 92,1032, 0, - 0, 0, 0, 128, 0, 0,1036, 0, 52,1040, 244, 244,1044, 124, 0, 0, - 0, 0, 248, 116, 0,1048,1052, 0, 32, 32, 204, 32, 64, 0, 0, 0, - 0, 0,1056,1060,1064, 0,1068, 0, 200, 0, 0, 0, 32, 32, 32,1072, - 32, 32, 184, 120, 248, 68, 144, 0,1076,1080,1084, 0, 52, 0, 0, 0, - 0, 0, 84, 0, 0, 0, 92, 0, 0, 0,1088, 120, 0, 64, 0, 0, - 1092, 0, 0, 0,1096, 0, 96, 0, 0,1100, 104, 112, 236, 112, 0, 256, - 0, 0, 0,1104,1108,1112, 132, 12, 12, 12,1116,1120,1124,1128, 104,1132, - 108, 12, 256, 12, 12, 12,1136, 12, 12,1140, 12, 12, 12, 12, 12,1144, - 12, 12, 12,1148,1152, 12,1156,1160,1164,1168,1172,1176,1180,1184,1188,1192, - 12, 0, 0, 0,1196,1200,1204,1208, 0, 264, 12, 12, 96, 0, 0, 0, - 136, 264, 0, 0, 136, 0, 132, 96,1212, 108, 12, 12, 96, 12, 12,1216, - 1220, 12, 12, 12, 0, 136, 132, 12, 8, 8, 32, 32, 32, 32, 32, 8, -}; - -static inline uint8_t _raqm_b4 (const uint8_t* a, unsigned i) -{ - return (a[i>>1]>>((i&1)<<2))&15; -} -static inline uint8_t _raqm_get_grapheme_break (unsigned u) -{ - /* packtab: [2^4,2^4,2^3,2^2,2^2,2^2] */ - return u<921600u ? (uint8_t)(_raqm_b4(_raqm_u8+2081u,((_raqm_u8[857u+_raqm_u16[((_raqm_u8[257u+((_raqm_u8[57u+((_raqm_b4(_raqm_u8,((((((((((u)>>2))>>2))>>2))>>3))>>4)))<<4)+((((((((((u)>>2))>>2))>>2))>>3))&15)])<<3)+((((((((u)>>2))>>2))>>2))&7)])<<2)+((((((u)>>2))>>2))&3)]+((((u)>>2))&3)])<<2)+((u)&3))) : 0; -} -static inline uint8_t _raqm_b2 (const uint8_t* a, unsigned i) -{ - return (a[i>>2]>>((i&3)<<1))&3; -} -static inline uint8_t _raqm_get_incb (unsigned u) -{ - /* packtab: [2^4,2^4,2^3,2^2,2^4] */ - return u<918000u ? (uint8_t)(_raqm_b2(_raqm_u8+3614u,((_raqm_u8[2958u+((_raqm_u8[2550u+((_raqm_u8[2370u+((_raqm_b4(_raqm_u8+2313u,((((((((u)>>4))>>2))>>3))>>4)))<<4)+((((((((u)>>4))>>2))>>3))&15)])<<3)+((((((u)>>4))>>2))&7)])<<2)+((((u)>>4))&3)])<<4)+((u)&15))) : 0; -} - -#endif /* _RAQM_GRAPHEME_DATA_H_ */ diff --git a/src/thirdparty/raqm/raqm-version.h b/src/thirdparty/raqm/raqm-version.h deleted file mode 100644 index 2b584fcc717..00000000000 --- a/src/thirdparty/raqm/raqm-version.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © 2011 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#ifndef _RAQM_H_IN_ -#error "Include instead." -#endif - -#ifndef _RAQM_VERSION_H_ -#define _RAQM_VERSION_H_ - -#define RAQM_VERSION_MAJOR 0 -#define RAQM_VERSION_MINOR 11 -#define RAQM_VERSION_MICRO 0 - -#define RAQM_VERSION_STRING "0.11.0" - -#define RAQM_VERSION_ATLEAST(major,minor,micro) \ - ((major)*10000+(minor)*100+(micro) <= \ - RAQM_VERSION_MAJOR*10000+RAQM_VERSION_MINOR*100+RAQM_VERSION_MICRO) - -#endif /* _RAQM_VERSION_H_ */ diff --git a/src/thirdparty/raqm/raqm.c b/src/thirdparty/raqm/raqm.c deleted file mode 100644 index 36ff187499f..00000000000 --- a/src/thirdparty/raqm/raqm.c +++ /dev/null @@ -1,2950 +0,0 @@ -/* - * Copyright © 2015 Information Technology Authority (ITA) - * Copyright © 2016-2023 Khaled Hosny - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - * - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include - -#ifdef RAQM_SHEENBIDI -#ifdef RAQM_SHEENBIDI_GT_2_9 -#include -#else -#include -#endif -#else -#ifdef HAVE_FRIBIDI_SYSTEM -#include -#else -#include "../fribidi-shim/fribidi.h" -#endif -#endif - -#include -#include - -#include "raqm.h" -#include "grapheme-data.h" - -/** - * SECTION:raqm - * @title: Raqm - * @short_description: A library for complex text layout - * @include: raqm.h - * - * Raqm is a lightweight text layout library with strong emphasis on - * supporting languages and writing systems that require complex text layout. - * - * The main object in Raqm API is #raqm_t, it stores all the states of the - * input text, its properties, and the output of the layout process. - * - * To start, you create a #raqm_t object, add text and font(s) to it, run the - * layout process, and finally query about the output. For example: - * - * |[ - * #include "raqm.h" - * - * int - * main (int argc, char *argv[]) - * { - * const char *fontfile; - * const char *text; - * const char *direction; - * const char *language; - * int ret = 1; - * - * FT_Library library = NULL; - * FT_Face face = NULL; - * - * if (argc < 5) - * { - * printf ("Usage: %s FONT_FILE TEXT DIRECTION LANG\n", argv[0]); - * return 1; - * } - * - * fontfile = argv[1]; - * text = argv[2]; - * direction = argv[3]; - * language = argv[4]; - * - * if (FT_Init_FreeType (&library) == 0) - * { - * if (FT_New_Face (library, fontfile, 0, &face) == 0) - * { - * if (FT_Set_Char_Size (face, face->units_per_EM, 0, 0, 0) == 0) - * { - * raqm_t *rq = raqm_create (); - * if (rq != NULL) - * { - * raqm_direction_t dir = RAQM_DIRECTION_DEFAULT; - * - * if (strcmp (direction, "r") == 0) - * dir = RAQM_DIRECTION_RTL; - * else if (strcmp (direction, "l") == 0) - * dir = RAQM_DIRECTION_LTR; - * - * if (raqm_set_text_utf8 (rq, text, strlen (text)) && - * raqm_set_freetype_face (rq, face) && - * raqm_set_par_direction (rq, dir) && - * raqm_set_language (rq, language, 0, strlen (text)) && - * raqm_layout (rq)) - * { - * size_t count, i; - * raqm_glyph_t *glyphs = raqm_get_glyphs (rq, &count); - * - * ret = !(glyphs != NULL || count == 0); - * - * printf("glyph count: %zu\n", count); - * for (i = 0; i < count; i++) - * { - * printf ("gid#%d off: (%d, %d) adv: (%d, %d) idx: %d\n", - * glyphs[i].index, - * glyphs[i].x_offset, - * glyphs[i].y_offset, - * glyphs[i].x_advance, - * glyphs[i].y_advance, - * glyphs[i].cluster); - * } - * } - * - * raqm_destroy (rq); - * } - * } - * - * FT_Done_Face (face); - * } - * - * FT_Done_FreeType (library); - * } - * - * return ret; - * } - * ]| - * To compile this example: - * |[ - * cc -o test test.c `pkg-config --libs --cflags raqm` - * ]| - */ - -/* For enabling debug mode */ -/*#define RAQM_DEBUG 1*/ -#ifdef RAQM_DEBUG -#define RAQM_DBG(...) fprintf (stderr, __VA_ARGS__) -#else -#define RAQM_DBG(...) -#endif - -#ifdef RAQM_TESTING -# define RAQM_TEST(...) printf (__VA_ARGS__) -# define RAQM_STATIC -# define SCRIPT_TO_STRING(script) \ - char buff[5]; \ - hb_tag_to_string (hb_script_to_iso15924_tag (script), buff); \ - buff[4] = '\0'; -#else -# define RAQM_TEST(...) -# define RAQM_STATIC static -#endif - -#define RAQM_BIDI_LEVEL_IS_RTL(level) \ - ((level) & 1) - -#ifdef RAQM_SHEENBIDI - typedef SBLevel _raqm_bidi_level_t; -#else - typedef FriBidiLevel _raqm_bidi_level_t; -#endif - -typedef struct -{ - FT_Face ftface; - int ftloadflags; - hb_language_t lang; - hb_script_t script; - int spacing_after; -} _raqm_text_info; - -typedef struct _raqm_run raqm_run_t; - -struct _raqm -{ - int ref_count; - - uint32_t *text; - uint16_t *text_utf16; - char *text_utf8; - size_t text_len; - size_t text_capacity_bytes; - - _raqm_text_info *text_info; - - raqm_direction_t base_dir; - raqm_direction_t resolved_dir; - - hb_feature_t *features; - size_t features_len; - - raqm_run_t *runs; - raqm_run_t *runs_pool; - - raqm_glyph_t *glyphs; - size_t glyphs_capacity; - - int invisible_glyph; -}; - -struct _raqm_run -{ - uint32_t pos; - uint32_t len; - - hb_direction_t direction; - hb_script_t script; - hb_font_t *font; - hb_buffer_t *buffer; - - raqm_run_t *next; -}; - -static size_t -_raqm_encoding_to_u32_index (raqm_t *rq, - size_t index); - -static void -_raqm_init_text_info (raqm_t *rq) -{ - hb_language_t default_lang = hb_language_get_default (); - for (size_t i = 0; i < rq->text_len; i++) - { - rq->text_info[i].ftface = NULL; - rq->text_info[i].ftloadflags = -1; - rq->text_info[i].lang = default_lang; - rq->text_info[i].script = HB_SCRIPT_INVALID; - rq->text_info[i].spacing_after = 0; - } -} - -static void -_raqm_release_text_info (raqm_t *rq) -{ - if (!rq->text_info) - return; - - for (size_t i = 0; i < rq->text_len; i++) - { - if (rq->text_info[i].ftface) - FT_Done_Face (rq->text_info[i].ftface); - } -} - -static bool -_raqm_compare_text_info (_raqm_text_info a, - _raqm_text_info b) -{ - if (a.ftface != b.ftface) - return false; - - if (a.ftloadflags != b.ftloadflags) - return false; - - if (a.lang != b.lang) - return false; - - if (a.script != b.script) - return false; - - /* Spacing shouldn't break runs, so we don't compare them here. */ - - return true; -} - -static void -_raqm_free_text(raqm_t* rq) -{ - free (rq->text); - rq->text = NULL; - rq->text_info = NULL; - rq->text_utf8 = NULL; - rq->text_utf16 = NULL; - rq->text_len = 0; - rq->text_capacity_bytes = 0; -} - -static bool -_raqm_alloc_text(raqm_t *rq, - size_t len, - bool need_utf8, - bool need_utf16) -{ - /* Allocate contiguous memory block for texts and text_info */ - size_t mem_size = (sizeof (uint32_t) + sizeof (_raqm_text_info)) * len; - if (need_utf8) - mem_size += sizeof (char) * len; - else if (need_utf16) - mem_size += sizeof (uint16_t) * len; - - if (mem_size > rq->text_capacity_bytes) - { - void* new_mem = realloc (rq->text, mem_size); - if (!new_mem) - { - _raqm_free_text (rq); - return false; - } - - rq->text_capacity_bytes = mem_size; - rq->text = new_mem; - } - - rq->text_info = (_raqm_text_info*)(rq->text + len); - rq->text_utf8 = need_utf8 ? (char*)(rq->text_info + len) : NULL; - rq->text_utf16 = need_utf16 ? (uint16_t*)(rq->text_info + len) : NULL; - - return true; -} - -static raqm_run_t* -_raqm_alloc_run (raqm_t *rq) -{ - raqm_run_t *run = rq->runs_pool; - if (run) - { - rq->runs_pool = run->next; - } - else - { - run = malloc (sizeof (raqm_run_t)); - if (!run) - return NULL; - run->font = NULL; - run->buffer = NULL; - } - - run->pos = 0; - run->len = 0; - run->direction = HB_DIRECTION_INVALID; - run->script = HB_SCRIPT_INVALID; - run->next = NULL; - - return run; -} - -static void -_raqm_free_runs (raqm_run_t *runs) -{ - while (runs) - { - raqm_run_t *run = runs; - runs = runs->next; - - if (run->buffer) - hb_buffer_destroy (run->buffer); - - if (run->font) - hb_font_destroy (run->font); - - free (run); - } -} - -/** - * raqm_create: - * - * Creates a new #raqm_t with all its internal states initialized to their - * defaults. - * - * Return value: - * A newly allocated #raqm_t with a reference count of 1. The initial reference - * count should be released with raqm_destroy() when you are done using the - * #raqm_t. Returns `NULL` in case of error. - * - * Since: 0.1 - */ -raqm_t * -raqm_create (void) -{ - raqm_t *rq; - - rq = malloc (sizeof (raqm_t)); - if (!rq) - return NULL; - - rq->ref_count = 1; - - rq->base_dir = RAQM_DIRECTION_DEFAULT; - rq->resolved_dir = RAQM_DIRECTION_DEFAULT; - - rq->features = NULL; - rq->features_len = 0; - - rq->invisible_glyph = 0; - - rq->text = NULL; - rq->text_utf16 = NULL; - rq->text_utf8 = NULL; - rq->text_info = NULL; - rq->text_capacity_bytes = 0; - rq->text_len = 0; - - rq->runs = NULL; - rq->runs_pool = NULL; - - rq->glyphs = NULL; - rq->glyphs_capacity = 0; - - return rq; -} - -/** - * raqm_reference: - * @rq: a #raqm_t. - * - * Increases the reference count on @rq by one. This prevents @rq from being - * destroyed until a matching call to raqm_destroy() is made. - * - * Return value: - * The referenced #raqm_t. - * - * Since: 0.1 - */ -raqm_t * -raqm_reference (raqm_t *rq) -{ - if (rq) - rq->ref_count++; - - return rq; -} - -/** - * raqm_destroy: - * @rq: a #raqm_t. - * - * Decreases the reference count on @rq by one. If the result is zero, then @rq - * and all associated resources are freed. - * See raqm_reference(). - * - * Since: 0.1 - */ -void -raqm_destroy (raqm_t *rq) -{ - if (!rq || --rq->ref_count != 0) - return; - - _raqm_release_text_info (rq); - _raqm_free_text (rq); - _raqm_free_runs (rq->runs); - _raqm_free_runs (rq->runs_pool); - free (rq->glyphs); - free (rq->features); - free (rq); -} - -/** - * raqm_clear_contents: - * @rq: a #raqm_t. - * - * Clears internal state of previously used raqm_t object, making it ready - * for reuse and keeping some of allocated memory to increase performance. - * - * Since: 0.9 - */ -void -raqm_clear_contents (raqm_t *rq) -{ - if (!rq) - return; - - _raqm_release_text_info (rq); - - /* Return allocated runs to the pool, keep hb buffers for reuse */ - raqm_run_t *run = rq->runs; - while (run) - { - if (run->buffer) - hb_buffer_reset (run->buffer); - - if (run->font) - { - hb_font_destroy (run->font); - run->font = NULL; - } - - if (!run->next) - { - run->next = rq->runs_pool; - rq->runs_pool = rq->runs; - rq->runs = NULL; - break; - } - - run = run->next; - } - - rq->text_len = 0; - rq->resolved_dir = RAQM_DIRECTION_DEFAULT; -} - -/** - * raqm_set_text: - * @rq: a #raqm_t. - * @text: a UTF-32 encoded text string. - * @len: the length of @text. - * - * Adds @text to @rq to be used for layout. It must be a valid UTF-32 text, any - * invalid characters will be replaced with U+FFFD. The text must - * represent a single full paragraph, since doing the layout of chunks of text - * separately can give improper output. - * - * The text must be set before any of the functions that assign attributes to - * it, e.g. raqm_set_freetype_face(), raqm_set_language(), etc. - * - * Return value: - * `true` if no errors happened, `false` otherwise. - * - * Since: 0.1 - */ -bool -raqm_set_text (raqm_t *rq, - const uint32_t *text, - size_t len) -{ - if (!rq || !text) - return false; - - /* Call raqm_clear_contents to reuse this raqm_t */ - if (rq->text_len) - return false; - - /* Empty string, don’t fail but do nothing */ - if (!len) - return true; - - if (!_raqm_alloc_text(rq, len, false, false)) - return false; - - rq->text_len = len; - memcpy (rq->text, text, sizeof (uint32_t) * len); - _raqm_init_text_info (rq); - - return true; -} - -/* Ported from HarfBuzz’s hb_utf8_t::next(). */ -static const char * -_raqm_get_utf8_codepoint (const char *str, - const char *end, - uint32_t *out_codepoint) -{ - uint32_t c = (unsigned char) *str++; - - if (c > 0x7Fu) - { - if (c >= 0xC2u && c <= 0xDFu) /* Two-byte */ - { - unsigned int t1; - if (str < end && (t1 = (unsigned char) str[0] - 0x80u) <= 0x3Fu) - { - c = ((c & 0x1Fu) << 6) | t1; - str += 1; - } - else - c = 0xFFFDu; - } - else if (c >= 0xE0u && c <= 0xEFu) /* Three-byte */ - { - unsigned int t1, t2; - if (end - str >= 2 && - (t1 = (unsigned char) str[0] - 0x80u) <= 0x3Fu && - (t2 = (unsigned char) str[1] - 0x80u) <= 0x3Fu) - { - c = ((c & 0x0Fu) << 12) | (t1 << 6) | t2; - if (c < 0x0800u || (c >= 0xD800u && c <= 0xDFFFu)) - c = 0xFFFDu; - else - str += 2; - } - else - c = 0xFFFDu; - } - else if (c >= 0xF0u && c <= 0xF4u) /* Four-byte */ - { - unsigned int t1, t2, t3; - if (end - str >= 3 && - (t1 = (unsigned char) str[0] - 0x80u) <= 0x3Fu && - (t2 = (unsigned char) str[1] - 0x80u) <= 0x3Fu && - (t3 = (unsigned char) str[2] - 0x80u) <= 0x3Fu) - { - c = ((c & 0x07u) << 18) | (t1 << 12) | (t2 << 6) | t3; - if (c < 0x10000u || c > 0x10FFFFu) - c = 0xFFFDu; - else - str += 3; - } - else - c = 0xFFFDu; - } - else - c = 0xFFFDu; - } - - *out_codepoint = c; - return str; -} - -static size_t -_raqm_u8_to_u32 (const char *text, size_t len, uint32_t *unicode) -{ - size_t in_len = 0; - uint32_t *out_utf32 = unicode; - const char *in_utf8 = text; - const char *end = text + len; - - while ((in_len < len) && (*in_utf8 != '\0')) - { - const char *out_utf8 = _raqm_get_utf8_codepoint (in_utf8, end, out_utf32); - in_len += out_utf8 - in_utf8; - in_utf8 = out_utf8; - ++out_utf32; - } - - return (out_utf32 - unicode); -} - -/* Ported from HarfBuzz’s hb_utf16_xe_t::next(). */ -static const uint16_t * -_raqm_get_utf16_codepoint (const uint16_t *str, - const uint16_t *end, - uint32_t *out_codepoint) -{ - uint32_t c = *str++; - - if (c < 0xD800u || c > 0xDFFFu) - { - *out_codepoint = c; - return str; - } - - if (c <= 0xDBFFu && str < end) - { - /* High-surrogate in c */ - uint32_t l = *str; - if (l >= 0xDC00u && l <= 0xDFFFu) - { - /* Low-surrogate in l */ - *out_codepoint = (c << 10) + l - ((0xD800u << 10) - 0x10000u + 0xDC00u); - str += 1; - return str; - } - } - - /* Lonely / out-of-order surrogate. */ - *out_codepoint = 0xFFFDu; - return str; -} - -static size_t -_raqm_u16_to_u32 (const uint16_t *text, size_t len, uint32_t *unicode) -{ - size_t in_len = 0; - uint32_t *out_utf32 = unicode; - const uint16_t *in_utf16 = text; - const uint16_t *end = text + len; - - while ((in_len < len) && (*in_utf16 != '\0')) - { - const uint16_t *out_utf16 = _raqm_get_utf16_codepoint (in_utf16, end, out_utf32); - in_len += (out_utf16 - in_utf16); - in_utf16 = out_utf16; - ++out_utf32; - } - - return (out_utf32 - unicode); -} - -/** - * raqm_set_text_utf8: - * @rq: a #raqm_t. - * @text: a UTF-8 encoded text string. - * @len: the length of @text in UTF-8 bytes. - * - * Same as raqm_set_text(), but for text encoded in UTF-8 encoding. - * - * Return value: - * `true` if no errors happened, `false` otherwise. - * - * Since: 0.1 - */ -bool -raqm_set_text_utf8 (raqm_t *rq, - const char *text, - size_t len) -{ - if (!rq || !text) - return false; - - /* Call raqm_clear_contents to reuse this raqm_t */ - if (rq->text_len) - return false; - - /* Empty string, don’t fail but do nothing */ - if (!len) - return true; - - if (!_raqm_alloc_text(rq, len, true, false)) - return false; - - rq->text_len = _raqm_u8_to_u32 (text, len, rq->text); - memcpy (rq->text_utf8, text, sizeof (char) * len); - _raqm_init_text_info (rq); - - return true; -} - -/** - * raqm_set_text_utf16: - * @rq: a #raqm_t. - * @text: a UTF-16 encoded text string. - * @len: the length of @text in UTF-16 shorts. - * - * Same as raqm_set_text(), but for text encoded in UTF-16 encoding. - * - * Return value: - * `true` if no errors happened, `false` otherwise. - * - * Since: 0.10 - */ -bool -raqm_set_text_utf16 (raqm_t *rq, - const uint16_t *text, - size_t len) -{ - if (!rq || !text) - return false; - - /* Call raqm_clear_contents to reuse this raqm_t */ - if (rq->text_len) - return false; - - /* Empty string, don’t fail but do nothing */ - if (!len) - return true; - - if (!_raqm_alloc_text(rq, len, false, true)) - return false; - - rq->text_len = _raqm_u16_to_u32 (text, len, rq->text); - memcpy (rq->text_utf16, text, sizeof (uint16_t) * len); - _raqm_init_text_info (rq); - - return true; -} -/** - * raqm_set_par_direction: - * @rq: a #raqm_t. - * @dir: the direction of the paragraph. - * - * Sets the paragraph direction, also known as block direction in CSS. For - * horizontal text, this controls the overall direction in the Unicode - * Bidirectional Algorithm, so when the text is mainly right-to-left (with or - * without some left-to-right) text, then the base direction should be set to - * #RAQM_DIRECTION_RTL and vice versa. - * - * The default is #RAQM_DIRECTION_DEFAULT, which determines the paragraph - * direction based on the first character with strong bidi type (see [rule - * P2](https://unicode.org/reports/tr9/#P2) in Unicode Bidirectional Algorithm), - * which can be good enough for many cases but has problems when a mainly - * right-to-left paragraph starts with a left-to-right character and vice versa - * as the detected paragraph direction will be the wrong one, or when text does - * not contain any characters with string bidi types (e.g. only punctuation or - * numbers) as this will default to left-to-right paragraph direction. - * - * For vertical, top-to-bottom text, #RAQM_DIRECTION_TTB should be used. Raqm, - * however, provides limited vertical text support and does not handle rotated - * horizontal text in vertical text, instead everything is treated as vertical - * text. - * - * Return value: - * `true` if no errors happened, `false` otherwise. - * - * Since: 0.1 - */ -bool -raqm_set_par_direction (raqm_t *rq, - raqm_direction_t dir) -{ - if (!rq) - return false; - - rq->base_dir = dir; - - return true; -} - -/** - * raqm_set_language: - * @rq: a #raqm_t. - * @lang: a BCP47 language code. - * @start: index of the first character that should use @face. - * @len: number of characters using @face. - * - * Sets a [BCP47 language - * code](https://www.w3.org/International/articles/language-tags/) to be used - * for @len-number of characters starting at @start. The @start and @len are - * input string array indices (i.e. counting bytes in UTF-8 and scalar values - * in UTF-32). - * - * This method can be used repeatedly to set different languages for different - * parts of the text. - * - * Return value: - * `true` if no errors happened, `false` otherwise. - * - * Stability: - * Unstable - * - * Since: 0.2 - */ -bool -raqm_set_language (raqm_t *rq, - const char *lang, - size_t start, - size_t len) -{ - hb_language_t language; - size_t end; - - if (!rq) - return false; - - if (!rq->text_len) - return true; - - end = _raqm_encoding_to_u32_index (rq, start + len); - start = _raqm_encoding_to_u32_index (rq, start); - - if (start >= rq->text_len || end > rq->text_len) - return false; - - if (!rq->text_info) - return false; - - language = hb_language_from_string (lang, -1); - for (size_t i = start; i < end; i++) - { - rq->text_info[i].lang = language; - } - - return true; -} - -static bool -_raqm_add_font_feature (raqm_t *rq, - hb_feature_t fea) -{ - void* new_features; - - if (!rq) - return false; - - new_features = realloc (rq->features, - sizeof (hb_feature_t) * (rq->features_len + 1)); - if (!new_features) - return false; - - if (fea.start != HB_FEATURE_GLOBAL_START) - fea.start = _raqm_encoding_to_u32_index (rq, fea.start); - if (fea.end != HB_FEATURE_GLOBAL_END) - fea.end = _raqm_encoding_to_u32_index (rq, fea.end); - - rq->features = new_features; - rq->features[rq->features_len] = fea; - rq->features_len++; - - return true; -} - -/** - * raqm_add_font_feature: - * @rq: a #raqm_t. - * @feature: (transfer none): a font feature string. - * @len: length of @feature, -1 for `NULL`-terminated. - * - * Adds a font feature to be used by the #raqm_t during text layout. This is - * usually used to turn on optional font features that are not enabled by - * default, for example `dlig` or `ss01`, but can be also used to turn off - * default font features. - * - * @feature is string representing a single font feature, in the syntax - * understood by hb_feature_from_string(). - * - * This function can be called repeatedly, new features will be appended to the - * end of the features list and can potentially override previous features. - * - * Return value: - * `true` if parsing @feature succeeded, `false` otherwise. - * - * Since: 0.1 - */ -bool -raqm_add_font_feature (raqm_t *rq, - const char *feature, - int len) -{ - hb_bool_t ok; - hb_feature_t fea; - - if (!rq) - return false; - - ok = hb_feature_from_string (feature, len, &fea); - if (ok) - _raqm_add_font_feature (rq, fea); - - return ok; -} - -static hb_font_t * -_raqm_create_hb_font (raqm_t *rq, - FT_Face face, - int loadflags) -{ - hb_font_t *font = hb_ft_font_create_referenced (face); - - if (loadflags >= 0) - hb_ft_font_set_load_flags (font, loadflags); - - return font; -} - -static bool -_raqm_set_freetype_face (raqm_t *rq, - FT_Face face, - size_t start, - size_t end) -{ - if (!rq) - return false; - - if (!rq->text_len) - return true; - - if (start >= rq->text_len || end > rq->text_len) - return false; - - if (!rq->text_info) - return false; - - for (size_t i = start; i < end; i++) - { - if (rq->text_info[i].ftface) - FT_Done_Face (rq->text_info[i].ftface); - rq->text_info[i].ftface = face; - FT_Reference_Face (face); - } - - return true; -} - -/** - * raqm_set_freetype_face: - * @rq: a #raqm_t. - * @face: an #FT_Face. - * - * Sets an #FT_Face to be used for all characters in @rq. - * - * The text must be set with raqm_set_text() or one of its variants before - * calling this function. - * - * See also raqm_set_freetype_face_range(). - * - * Return value: - * `true` if no errors happened, `false` otherwise. - * - * Since: 0.1 - */ -bool -raqm_set_freetype_face (raqm_t *rq, - FT_Face face) -{ - return _raqm_set_freetype_face (rq, face, 0, rq->text_len); -} - -/** - * raqm_set_freetype_face_range: - * @rq: a #raqm_t. - * @face: an #FT_Face. - * @start: index of the first character that should use @face from the input string. - * @len: number of elements using @face. - * - * Sets an #FT_Face to be used for @len-number of characters staring at @start. - * The @start and @len are input string array indices, counting elements - * according to the underlying encoding. @start must always be aligned to the - * start of an encoded codepoint, and @len must always end at a codepoint's - * final element. - * - * This method can be used repeatedly to set different faces for different - * parts of the text. It is the responsibility of the client to make sure that - * face ranges cover the whole text, and are properly aligned. - * - * See also raqm_set_freetype_face(). - * - * Return value: - * `true` if no errors happened, `false` otherwise. - * - * Since: 0.1 - */ -bool -raqm_set_freetype_face_range (raqm_t *rq, - FT_Face face, - size_t start, - size_t len) -{ - size_t end; - - if (!rq) - return false; - - if (!rq->text_len) - return true; - - end = _raqm_encoding_to_u32_index (rq, start + len); - start = _raqm_encoding_to_u32_index (rq, start); - - return _raqm_set_freetype_face (rq, face, start, end); -} - -static bool -_raqm_set_freetype_load_flags (raqm_t *rq, - int flags, - size_t start, - size_t end) -{ - if (!rq) - return false; - - if (!rq->text_len) - return true; - - if (start >= rq->text_len || end > rq->text_len) - return false; - - if (!rq->text_info) - return false; - - for (size_t i = start; i < end; i++) - rq->text_info[i].ftloadflags = flags; - - return true; -} - -/** - * raqm_set_freetype_load_flags: - * @rq: a #raqm_t. - * @flags: FreeType load flags. - * - * Sets the load flags passed to FreeType when loading glyphs, should be the - * same flags used by the client when rendering FreeType glyphs. - * - * Return value: - * `true` if no errors happened, `false` otherwise. - * - * Since: 0.3 - */ -bool -raqm_set_freetype_load_flags (raqm_t *rq, - int flags) -{ - return _raqm_set_freetype_load_flags(rq, flags, 0, rq->text_len); -} - -/** - * raqm_set_freetype_load_flags_range: - * @rq: a #raqm_t. - * @flags: FreeType load flags. - * @start: index of the first character that should use @flags. - * @len: number of characters using @flags. - * - * Sets the load flags passed to FreeType when loading glyphs for @len-number - * of characters staring at @start. Flags should be the same as used by the - * client when rendering corresponding FreeType glyphs. The @start and @len - * are input string array indices (i.e. counting bytes in UTF-8 and scalar - * values in UTF-32). - * - * This method can be used repeatedly to set different flags for different - * parts of the text. It is the responsibility of the client to make sure that - * flag ranges cover the whole text. - * - * See also raqm_set_freetype_load_flags(). - * - * Return value: - * `true` if no errors happened, `false` otherwise. - * - * Since: 0.9 - */ -bool -raqm_set_freetype_load_flags_range (raqm_t *rq, - int flags, - size_t start, - size_t len) -{ - size_t end; - - if (!rq) - return false; - - if (!rq->text_len) - return true; - - end = _raqm_encoding_to_u32_index (rq, start + len); - start = _raqm_encoding_to_u32_index (rq, start); - - return _raqm_set_freetype_load_flags (rq, flags, start, end); -} - -static bool -_raqm_set_spacing (raqm_t *rq, - int spacing, - bool word_spacing, - size_t start, - size_t end) -{ - if (!rq) - return false; - - if (!rq->text_len) - return true; - - if (start >= rq->text_len || end > rq->text_len) - return false; - - if (!rq->text_info) - return false; - - for (size_t i = start; i < end; i++) - { - bool set_spacing = i == 0; - if (!set_spacing) - set_spacing = raqm_allowed_grapheme_boundary (rq, i - 1); - - if (set_spacing) - { - if (word_spacing) - { - if (raqm_allowed_grapheme_boundary (rq, i)) - { - /* CSS word separators, word spacing is only applied on these.*/ - if (rq->text[i] == 0x0020 || /* Space */ - rq->text[i] == 0x00A0 || /* No Break Space */ - rq->text[i] == 0x1361 || /* Ethiopic Word Space */ - rq->text[i] == 0x10100 || /* Aegean Word Separator Line */ - rq->text[i] == 0x10101 || /* Aegean Word Separator Dot */ - rq->text[i] == 0x1039F || /* Ugaric Word Divider */ - rq->text[i] == 0x1091F) /* Phoenician Word Separator */ - { - rq->text_info[i].spacing_after = spacing; - } - } - } - else - { - rq->text_info[i].spacing_after = spacing; - } - } - } - - return true; -} - -/** - * raqm_set_letter_spacing_range: - * @rq: a #raqm_t. - * @spacing: amount of spacing in Freetype Font Units (26.6 format). - * @start: index of the first character that should use @spacing. - * @len: number of characters using @spacing. - * - * Set the letter spacing or tracking for a given range, the value - * will be added onto the advance and offset for RTL, and the advance for - * other directions. Letter spacing will be applied between characters, so - * the last character will not have spacing applied after it. - * Note that not all scripts have a letter-spacing tradition, - * for example, Arabic does not, while Devanagari does. - * - * This will also add “disable `liga`, `clig`, `hlig`, `dlig`, and `calt`” font - * features to the internal features list, so call this function after setting - * the font features for best spacing results. - * - * Return value: - * `true` if no errors happened, `false` otherwise. - * - * Since: 0.10 - */ -bool -raqm_set_letter_spacing_range(raqm_t *rq, - int spacing, - size_t start, - size_t len) -{ - size_t end; - - if (!rq) - return false; - - if (!rq->text_len) - return true; - - end = start + len - 1; - - if (spacing != 0) - { -#define NUM_TAGS 5 - static char *tags[NUM_TAGS] = { "clig", "liga", "hlig", "dlig", "calt" }; - for (size_t i = 0; i < NUM_TAGS; i++) - { - hb_feature_t fea = { hb_tag_from_string(tags[i], 5), 0, start, end }; - _raqm_add_font_feature (rq, fea); - } -#undef NUM_TAGS - } - - start = _raqm_encoding_to_u32_index (rq, start); - end = _raqm_encoding_to_u32_index (rq, end); - - return _raqm_set_spacing (rq, spacing, false, start, end); -} - -/** - * raqm_set_word_spacing_range: - * @rq: a #raqm_t. - * @spacing: amount of spacing in Freetype Font Units (26.6 format). - * @start: index of the first character that should use @spacing. - * @len: number of characters using @spacing. - * - * Set the word spacing for a given range. Word spacing will only be applied to - * 'word separator' characters, such as 'space', 'no break space' and - * 'Ethiopic word separator'. - * The value will be added onto the advance and offset for RTL, and the advance - * for other directions. - * - * Return value: - * `true` if no errors happened, `false` otherwise. - * - * Since: 0.10 - */ -bool -raqm_set_word_spacing_range(raqm_t *rq, - int spacing, - size_t start, - size_t len) -{ - size_t end; - - if (!rq) - return false; - - if (!rq->text_len) - return true; - - end = _raqm_encoding_to_u32_index (rq, start + len); - start = _raqm_encoding_to_u32_index (rq, start); - - return _raqm_set_spacing (rq, spacing, true, start, end); -} - -/** - * raqm_set_invisible_glyph: - * @rq: a #raqm_t. - * @gid: glyph id to use for invisible glyphs. - * - * Sets the glyph id to be used for invisible glyphs. - * - * If @gid is negative, invisible glyphs will be suppressed from the output. - * - * If @gid is zero, invisible glyphs will be rendered as space. - * - * If @gid is a positive number, it will be used for invisible glyphs. - * - * Return value: - * `true` if no errors happened, `false` otherwise. - * - * Since: 0.6 - */ -bool -raqm_set_invisible_glyph (raqm_t *rq, - int gid) -{ - if (!rq) - return false; - - rq->invisible_glyph = gid; - return true; -} - -static bool -_raqm_itemize (raqm_t *rq); - -static bool -_raqm_shape (raqm_t *rq); - -static bool -_raqm_is_paragraph_separator (uint32_t ch) -{ - /* Unicode characters with Bidi class B (Paragraph_Separator). */ - return ch == 0x000A || ch == 0x000D || - ch == 0x001C || ch == 0x001D || ch == 0x001E || - ch == 0x0085 || ch == 0x2029; -} - -/** - * raqm_layout: - * @rq: a #raqm_t. - * - * Run the text layout process on @rq. This is the main Raqm function where the - * Unicode Bidirectional Text algorithm will be applied to the text in @rq, - * text shaping, and any other part of the layout process. - * - * Return value: - * `true` if the layout process was successful, `false` otherwise. It also fails - * if the text is not a single paragraph (see raqm_set_text()). - * - * Since: 0.1 - */ -bool -raqm_layout (raqm_t *rq) -{ - if (!rq) - return false; - - if (!rq->text_len) - return true; - - if (!rq->text_info) - return false; - - for (size_t i = 0; i < rq->text_len; i++) - { - if (!rq->text_info[i].ftface) - return false; - - /* The text must be a single paragraph. */ - if (_raqm_is_paragraph_separator (rq->text[i])) - return false; - } - - if (!_raqm_itemize (rq)) - return false; - - if (!_raqm_shape (rq)) - return false; - - return true; -} - -static uint32_t -_raqm_u32_to_u8_index (raqm_t *rq, - uint32_t index); -static uint32_t -_raqm_u32_to_u16_index (raqm_t *rq, - uint32_t index); -/** - * raqm_get_glyphs: - * @rq: a #raqm_t. - * @length: (out): output array length. - * - * Gets the final result of Raqm layout process, an array of #raqm_glyph_t - * containing the glyph indices in the font, their positions and other possible - * information. - * - * Return value: (transfer none): - * An array of #raqm_glyph_t, or `NULL` in case of error. This is owned by @rq - * and must not be freed. - * - * Since: 0.1 - */ -raqm_glyph_t * -raqm_get_glyphs (raqm_t *rq, - size_t *length) -{ - size_t count = 0; - - if (!rq || !rq->runs || !length) - { - if (length) - *length = 0; - return NULL; - } - - for (raqm_run_t *run = rq->runs; run != NULL; run = run->next) - count += hb_buffer_get_length (run->buffer); - - if (count > rq->glyphs_capacity) - { - void* new_mem = realloc (rq->glyphs, sizeof (raqm_glyph_t) * count); - if (!new_mem) - { - *length = 0; - return NULL; - } - - rq->glyphs = new_mem; - rq->glyphs_capacity = count; - } - - *length = count; - - RAQM_TEST ("Glyph information:\n"); - - count = 0; - for (raqm_run_t *run = rq->runs; run != NULL; run = run->next) - { - size_t len; - hb_glyph_info_t *info; - hb_glyph_position_t *position; - - len = hb_buffer_get_length (run->buffer); - info = hb_buffer_get_glyph_infos (run->buffer, NULL); - position = hb_buffer_get_glyph_positions (run->buffer, NULL); - - for (size_t i = 0; i < len; i++) - { - rq->glyphs[count + i].index = info[i].codepoint; - rq->glyphs[count + i].cluster = info[i].cluster; - rq->glyphs[count + i].x_advance = position[i].x_advance; - rq->glyphs[count + i].y_advance = position[i].y_advance; - rq->glyphs[count + i].x_offset = position[i].x_offset; - rq->glyphs[count + i].y_offset = position[i].y_offset; - rq->glyphs[count + i].ftface = rq->text_info[info[i].cluster].ftface; - - RAQM_TEST ("glyph [%d]\tx_offset: %d\ty_offset: %d\tx_advance: %d\tfont: %s\n", - rq->glyphs[count + i].index, rq->glyphs[count + i].x_offset, - rq->glyphs[count + i].y_offset, rq->glyphs[count + i].x_advance, - rq->glyphs[count + i].ftface->family_name); - } - - count += len; - } - - if (rq->text_utf8) - { -#ifdef RAQM_TESTING - RAQM_TEST ("\nUTF-32 clusters:"); - for (size_t i = 0; i < count; i++) - RAQM_TEST (" %02d", rq->glyphs[i].cluster); - RAQM_TEST ("\n"); -#endif - - for (size_t i = 0; i < count; i++) - rq->glyphs[i].cluster = _raqm_u32_to_u8_index (rq, - rq->glyphs[i].cluster); - -#ifdef RAQM_TESTING - RAQM_TEST ("UTF-8 clusters: "); - for (size_t i = 0; i < count; i++) - RAQM_TEST (" %02d", rq->glyphs[i].cluster); - RAQM_TEST ("\n"); -#endif - } - else if (rq->text_utf16) - { - for (size_t i = 0; i < count; i++) - rq->glyphs[i].cluster = _raqm_u32_to_u16_index (rq, - rq->glyphs[i].cluster); - } - return rq->glyphs; -} - -/** - * raqm_get_par_resolved_direction: - * @rq: a #raqm_t. - * - * Gets the resolved direction of the paragraph; - * - * Return value: - * The #raqm_direction_t specifying the resolved direction of text, - * or #RAQM_DIRECTION_DEFAULT if raqm_layout() has not been called on @rq. - * - * Since: 0.8 - */ -raqm_direction_t -raqm_get_par_resolved_direction (raqm_t *rq) -{ - if (!rq) - return RAQM_DIRECTION_DEFAULT; - - return rq->resolved_dir; -} - -static raqm_direction_t -_raqm_detect_direction (raqm_t *rq); - -/** - * raqm_get_par_detected_direction: - * @rq: a #raqm_t. - * - * Gets the natural direction of the text as detected from its content - * following the Unicode Bidirectional Algorithm, regardless of the base - * direction set with raqm_set_par_direction(). Unlike - * raqm_get_par_resolved_direction(), this does not require raqm_layout() to - * have been called, and can report that the text is direction neutral. - * - * Return value: - * #RAQM_DIRECTION_LTR or #RAQM_DIRECTION_RTL if the text has a strong - * direction, or #RAQM_DIRECTION_DEFAULT if the text is direction neutral (has - * no strong characters) or no text has been set. #RAQM_DIRECTION_TTB is never - * returned, as vertical direction is a layout property, not a property of the - * text content. - * - * Since: 0.11 - */ -raqm_direction_t -raqm_get_par_detected_direction (raqm_t *rq) -{ - if (!rq || !rq->text || rq->text_len == 0) - return RAQM_DIRECTION_DEFAULT; - - return _raqm_detect_direction (rq); -} - -/** - * raqm_get_direction_at_index: - * @rq: a #raqm_t. - * @index: (in): character index. - * - * Gets the resolved direction of the character at specified index; - * - * Return value: - * The #raqm_direction_t specifying the resolved direction of text at the - * specified index, or #RAQM_DIRECTION_DEFAULT if raqm_layout() has not been - * called on @rq. - * - * Since: 0.8 - */ -raqm_direction_t -raqm_get_direction_at_index (raqm_t *rq, - size_t index) -{ - if (!rq) - return RAQM_DIRECTION_DEFAULT; - - for (raqm_run_t *run = rq->runs; run != NULL; run = run->next) - { - if (run->pos <= index && index < run->pos + run->len) - { - switch (run->direction) - { - case HB_DIRECTION_LTR: - return RAQM_DIRECTION_LTR; - case HB_DIRECTION_RTL: - return RAQM_DIRECTION_RTL; - case HB_DIRECTION_TTB: - return RAQM_DIRECTION_TTB; - default: - return RAQM_DIRECTION_DEFAULT; - } - } - } - - return RAQM_DIRECTION_DEFAULT; -} - -static bool -_raqm_resolve_scripts (raqm_t *rq); - -static hb_direction_t -_raqm_hb_dir (raqm_t *rq, _raqm_bidi_level_t level) -{ - hb_direction_t dir = HB_DIRECTION_LTR; - - if (rq->base_dir == RAQM_DIRECTION_TTB) - dir = HB_DIRECTION_TTB; - else if (RAQM_BIDI_LEVEL_IS_RTL(level)) - dir = HB_DIRECTION_RTL; - - return dir; -} - -typedef struct -{ - size_t pos; - size_t len; - _raqm_bidi_level_t level; -} _raqm_bidi_run; - -#ifdef RAQM_SHEENBIDI -static _raqm_bidi_run * -_raqm_bidi_itemize (raqm_t *rq, size_t *run_count) -{ - _raqm_bidi_run *runs; - SBAlgorithmRef bidi; - SBParagraphRef par; - SBUInteger par_len; - SBLineRef line; - - SBLevel base_level = SBLevelDefaultLTR; - SBCodepointSequence input = { - SBStringEncodingUTF32, - (void *) rq->text, - rq->text_len - }; - - if (rq->base_dir == RAQM_DIRECTION_RTL) - base_level = 1; - else if (rq->base_dir == RAQM_DIRECTION_LTR) - base_level = 0; - - /* paragraph */ - bidi = SBAlgorithmCreate (&input); - par = SBAlgorithmCreateParagraph (bidi, 0, INT32_MAX, base_level); - par_len = SBParagraphGetLength (par); - - /* lines */ - line = SBParagraphCreateLine (par, 0, par_len); - *run_count = SBLineGetRunCount (line); - - if (SBParagraphGetBaseLevel (par) == 1) - rq->resolved_dir = RAQM_DIRECTION_RTL; - else - rq->resolved_dir = RAQM_DIRECTION_LTR; - - runs = malloc (sizeof (_raqm_bidi_run) * (*run_count)); - if (runs) - { - const SBRun *sheenbidi_runs = SBLineGetRunsPtr(line); - - for (size_t i = 0; i < (*run_count); ++i) - { - runs[i].pos = sheenbidi_runs[i].offset; - runs[i].len = sheenbidi_runs[i].length; - runs[i].level = sheenbidi_runs[i].level; - } - } - - SBLineRelease (line); - SBParagraphRelease (par); - SBAlgorithmRelease (bidi); - - return runs; -} - -static raqm_direction_t -_raqm_detect_direction (raqm_t *rq) -{ - raqm_direction_t dir = RAQM_DIRECTION_DEFAULT; - size_t isolate = 0; - - /* UBA: P2 and P3. */ - for (size_t i = 0; i < rq->text_len; i++) - { - SBBidiType type = SBCodepointGetBidiType (rq->text[i]); - - if (type == SBBidiTypeLRI || type == SBBidiTypeRLI || type == SBBidiTypeFSI) - isolate++; - else if (type == SBBidiTypePDI && isolate > 0) - isolate--; - else if (isolate == 0 && SBBidiTypeIsStrong (type)) - { - dir = (type == SBBidiTypeL) ? RAQM_DIRECTION_LTR : RAQM_DIRECTION_RTL; - break; - } - } - - return dir; -} -#else -static void -_raqm_reverse_run (_raqm_bidi_run *run, const size_t len) -{ - assert (run); - - for (size_t i = 0; i < len / 2; i++) - { - _raqm_bidi_run temp = run[i]; - run[i] = run[len - 1 - i]; - run[len - 1 - i] = temp; - } -} - -static _raqm_bidi_run * -_raqm_reorder_runs (const FriBidiCharType *types, - const size_t len, - const FriBidiParType base_dir, - /* input and output */ - FriBidiLevel *levels, - /* output */ - size_t *run_count) -{ - FriBidiLevel level; - FriBidiLevel last_level = -1; - FriBidiLevel max_level = 0; - size_t run_start = 0; - size_t run_index = 0; - _raqm_bidi_run *runs = NULL; - size_t count = 0; - - if (len == 0) - { - *run_count = 0; - return NULL; - } - - assert (types); - assert (levels); - - /* L1. Reset the embedding levels of some chars: - 4. any sequence of white space characters at the end of the line. */ - for (int i = len - 1; - i >= 0 && FRIBIDI_IS_EXPLICIT_OR_BN_OR_WS (types[i]); i--) - { - levels[i] = FRIBIDI_DIR_TO_LEVEL (base_dir); - } - - /* Find max_level of the line. We don't reuse the paragraph - * max_level, both for a cleaner API, and that the line max_level - * may be far less than paragraph max_level. */ - for (int i = len - 1; i >= 0; i--) - { - if (levels[i] > max_level) - max_level = levels[i]; - } - - for (size_t i = 0; i < len; i++) - { - if (levels[i] != last_level) - count++; - - last_level = levels[i]; - } - - runs = malloc (sizeof (_raqm_bidi_run) * count); - if (!runs) - { - *run_count = 0; - return NULL; - } - - while (run_start < len) - { - size_t run_end = run_start; - while (run_end < len && levels[run_start] == levels[run_end]) - { - run_end++; - } - - runs[run_index].pos = run_start; - runs[run_index].level = levels[run_start]; - runs[run_index].len = run_end - run_start; - run_start = run_end; - run_index++; - } - - /* L2. Reorder. */ - for (level = max_level; level > 0; level--) - { - for (int i = count - 1; i >= 0; i--) - { - if (runs[i].level >= level) - { - int end = i; - for (i--; (i >= 0 && runs[i].level >= level); i--) - ; - _raqm_reverse_run (runs + i + 1, end - i); - } - } - } - - *run_count = count; - return runs; -} - -static _raqm_bidi_run * -_raqm_bidi_itemize (raqm_t *rq, size_t *run_count) -{ - FriBidiParType par_type = FRIBIDI_PAR_ON; - _raqm_bidi_run *runs = NULL; - - FriBidiCharType *types; - _raqm_bidi_level_t *levels; - int max_level = 0; - FriBidiBracketType *btypes; - - types = calloc (rq->text_len, sizeof (FriBidiCharType)); - btypes = calloc (rq->text_len, sizeof (FriBidiBracketType)); - levels = calloc (rq->text_len, sizeof (_raqm_bidi_level_t)); - - if (!types || !levels || !btypes) - goto done; - - if (rq->base_dir == RAQM_DIRECTION_RTL) - par_type = FRIBIDI_PAR_RTL; - else if (rq->base_dir == RAQM_DIRECTION_LTR) - par_type = FRIBIDI_PAR_LTR; - - fribidi_get_bidi_types (rq->text, rq->text_len, types); - fribidi_get_bracket_types (rq->text, rq->text_len, types, btypes); - max_level = fribidi_get_par_embedding_levels_ex (types, btypes, - rq->text_len, &par_type, - levels); - - if (par_type == FRIBIDI_PAR_RTL) - rq->resolved_dir = RAQM_DIRECTION_RTL; - else - rq->resolved_dir = RAQM_DIRECTION_LTR; - - if (max_level == 0) - goto done; - - /* Get the number of bidi runs */ - runs = _raqm_reorder_runs (types, rq->text_len, par_type, levels, run_count); - -done: - free (types); - free (levels); - free (btypes); - - return runs; -} - -static raqm_direction_t -_raqm_detect_direction (raqm_t *rq) -{ - raqm_direction_t dir = RAQM_DIRECTION_DEFAULT; - FriBidiParType par_type; - FriBidiCharType *types; - - types = calloc (rq->text_len, sizeof (FriBidiCharType)); - if (types) - { - fribidi_get_bidi_types (rq->text, rq->text_len, types); - par_type = fribidi_get_par_direction (types, rq->text_len); - - if (par_type == FRIBIDI_PAR_LTR) - dir = RAQM_DIRECTION_LTR; - else if (par_type == FRIBIDI_PAR_RTL) - dir = RAQM_DIRECTION_RTL; - - free (types); - } - - return dir; -} -#endif - -static bool -_raqm_itemize (raqm_t *rq) -{ - _raqm_bidi_run *runs = NULL; - raqm_run_t *last; - size_t run_count = 0; - bool ok = true; - -#ifdef RAQM_TESTING - static char *dir_names[] = { - "DEFAULT", - "RTL", - "LTR", - "TTB" - }; - - assert (rq->base_dir < sizeof (dir_names)); - RAQM_TEST ("Direction is: %s\n\n", dir_names[rq->base_dir]); -#endif - - if (!_raqm_resolve_scripts (rq)) - { - ok = false; - goto done; - } - - if (rq->base_dir == RAQM_DIRECTION_TTB) - { - /* Treat every thing as LTR in vertical text */ - run_count = 1; - rq->resolved_dir = RAQM_DIRECTION_TTB; - runs = malloc (sizeof (_raqm_bidi_run)); - if (runs) - { - runs->pos = 0; - runs->len = rq->text_len; - runs->level = 0; - } - } - else - runs = _raqm_bidi_itemize (rq, &run_count); - - if (!runs) - { - ok = false; - goto done; - } - -#ifdef RAQM_TESTING - assert (rq->resolved_dir < sizeof (dir_names)); - if (rq->base_dir == RAQM_DIRECTION_DEFAULT) - RAQM_TEST ("Resolved direction is: %s\n\n", dir_names[rq->resolved_dir]); - RAQM_TEST ("Number of runs before script itemization: %zu\n\n", run_count); - - RAQM_TEST ("BiDi Runs:\n"); - for (size_t i = 0; i < run_count; i++) - { - RAQM_TEST ("run[%zu]:\t start: %zu\tlength: %zu\tlevel: %d\n", - i, runs[i].pos, runs[i].len, runs[i].level); - } - RAQM_TEST ("\n"); -#endif - - last = NULL; - for (size_t i = 0; i < run_count; i++) - { - raqm_run_t *run = _raqm_alloc_run (rq); - if (!run) - { - ok = false; - goto done; - } - - if (!rq->runs) - rq->runs = run; - - if (last) - last->next = run; - - run->direction = _raqm_hb_dir (rq, runs[i].level); - - if (HB_DIRECTION_IS_BACKWARD (run->direction)) - { - run->pos = runs[i].pos + runs[i].len - 1; - run->script = rq->text_info[run->pos].script; - run->font = _raqm_create_hb_font (rq, rq->text_info[run->pos].ftface, - rq->text_info[run->pos].ftloadflags); - for (int j = runs[i].len - 1; j >= 0; j--) - { - _raqm_text_info info = rq->text_info[runs[i].pos + j]; - if (!_raqm_compare_text_info (rq->text_info[run->pos], info)) - { - raqm_run_t *newrun = _raqm_alloc_run (rq); - if (!newrun) - { - ok = false; - goto done; - } - newrun->pos = runs[i].pos + j; - newrun->len = 1; - newrun->direction = _raqm_hb_dir (rq, runs[i].level); - newrun->script = info.script; - newrun->font = _raqm_create_hb_font (rq, info.ftface, - info.ftloadflags); - run->next = newrun; - run = newrun; - } - else - { - run->len++; - run->pos = runs[i].pos + j; - } - } - } - else - { - run->pos = runs[i].pos; - run->script = rq->text_info[run->pos].script; - run->font = _raqm_create_hb_font (rq, rq->text_info[run->pos].ftface, - rq->text_info[run->pos].ftloadflags); - for (size_t j = 0; j < runs[i].len; j++) - { - _raqm_text_info info = rq->text_info[runs[i].pos + j]; - if (!_raqm_compare_text_info (rq->text_info[run->pos], info)) - { - raqm_run_t *newrun = _raqm_alloc_run (rq); - if (!newrun) - { - ok = false; - goto done; - } - newrun->pos = runs[i].pos + j; - newrun->len = 1; - newrun->direction = _raqm_hb_dir (rq, runs[i].level); - newrun->script = info.script; - newrun->font = _raqm_create_hb_font (rq, info.ftface, - info.ftloadflags); - run->next = newrun; - run = newrun; - } - else - run->len++; - } - } - - last = run; - last->next = NULL; - } - -#ifdef RAQM_TESTING - run_count = 0; - for (raqm_run_t *run = rq->runs; run != NULL; run = run->next) - run_count++; - RAQM_TEST ("Number of runs after script itemization: %zu\n\n", run_count); - - run_count = 0; - RAQM_TEST ("Final Runs:\n"); - for (raqm_run_t *run = rq->runs; run != NULL; run = run->next) - { - SCRIPT_TO_STRING (run->script); - RAQM_TEST ("run[%zu]:\t start: %d\tlength: %d\tdirection: %s\tscript: %s\tfont: %s\n", - run_count++, run->pos, run->len, - hb_direction_to_string (run->direction), buff, - rq->text_info[run->pos].ftface->family_name); - } - RAQM_TEST ("\n"); -#endif - -done: - free (runs); - - return ok; -} - -/* Stack to handle script detection */ -typedef struct -{ - size_t capacity; - size_t size; - int *pair_index; - hb_script_t *script; -} _raqm_stack_t; - -/* Special paired characters for script detection */ -static size_t paired_len = 34; -static const uint32_t paired_chars[] = -{ - 0x0028, 0x0029, /* ascii paired punctuation */ - 0x003c, 0x003e, - 0x005b, 0x005d, - 0x007b, 0x007d, - 0x00ab, 0x00bb, /* guillemets */ - 0x2018, 0x2019, /* general punctuation */ - 0x201c, 0x201d, - 0x2039, 0x203a, - 0x3008, 0x3009, /* chinese paired punctuation */ - 0x300a, 0x300b, - 0x300c, 0x300d, - 0x300e, 0x300f, - 0x3010, 0x3011, - 0x3014, 0x3015, - 0x3016, 0x3017, - 0x3018, 0x3019, - 0x301a, 0x301b -}; - -static void -_raqm_stack_free (_raqm_stack_t *stack) -{ - free (stack->script); - free (stack->pair_index); - free (stack); -} - -/* Stack handling functions */ -static _raqm_stack_t * -_raqm_stack_new (size_t max) -{ - _raqm_stack_t *stack; - stack = calloc (1, sizeof (_raqm_stack_t)); - if (!stack) - return NULL; - - stack->script = malloc (sizeof (hb_script_t) * max); - if (!stack->script) - { - _raqm_stack_free (stack); - return NULL; - } - - stack->pair_index = malloc (sizeof (int) * max); - if (!stack->pair_index) - { - _raqm_stack_free (stack); - return NULL; - } - - stack->size = 0; - stack->capacity = max; - - return stack; -} - -static bool -_raqm_stack_pop (_raqm_stack_t *stack) -{ - if (!stack->size) - { - RAQM_DBG ("Stack is Empty\n"); - return false; - } - - stack->size--; - - return true; -} - -static hb_script_t -_raqm_stack_top (_raqm_stack_t *stack) -{ - if (!stack->size) - { - RAQM_DBG ("Stack is Empty\n"); - return HB_SCRIPT_INVALID; /* XXX: check this */ - } - - return stack->script[stack->size]; -} - -static bool -_raqm_stack_push (_raqm_stack_t *stack, - hb_script_t script, - int pair_index) -{ - if (stack->size == stack->capacity) - { - RAQM_DBG ("Stack is Full\n"); - return false; - } - - stack->size++; - stack->script[stack->size] = script; - stack->pair_index[stack->size] = pair_index; - - return true; -} - -static int -_get_pair_index (const uint32_t ch) -{ - int lower = 0; - int upper = paired_len - 1; - - while (lower <= upper) - { - int mid = (lower + upper) / 2; - if (ch < paired_chars[mid]) - upper = mid - 1; - else if (ch > paired_chars[mid]) - lower = mid + 1; - else - return mid; - } - - return -1; -} - -#define STACK_IS_EMPTY(script) ((script)->size <= 0) -#define IS_OPEN(pair_index) (((pair_index) & 1) == 0) - -static hb_script_t -_raqm_unicode_script (hb_codepoint_t u) -{ - static hb_unicode_funcs_t* unicode_funcs; - - unicode_funcs = hb_unicode_funcs_get_default (); - - /* Make combining marks inherit the script of their bases, regardless of - * their own script. - */ - if (hb_unicode_general_category (unicode_funcs, u) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) - return HB_SCRIPT_INHERITED; - - return hb_unicode_script (unicode_funcs, u); -} - -/* Resolve the script for each character in the input string, if the character - * script is common or inherited it takes the script of the character before it - * except paired characters which we try to make them use the same script. We - * then split the BiDi runs, if necessary, on script boundaries. - */ -static bool -_raqm_resolve_scripts (raqm_t *rq) -{ - int last_script_index = -1; - int last_set_index = -1; - hb_script_t last_script = HB_SCRIPT_INVALID; - _raqm_stack_t *stack = NULL; - - for (size_t i = 0; i < rq->text_len; ++i) - rq->text_info[i].script = _raqm_unicode_script (rq->text[i]); - -#ifdef RAQM_TESTING - RAQM_TEST ("Before script detection:\n"); - for (size_t i = 0; i < rq->text_len; ++i) - { - SCRIPT_TO_STRING (rq->text_info[i].script); - RAQM_TEST ("script for ch[%zu]\t%s\n", i, buff); - } - RAQM_TEST ("\n"); -#endif - - stack = _raqm_stack_new (rq->text_len); - if (!stack) - return false; - - for (int i = 0; i < (int) rq->text_len; i++) - { - if (rq->text_info[i].script == HB_SCRIPT_COMMON && last_script_index != -1) - { - int pair_index = _get_pair_index (rq->text[i]); - if (pair_index >= 0) - { - if (IS_OPEN (pair_index)) - { - /* is a paired character */ - rq->text_info[i].script = last_script; - last_set_index = i; - _raqm_stack_push (stack, rq->text_info[i].script, pair_index); - } - else - { - /* is a close paired character */ - /* find matching opening (by getting the last even index for current - * odd index) */ - while (!STACK_IS_EMPTY (stack) && - stack->pair_index[stack->size] != (pair_index & ~1)) - { - _raqm_stack_pop (stack); - } - if (!STACK_IS_EMPTY (stack)) - { - rq->text_info[i].script = _raqm_stack_top (stack); - last_script = rq->text_info[i].script; - last_set_index = i; - } - else - { - rq->text_info[i].script = last_script; - last_set_index = i; - } - } - } - else - { - rq->text_info[i].script = last_script; - last_set_index = i; - } - } - else if (rq->text_info[i].script == HB_SCRIPT_INHERITED && - last_script_index != -1) - { - rq->text_info[i].script = last_script; - last_set_index = i; - } - else - { - for (int j = last_set_index + 1; j < i; ++j) - rq->text_info[j].script = rq->text_info[i].script; - last_script = rq->text_info[i].script; - last_script_index = i; - last_set_index = i; - } - } - - /* Loop backwards and change any remaining Common or Inherit characters to - * take the script if the next character. - * https://github.com/HOST-Oman/libraqm/issues/95 - */ - for (int i = rq->text_len - 2; i >= 0; --i) - { - if (rq->text_info[i].script == HB_SCRIPT_INHERITED || - rq->text_info[i].script == HB_SCRIPT_COMMON) - rq->text_info[i].script = rq->text_info[i + 1].script; - } - -#ifdef RAQM_TESTING - RAQM_TEST ("After script detection:\n"); - for (size_t i = 0; i < rq->text_len; ++i) - { - SCRIPT_TO_STRING (rq->text_info[i].script); - RAQM_TEST ("script for ch[%zu]\t%s\n", i, buff); - } - RAQM_TEST ("\n"); -#endif - - _raqm_stack_free (stack); - - return true; -} - -static void -_raqm_ft_transform (int *x, - int *y, - FT_Matrix matrix) -{ - FT_Vector vector; - vector.x = *x; - vector.y = *y; - - FT_Vector_Transform (&vector, &matrix); - - *x = vector.x; - *y = vector.y; -} - -#if !HB_VERSION_ATLEAST (10, 4, 0) -# define hb_ft_font_get_ft_face hb_ft_font_get_face -#endif - -static bool -_raqm_shape (raqm_t *rq) -{ - hb_buffer_flags_t hb_buffer_flags = HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT; - - if (rq->invisible_glyph < 0) - hb_buffer_flags |= HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES; - - for (raqm_run_t *run = rq->runs; run != NULL; run = run->next) - { - if (!run->buffer) - run->buffer = hb_buffer_create (); - - hb_buffer_add_utf32 (run->buffer, rq->text, rq->text_len, - run->pos, run->len); - hb_buffer_set_script (run->buffer, run->script); - hb_buffer_set_language (run->buffer, rq->text_info[run->pos].lang); - hb_buffer_set_direction (run->buffer, run->direction); - hb_buffer_set_flags (run->buffer, hb_buffer_flags); - - if (rq->invisible_glyph > 0) - hb_buffer_set_invisible_glyph (run->buffer, rq->invisible_glyph); - - hb_shape_full (run->font, run->buffer, rq->features, rq->features_len, - NULL); - - { - FT_Matrix matrix; - hb_glyph_info_t *info; - hb_glyph_position_t *pos; - unsigned int len; - - FT_Get_Transform (hb_ft_font_get_ft_face (run->font), &matrix, NULL); - pos = hb_buffer_get_glyph_positions (run->buffer, &len); - info = hb_buffer_get_glyph_infos (run->buffer, &len); - - for (unsigned int i = 0; i < len; i++) - { - _raqm_ft_transform (&pos[i].x_advance, &pos[i].y_advance, matrix); - _raqm_ft_transform (&pos[i].x_offset, &pos[i].y_offset, matrix); - - bool set_spacing = false; - if (run->direction == HB_DIRECTION_RTL) - { - set_spacing = i == 0; - if (!set_spacing) - set_spacing = info[i].cluster != info[i-1].cluster; - } - else - { - set_spacing = i == len - 1; - if (!set_spacing) - set_spacing = info[i].cluster != info[i+1].cluster; - } - - _raqm_text_info rq_info = rq->text_info[info[i].cluster]; - - if (rq_info.spacing_after != 0 && set_spacing) - { - if (run->direction == HB_DIRECTION_TTB) - pos[i].y_advance -= rq_info.spacing_after; - else if (run->direction == HB_DIRECTION_RTL) - { - pos[i].x_advance += rq_info.spacing_after; - pos[i].x_offset += rq_info.spacing_after; - } - else - pos[i].x_advance += rq_info.spacing_after; - } - } - } - } - - return true; -} - -/* Count equivalent UTF-8 bytes in codepoint */ -static size_t -_raqm_count_codepoint_utf8_bytes (uint32_t chr) -{ - if (0 == ((uint32_t) 0xffffff80 & chr)) - return 1; - else if (0 == ((uint32_t) 0xfffff800 & chr)) - return 2; - else if (0 == ((uint32_t) 0xffff0000 & chr)) - return 3; - else - return 4; -} - -/* Convert index from UTF-32 to UTF-8 */ -static uint32_t -_raqm_u32_to_u8_index (raqm_t *rq, - uint32_t index) -{ - size_t length = 0; - - for (uint32_t i = 0; i < index; ++i) - length += _raqm_count_codepoint_utf8_bytes (rq->text[i]); - - return length; -} - -/* Convert index from UTF-8 to UTF-32 */ -static size_t -_raqm_u8_to_u32_index (raqm_t *rq, - size_t index) -{ - const unsigned char *s = (const unsigned char *) rq->text_utf8; - const unsigned char *t = s; - size_t length = 0; - - while (((size_t) (s - t) < index) && ('\0' != *s)) - { - if (0xf0 == (0xf8 & *s)) - s += 4; - else if (0xe0 == (0xf0 & *s)) - s += 3; - else if (0xc0 == (0xe0 & *s)) - s += 2; - else - s += 1; - - length++; - } - - if ((size_t) (s-t) > index) - length--; - - return length; -} - -/* Count equivalent UTF-16 short in codepoint */ -static size_t -_raqm_count_codepoint_utf16_short (uint32_t chr) -{ - if (chr > 0x010000) - return 2; - else - return 1; -} - -/* Convert index from UTF-32 to UTF-16 */ -static uint32_t -_raqm_u32_to_u16_index (raqm_t *rq, - uint32_t index) -{ - size_t length = 0; - - for (uint32_t i = 0; i < index; ++i) - length += _raqm_count_codepoint_utf16_short (rq->text[i]); - - return length; -} - -/* Convert index from UTF-16 to UTF-32 */ -static size_t -_raqm_u16_to_u32_index (raqm_t *rq, - size_t index) -{ - const uint16_t *s = (const uint16_t *) rq->text_utf16; - const uint16_t *t = s; - size_t length = 0; - - while (((size_t) (s - t) < index) && ('\0' != *s)) - { - if (*s < 0xD800 || *s > 0xDBFF) - s += 1; - else - s += 2; - - length++; - } - - if ((size_t) (s-t) > index) - length--; - - return length; -} - -static inline size_t -_raqm_encoding_to_u32_index (raqm_t *rq, - size_t index) -{ - if (rq->text_utf8) - return _raqm_u8_to_u32_index (rq, index); - else if (rq->text_utf16) - return _raqm_u16_to_u32_index (rq, index); - return index; -} - -/** - * raqm_index_to_position: - * @rq: a #raqm_t. - * @index: (inout): character index. - * @x: (out): output x position. - * @y: (out): output y position. - * - * Calculates the cursor position after the character at @index. If the character - * is right-to-left, then the cursor will be at the left of it, whereas if the - * character is left-to-right, then the cursor will be at the right of it. - * - * Return value: - * `true` if the process was successful, `false` otherwise. - * - * Since: 0.2 - */ -bool -raqm_index_to_position (raqm_t *rq, - size_t *index, - int *x, - int *y) -{ - /* We don't currently support multiline, so y is always 0 */ - *y = 0; - *x = 0; - - if (rq == NULL) - return false; - - *index = _raqm_encoding_to_u32_index (rq, *index); - - if (*index >= rq->text_len) - return false; - - RAQM_TEST ("\n"); - - while (*index < rq->text_len) - { - if (raqm_allowed_grapheme_boundary (rq, *index)) - break; - - ++*index; - } - - for (raqm_run_t *run = rq->runs; run != NULL; run = run->next) - { - size_t len; - hb_glyph_info_t *info; - hb_glyph_position_t *position; - len = hb_buffer_get_length (run->buffer); - info = hb_buffer_get_glyph_infos (run->buffer, NULL); - position = hb_buffer_get_glyph_positions (run->buffer, NULL); - - for (size_t i = 0; i < len; i++) - { - uint32_t curr_cluster = info[i].cluster; - uint32_t next_cluster = curr_cluster; - *x += position[i].x_advance; - - if (run->direction == HB_DIRECTION_LTR) - { - for (size_t j = i + 1; j < len && next_cluster == curr_cluster; j++) - next_cluster = info[j].cluster; - } - else - { - for (int j = i - 1; i != 0 && j >= 0 && next_cluster == curr_cluster; - j--) - next_cluster = info[j].cluster; - } - - if (next_cluster == curr_cluster) - next_cluster = run->pos + run->len; - - if (*index < next_cluster && *index >= curr_cluster) - { - if (run->direction == HB_DIRECTION_RTL) - *x -= position[i].x_advance; - *index = curr_cluster; - goto found; - } - } - } - -found: - if (rq->text_utf8) - *index = _raqm_u32_to_u8_index (rq, *index); - else if (rq->text_utf16) - *index = _raqm_u32_to_u16_index (rq, *index); - RAQM_TEST ("The position is %d at index %zu\n",*x ,*index); - return true; -} - -/** - * raqm_position_to_index: - * @rq: a #raqm_t. - * @x: x position. - * @y: y position. - * @index: (out): output character index. - * - * Returns the @index of the character at @x and @y position within text. - * If the position is outside the text, the last character is chosen as - * @index. - * - * Return value: - * `true` if the process was successful, `false` in case of error. - * - * Since: 0.2 - */ -bool -raqm_position_to_index (raqm_t *rq, - int x, - int y, - size_t *index) -{ - int delta_x = 0, current_x = 0; - (void)y; - - if (rq == NULL) - return false; - - if (x < 0) /* Get leftmost index */ - { - if (rq->resolved_dir == RAQM_DIRECTION_RTL) - *index = rq->text_len; - else - *index = 0; - return true; - } - - RAQM_TEST ("\n"); - - for (raqm_run_t *run = rq->runs; run != NULL; run = run->next) - { - size_t len; - hb_glyph_info_t *info; - hb_glyph_position_t *position; - len = hb_buffer_get_length (run->buffer); - info = hb_buffer_get_glyph_infos (run->buffer, NULL); - position = hb_buffer_get_glyph_positions (run->buffer, NULL); - - for (size_t i = 0; i < len; i++) - { - delta_x = position[i].x_advance; - if (x < (current_x + delta_x)) - { - bool before = false; - if (run->direction == HB_DIRECTION_LTR) - before = (x < current_x + (delta_x / 2)); - else - before = (x > current_x + (delta_x / 2)); - - if (before) - *index = info[i].cluster; - else - { - uint32_t curr_cluster = info[i].cluster; - uint32_t next_cluster = curr_cluster; - if (run->direction == HB_DIRECTION_LTR) - for (size_t j = i + 1; j < len && next_cluster == curr_cluster; j++) - next_cluster = info[j].cluster; - else - for (int j = i - 1; i != 0 && j >= 0 && next_cluster == curr_cluster; - j--) - next_cluster = info[j].cluster; - - if (next_cluster == curr_cluster) - next_cluster = run->pos + run->len; - - *index = next_cluster; - } - if (raqm_allowed_grapheme_boundary (rq, *index)) - { - RAQM_TEST ("The start-index is %zu at position %d \n", *index, x); - return true; - } - - while (*index < (unsigned)run->pos + run->len) - { - if (raqm_allowed_grapheme_boundary (rq, *index)) - { - *index += 1; - break; - } - *index += 1; - } - RAQM_TEST ("The start-index is %zu at position %d \n", *index, x); - return true; - } - else - current_x += delta_x; - } - } - - /* Get rightmost index*/ - if (rq->resolved_dir == RAQM_DIRECTION_RTL) - *index = 0; - else - *index = rq->text_len; - - RAQM_TEST ("The start-index is %zu at position %d \n", *index, x); - - return true; -} - -/** - * raqm_allowed_grapheme_boundary: - * @rq: a #raqm_t. - * @index: the index of the boundary to check. - * - * Checks whether a grapheme cluster boundary is allowed between the characters - * at @index and @index + 1, according to the Unicode Standard Annex #29 rules. - * - * The @rq must have had text set on it using raqm_set_text(). - * - * Returns: `true` if a boundary is allowed, `false` otherwise. - * - * Since: 0.11 - **/ -RAQM_API bool -raqm_allowed_grapheme_boundary (raqm_t *rq, - size_t index) -{ - if (!rq) - return true; - - /* GB1/GB2: break at start and end of text */ - if (index >= rq->text_len - 1) - return true; - - uint32_t l_char = rq->text[index]; - uint32_t r_char = rq->text[index + 1]; - _raqm_grapheme_t l = _raqm_get_grapheme_break (l_char); - _raqm_grapheme_t r = _raqm_get_grapheme_break (r_char); - - /* GB3: CR × LF */ - if (l == RAQM_GRAPHEME_CR && r == RAQM_GRAPHEME_LF) - return false; - - /* GB4: (Control|CR|LF) ÷ */ - if (l == RAQM_GRAPHEME_CONTROL || l == RAQM_GRAPHEME_CR || - l == RAQM_GRAPHEME_LF) - return true; - - /* GB5: ÷ (Control|CR|LF) */ - if (r == RAQM_GRAPHEME_CONTROL || r == RAQM_GRAPHEME_CR || - r == RAQM_GRAPHEME_LF) - return true; - - /* GB6: L × (L|V|LV|LVT) */ - if (l == RAQM_GRAPHEME_L && - (r == RAQM_GRAPHEME_L || r == RAQM_GRAPHEME_V || - r == RAQM_GRAPHEME_LV || r == RAQM_GRAPHEME_LVT)) - return false; - - /* GB7: (LV|V) × (V|T) */ - if ((l == RAQM_GRAPHEME_LV || l == RAQM_GRAPHEME_V) && - (r == RAQM_GRAPHEME_V || r == RAQM_GRAPHEME_T)) - return false; - - /* GB8: (LVT|T) × T */ - if ((l == RAQM_GRAPHEME_LVT || l == RAQM_GRAPHEME_T) && - r == RAQM_GRAPHEME_T) - return false; - - /* GB9: × (Extend|ZWJ) */ - if (r == RAQM_GRAPHEME_EXTEND || r == RAQM_GRAPHEME_ZWJ) - return false; - - /* GB9a: × SpacingMark */ - if (r == RAQM_GRAPHEME_SPACING_MARK) - return false; - - /* GB9b: Prepend × */ - if (l == RAQM_GRAPHEME_PREPEND) - return false; - - /* GB9c: \p{InCB=Consonant} [\p{InCB=Extend}\p{InCB=Linker}]* - * \p{InCB=Linker} [\p{InCB=Extend}\p{InCB=Linker}]* × \p{InCB=Consonant} - * - * If r_char is InCB=Consonant, look back for the required pattern. */ - if (_raqm_get_incb (r_char) == RAQM_INCB_CONSONANT) - { - bool found_linker = false; - size_t j = index + 1; - while (j > 0) - { - j--; - _raqm_incb_t incb = _raqm_get_incb (rq->text[j]); - if (incb == RAQM_INCB_LINKER) - found_linker = true; - else if (incb == RAQM_INCB_EXTEND) - ; /* Continue looking back through Extend characters */ - else if (incb == RAQM_INCB_CONSONANT) - { - if (found_linker) - return false; - break; - } - else - break; - } - } - - /* GB11: ExtPict Extend* ZWJ × ExtPict */ - if (l == RAQM_GRAPHEME_ZWJ && - r == RAQM_GRAPHEME_EXTENDED_PICTOGRAPHIC) - { - /* Look back past the ZWJ at index for Extend* ExtPict */ - for (size_t j = index; j > 0; j--) - { - _raqm_grapheme_t g = _raqm_get_grapheme_break (rq->text[j - 1]); - if (g == RAQM_GRAPHEME_EXTEND) - continue; - if (g == RAQM_GRAPHEME_EXTENDED_PICTOGRAPHIC) - return false; - break; - } - } - - /* GB12/GB13: Do not break between regional indicator symbols if there is - * an odd number of RI characters before the break point. */ - if (l == RAQM_GRAPHEME_REGIONAL_INDICATOR && - r == RAQM_GRAPHEME_REGIONAL_INDICATOR) - { - size_t ri_count = 0; - for (size_t j = index + 1; j > 0; j--) - { - if (_raqm_get_grapheme_break (rq->text[j - 1]) != RAQM_GRAPHEME_REGIONAL_INDICATOR) - break; - ri_count++; - } - /* If odd number of RI before the break, don't break (they pair up) */ - if (ri_count % 2 == 1) - return false; - } - - /* GB999: Otherwise, break everywhere. */ - return true; -} - -/** - * raqm_version: - * @major: (out): Library major version component. - * @minor: (out): Library minor version component. - * @micro: (out): Library micro version component. - * - * Returns library version as three integer components. - * - * Since: 0.7 - **/ -void -raqm_version (unsigned int *major, - unsigned int *minor, - unsigned int *micro) -{ - *major = RAQM_VERSION_MAJOR; - *minor = RAQM_VERSION_MINOR; - *micro = RAQM_VERSION_MICRO; -} - -/** - * raqm_version_string: - * - * Returns library version as a string with three components. - * - * Return value: library version string. - * - * Since: 0.7 - **/ -const char * -raqm_version_string (void) -{ - return RAQM_VERSION_STRING; -} - -/** - * raqm_version_atleast: - * @major: Library major version component. - * @minor: Library minor version component. - * @micro: Library micro version component. - * - * Checks if library version is less than or equal to the specified version. - * - * Return value: - * `true` if library version is less than or equal to the specified version, - * `false` otherwise. - * - * Since: 0.7 - **/ -bool -raqm_version_atleast (unsigned int major, - unsigned int minor, - unsigned int micro) -{ - return RAQM_VERSION_ATLEAST (major, minor, micro); -} - -/** - * RAQM_VERSION_ATLEAST: - * @major: Library major version component. - * @minor: Library minor version component. - * @micro: Library micro version component. - * - * Checks if library version is less than or equal to the specified version. - * - * Return value: - * `true` if library version is less than or equal to the specified version, - * `false` otherwise. - * - * Since: 0.7 - **/ - -/** - * RAQM_VERSION_STRING: - * - * Library version as a string with three components. - * - * Since: 0.7 - **/ - -/** - * RAQM_VERSION_MAJOR: - * - * Library major version component. - * - * Since: 0.7 - **/ - -/** - * RAQM_VERSION_MINOR: - * - * Library minor version component. - * - * Since: 0.7 - **/ - -/** - * RAQM_VERSION_MICRO: - * - * Library micro version component. - * - * Since: 0.7 - **/ diff --git a/src/thirdparty/raqm/raqm.h b/src/thirdparty/raqm/raqm.h deleted file mode 100644 index 4b60f148e01..00000000000 --- a/src/thirdparty/raqm/raqm.h +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright © 2015 Information Technology Authority (ITA) - * Copyright © 2016-2023 Khaled Hosny - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - * - */ - -#ifndef _RAQM_H_ -#define _RAQM_H_ -#define _RAQM_H_IN_ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifndef RAQM_API -#define RAQM_API -#endif - -#include -#include -#include -#include FT_FREETYPE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "raqm-version.h" - -/** - * raqm_t: - * - * This is the main object holding all the states of the currently processed text as - * well as its output. - * - * Since: 0.1 - */ -typedef struct _raqm raqm_t; - -/** - * raqm_direction_t: - * @RAQM_DIRECTION_DEFAULT: Detect paragraph direction automatically. - * @RAQM_DIRECTION_RTL: Paragraph is mainly right-to-left text. - * @RAQM_DIRECTION_LTR: Paragraph is mainly left-to-right text. - * @RAQM_DIRECTION_TTB: Paragraph is mainly vertical top-to-bottom text. - * - * Base paragraph direction, see raqm_set_par_direction(). - * - * Since: 0.1 - */ -typedef enum -{ - RAQM_DIRECTION_DEFAULT, - RAQM_DIRECTION_RTL, - RAQM_DIRECTION_LTR, - RAQM_DIRECTION_TTB -} raqm_direction_t; - -/** - * raqm_glyph_t: - * @index: the index of the glyph in the font file. - * @x_advance: the glyph advance width in horizontal text. - * @y_advance: the glyph advance width in vertical text. - * @x_offset: the horizontal movement of the glyph from the current point. - * @y_offset: the vertical movement of the glyph from the current point. - * @cluster: the index of the original character in the input text. - * @ftface: the @FT_Face of the glyph. - * - * The structure that holds information about output glyphs, returned from - * raqm_get_glyphs(). - */ -typedef struct raqm_glyph_t { - unsigned int index; - int x_advance; - int y_advance; - int x_offset; - int y_offset; - uint32_t cluster; - FT_Face ftface; -} raqm_glyph_t; - -RAQM_API raqm_t * -raqm_create (void); - -RAQM_API raqm_t * -raqm_reference (raqm_t *rq); - -RAQM_API void -raqm_destroy (raqm_t *rq); - -RAQM_API void -raqm_clear_contents (raqm_t *rq); - -RAQM_API bool -raqm_set_text (raqm_t *rq, - const uint32_t *text, - size_t len); - -RAQM_API bool -raqm_set_text_utf8 (raqm_t *rq, - const char *text, - size_t len); -RAQM_API bool -raqm_set_text_utf16 (raqm_t *rq, - const uint16_t *text, - size_t len); - -RAQM_API bool -raqm_set_par_direction (raqm_t *rq, - raqm_direction_t dir); - -RAQM_API bool -raqm_set_language (raqm_t *rq, - const char *lang, - size_t start, - size_t len); - -RAQM_API bool -raqm_add_font_feature (raqm_t *rq, - const char *feature, - int len); - -RAQM_API bool -raqm_set_freetype_face (raqm_t *rq, - FT_Face face); - -RAQM_API bool -raqm_set_freetype_face_range (raqm_t *rq, - FT_Face face, - size_t start, - size_t len); - -RAQM_API bool -raqm_set_freetype_load_flags (raqm_t *rq, - int flags); - -RAQM_API bool -raqm_set_freetype_load_flags_range (raqm_t *rq, - int flags, - size_t start, - size_t len); - -RAQM_API bool -raqm_set_letter_spacing_range(raqm_t *rq, - int spacing, - size_t start, - size_t len); -RAQM_API bool -raqm_set_word_spacing_range(raqm_t *rq, - int spacing, - size_t start, - size_t len); - -RAQM_API bool -raqm_set_invisible_glyph (raqm_t *rq, - int gid); - -RAQM_API bool -raqm_layout (raqm_t *rq); - -RAQM_API raqm_glyph_t * -raqm_get_glyphs (raqm_t *rq, - size_t *length); - -RAQM_API raqm_direction_t -raqm_get_par_resolved_direction (raqm_t *rq); - -RAQM_API raqm_direction_t -raqm_get_par_detected_direction (raqm_t *rq); - -RAQM_API raqm_direction_t -raqm_get_direction_at_index (raqm_t *rq, - size_t index); - -RAQM_API bool -raqm_index_to_position (raqm_t *rq, - size_t *index, - int *x, - int *y); - -RAQM_API bool -raqm_position_to_index (raqm_t *rq, - int x, - int y, - size_t *index); - -RAQM_API void -raqm_version (unsigned int *major, - unsigned int *minor, - unsigned int *micro); - -RAQM_API const char * -raqm_version_string (void); - -RAQM_API bool -raqm_allowed_grapheme_boundary (raqm_t *rq, - size_t index); - -RAQM_API bool -raqm_version_atleast (unsigned int major, - unsigned int minor, - unsigned int micro); - - -#ifdef __cplusplus -} -#endif -#undef _RAQM_H_IN_ -#endif /* _RAQM_H_ */ diff --git a/wheels/dependency_licenses/RAQM.txt b/wheels/dependency_licenses/RAQM.txt index 196511ef688..99468479637 100644 --- a/wheels/dependency_licenses/RAQM.txt +++ b/wheels/dependency_licenses/RAQM.txt @@ -1,7 +1,7 @@ The MIT License (MIT) Copyright © 2015 Information Technology Authority (ITA) -Copyright © 2016 Khaled Hosny +Copyright © 2016-2026 Khaled Hosny Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/wheels/dependency_licenses/SHEENBIDI.txt b/wheels/dependency_licenses/SHEENBIDI.txt new file mode 100644 index 00000000000..84d0dba9088 --- /dev/null +++ b/wheels/dependency_licenses/SHEENBIDI.txt @@ -0,0 +1,205 @@ +SheenBidi is licensed under the Apache License, Version 2.0. + +Copyright (C) 2014-2025 Muhammad Tayyab Akram + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/winbuild/README.md b/winbuild/README.md index 50ad0a895d2..8b7855b6dc9 100644 --- a/winbuild/README.md +++ b/winbuild/README.md @@ -22,9 +22,9 @@ cd /D C:\Pillow\winbuild %PYTHON%\python.exe build_prepare.py -v --depends=C:\pillow-depends build\build_dep_all.cmd cd .. -%PYTHON%\python.exe -m pip install -v -C raqm=vendor -C fribidi=vendor . +%PYTHON%\python.exe -m pip install -v -C raqm=enable . path C:\Pillow\winbuild\build\bin;%PATH% %PYTHON%\python.exe selftest.py %PYTHON%\python.exe -m pytest -vv -x --cov PIL --cov Tests --cov-report term --cov-report xml Tests -%PYTHON%\python.exe -m pip wheel -v -C raqm=vendor -C fribidi=vendor . +%PYTHON%\python.exe -m pip wheel -v -C raqm=enable . ``` diff --git a/winbuild/build.rst b/winbuild/build.rst index 9be9847e728..32b359a96e6 100644 --- a/winbuild/build.rst +++ b/winbuild/build.rst @@ -43,7 +43,7 @@ Run ``build_prepare.py`` to configure the build:: usage: winbuild\build_prepare.py [-h] [-v] [-d PILLOW_BUILD] [--depends PILLOW_DEPS] [--architecture {x86,AMD64,ARM64}] [--nmake] - [--no-imagequant] [--no-fribidi] + [--no-imagequant] [--no-raqm] Download and generate build scripts for Pillow dependencies. @@ -59,8 +59,8 @@ Run ``build_prepare.py`` to configure the build:: build architecture (default: same as host Python) --nmake build dependencies using NMake instead of Ninja --no-imagequant skip GPL-licensed optional dependency libimagequant - --no-fribidi, --no-raqm - skip LGPL-licensed optional dependency FriBiDi + --no-raqm skip optional dependency Raqm and its SheenBidi + dependency --no-avif skip optional dependency libavif Arguments can also be supplied using the environment variables PILLOW_BUILD, @@ -86,24 +86,24 @@ Once the dependencies are built, make sure the required environment variables are set by running ``winbuild\build\build_env.cmd`` and install Pillow with pip:: winbuild\build\build_env.cmd - python.exe -m pip install -v -C raqm=vendor -C fribidi=vendor . + python.exe -m pip install -v -C raqm=enable . You can also install Pillow in `editable mode`_:: winbuild\build\build_env.cmd - python.exe -m pip install -v -C raqm=vendor -C fribidi=vendor -e . + python.exe -m pip install -v -C raqm=enable -e . To build a binary wheel instead, run:: winbuild\build\build_env.cmd - python.exe -m pip wheel -v -C raqm=vendor -C fribidi=vendor . + python.exe -m pip wheel -v -C raqm=enable . .. _editable mode: https://setuptools.pypa.io/en/stable/userguide/development_mode.html Testing Pillow -------------- -Some binary dependencies (e.g. ``fribidi.dll``) will be stored in the +Some binary dependencies (e.g. ``djpeg.exe``) will be stored in the ``winbuild\build\bin`` directory; this directory should be added to ``PATH`` before running tests. @@ -121,8 +121,8 @@ Here's an example script to build on Windows:: build\build_dep_all.cmd build\build_env.cmd cd .. - %PYTHON%\python.exe -m pip install -v -C raqm=vendor -C fribidi=vendor . + %PYTHON%\python.exe -m pip install -v -C raqm=enable . path C:\Pillow\winbuild\build\bin;%PATH% %PYTHON%\python.exe selftest.py %PYTHON%\python.exe -m pytest -vv -x --cov PIL --cov Tests --cov-report term --cov-report xml Tests - %PYTHON%\python.exe -m pip wheel -v -C raqm=vendor -C fribidi=vendor . + %PYTHON%\python.exe -m pip wheel -v -C raqm=enable . diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 2e8b3b82643..8b005d9bd25 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -346,23 +346,27 @@ def cmd_msbuild( "headers": [r"src\*.h"], "libs": [r"*.lib"], }, - "fribidi": { - "url": f"https://github.com/fribidi/fribidi/archive/v{V['fribidi']}.zip", - "filename": f"fribidi-{V['fribidi']}.zip", + "sheenbidi": { + "url": f"https://github.com/Tehreer/SheenBidi/archive/v{V['sheenbidi']}.tar.gz", + "filename": f"SheenBidi-{V['sheenbidi']}.tar.gz", + "license": "LICENSE", + "build": [ + *cmds_cmake("SheenBidi"), + cmd_mkdir(r"{inc_dir}\SheenBidi"), + cmd_xcopy(r"Headers\SheenBidi", r"{inc_dir}\SheenBidi"), + ], + "libs": [r"*.lib"], + }, + "raqm": { + "url": f"https://github.com/HOST-Oman/libraqm/releases/download/v{V['raqm']}/FILENAME", + "filename": f"raqm-{V['raqm']}.tar.xz", "license": "COPYING", "build": [ - cmd_copy(r"COPYING", rf"{{bin_dir}}\fribidi-{V['fribidi']}-COPYING"), - cmd_copy(r"{winbuild_dir}\fribidi.cmake", r"CMakeLists.txt"), - # generated tab.i files cannot be cross-compiled - " ^&^& ".join( - [ - "if {architecture}==ARM64 cmd /c call {vcvarsall} x86", - *cmds_cmake("fribidi-gen", "-DARCH=x86", build_dir="build_x86"), - ] - ), - *cmds_cmake("fribidi", "-DARCH={architecture}"), + cmd_copy(r"{winbuild_dir}\raqm.cmake", r"CMakeLists.txt"), + *cmds_cmake("raqm", f"-DRAQM_VERSION={V['raqm']}"), ], - "bins": [r"*.dll"], + "headers": [r"src\raqm.h", r"src\raqm-version.h"], + "libs": [r"*.lib"], }, "libavif": { "url": f"https://github.com/AOMediaCodec/libavif/archive/v{V['libavif']}.tar.gz", @@ -684,10 +688,9 @@ def main() -> None: help="skip GPL-licensed optional dependency libimagequant", ) parser.add_argument( - "--no-fribidi", "--no-raqm", action="store_true", - help="skip LGPL-licensed optional dependency FriBiDi", + help="skip optional dependency Raqm and its SheenBidi dependency", ) parser.add_argument( "--no-avif", @@ -732,8 +735,8 @@ def main() -> None: disabled = [] if args.no_imagequant: disabled += ["libimagequant"] - if args.no_fribidi: - disabled += ["fribidi"] + if args.no_raqm: + disabled += ["sheenbidi", "raqm"] if args.no_avif or args.architecture == "ARM64": disabled += ["libavif"] diff --git a/winbuild/fribidi.cmake b/winbuild/fribidi.cmake deleted file mode 100644 index b16e0784c98..00000000000 --- a/winbuild/fribidi.cmake +++ /dev/null @@ -1,116 +0,0 @@ -cmake_minimum_required(VERSION 3.12) - -project(fribidi) - - -add_definitions(-D_CRT_SECURE_NO_WARNINGS) - -include_directories(lib) - -function(extract_regex_1 var text regex) - string(REGEX MATCH ${regex} _ ${text}) - set(${var} "${CMAKE_MATCH_1}" PARENT_SCOPE) -endfunction() - - -function(fribidi_conf) - file(READ configure.ac FRIBIDI_CONF) - extract_regex_1(FRIBIDI_MAJOR_VERSION "${FRIBIDI_CONF}" "\\(fribidi_major_version, ([0-9]+)\\)") - extract_regex_1(FRIBIDI_MINOR_VERSION "${FRIBIDI_CONF}" "\\(fribidi_minor_version, ([0-9]+)\\)") - extract_regex_1(FRIBIDI_MICRO_VERSION "${FRIBIDI_CONF}" "\\(fribidi_micro_version, ([0-9]+)\\)") - extract_regex_1(FRIBIDI_INTERFACE_VERSION "${FRIBIDI_CONF}" "\\(fribidi_interface_version, ([0-9]+)\\)") - extract_regex_1(FRIBIDI_INTERFACE_AGE "${FRIBIDI_CONF}" "\\(fribidi_interface_age, ([0-9]+)\\)") - extract_regex_1(FRIBIDI_BINARY_AGE "${FRIBIDI_CONF}" "\\(fribidi_binary_age, ([0-9]+)\\)") - set(FRIBIDI_VERSION "${FRIBIDI_MAJOR_VERSION}.${FRIBIDI_MINOR_VERSION}.${FRIBIDI_MICRO_VERSION}") - set(PACKAGE "fribidi") - set(PACKAGE_NAME "GNU FriBidi") - set(PACKAGE_BUGREPORT "https://github.com/fribidi/fribidi/issues/new") - set(SIZEOF_INT 4) - set(FRIBIDI_MSVC_BUILD_PLACEHOLDER "#define FRIBIDI_BUILT_WITH_MSVC") - message("Detected ${PACKAGE_NAME} version ${FRIBIDI_VERSION}") - configure_file(lib/fribidi-config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/lib/fribidi-config.h @ONLY) -endfunction() -fribidi_conf() - - -option(ARCH "Target architecture") -if(${ARCH} STREQUAL ARM64) - set(GEN FALSE) -else() - set(GEN TRUE) -endif() -message("Generate tab.i files: " ${GEN}) - -function(prepend var prefix) - set(out "") - foreach(f ${ARGN}) - list(APPEND out "${prefix}${f}") - endforeach() - set(${var} "${out}" PARENT_SCOPE) -endfunction() - -macro(fribidi_definitions _TGT) - target_compile_definitions(${_TGT} PUBLIC - HAVE_MEMSET - HAVE_MEMMOVE - HAVE_STRDUP - HAVE_STDLIB_H=1 - HAVE_STRING_H=1 - HAVE_MEMORY_H=1 - #HAVE_STRINGS_H - #HAVE_SYS_TIMES_H - STDC_HEADERS=1 - HAVE_STRINGIZE=1) -endmacro() - -function(fribidi_gen _NAME _OUTNAME _PARAM) - set(_OUT ${CMAKE_CURRENT_SOURCE_DIR}/lib/${_OUTNAME}) - if(GEN) - prepend(_DEP "${CMAKE_CURRENT_SOURCE_DIR}/gen.tab/" ${ARGN}) - add_executable(gen-${_NAME} - gen.tab/gen-${_NAME}.c - gen.tab/packtab.c) - fribidi_definitions(gen-${_NAME}) - target_compile_definitions(gen-${_NAME} - PUBLIC DONT_HAVE_FRIBIDI_CONFIG_H) - add_custom_command( - COMMAND gen-${_NAME} ${_PARAM} ${_DEP} > ${_OUT} - DEPENDS ${_DEP} - OUTPUT ${_OUT}) - endif(GEN) - list(APPEND FRIBIDI_SOURCES_GENERATED "${_OUT}") - set(FRIBIDI_SOURCES_GENERATED ${FRIBIDI_SOURCES_GENERATED} PARENT_SCOPE) -endfunction() - -fribidi_gen(unicode-version fribidi-unicode-version.h "" - unidata/ReadMe.txt unidata/BidiMirroring.txt) - - -macro(fribidi_tab _NAME) - fribidi_gen(${_NAME}-tab ${_NAME}.tab.i 2 ${ARGN}) - if(GEN) - target_sources(gen-${_NAME}-tab - PRIVATE lib/fribidi-unicode-version.h) - endif(GEN) -endmacro() - -fribidi_tab(bidi-type unidata/UnicodeData.txt) -fribidi_tab(joining-type unidata/UnicodeData.txt unidata/ArabicShaping.txt) -fribidi_tab(arabic-shaping unidata/UnicodeData.txt) -fribidi_tab(mirroring unidata/BidiMirroring.txt) -fribidi_tab(brackets unidata/BidiBrackets.txt unidata/UnicodeData.txt) -fribidi_tab(brackets-type unidata/BidiBrackets.txt) - -add_custom_target(fribidi-gen DEPENDS ${FRIBIDI_SOURCES_GENERATED}) - - -file(GLOB FRIBIDI_SOURCES lib/*.c) -file(GLOB FRIBIDI_HEADERS lib/*.h) - -add_library(fribidi SHARED - ${FRIBIDI_SOURCES} - ${FRIBIDI_HEADERS} - ${FRIBIDI_SOURCES_GENERATED}) -fribidi_definitions(fribidi) -target_compile_definitions(fribidi - PUBLIC "-DFRIBIDI_BUILD") diff --git a/winbuild/raqm.cmake b/winbuild/raqm.cmake new file mode 100644 index 00000000000..a4230668bde --- /dev/null +++ b/winbuild/raqm.cmake @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.12) + +project(raqm C) + +add_definitions(-D_CRT_SECURE_NO_WARNINGS) + +set(RAQM_VERSION "" CACHE STRING "Raqm version, as x.y.z") +string(REPLACE "." ";" RAQM_VERSION_PARTS ${RAQM_VERSION}) +list(GET RAQM_VERSION_PARTS 0 RAQM_VERSION_MAJOR) +list(GET RAQM_VERSION_PARTS 1 RAQM_VERSION_MINOR) +list(GET RAQM_VERSION_PARTS 2 RAQM_VERSION_MICRO) +message("Building Raqm version ${RAQM_VERSION}") +configure_file(src/raqm-version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/src/raqm-version.h @ONLY) + +# FreeType, HarfBuzz and SheenBidi headers come from INCLUDE, set by build_env.cmd. +add_library(raqm STATIC src/raqm.c) +target_compile_definitions(raqm PRIVATE RAQM_SHEENBIDI RAQM_SHEENBIDI_GT_2_9)